Coverage Report - org.paneris.bibliomania.logs.LogAnalysis
 
Classes in this File Line Coverage Branch Coverage Complexity
LogAnalysis
0%
0/61
0%
0/24
3.87
LogLine
0%
0/87
0%
0/35
3.87
NotTodayFilter
0%
0/8
0%
0/6
3.87
TranslatedFilter
0%
0/4
0%
0/4
3.87
Translator
0%
0/36
0%
0/14
3.87
Translator$1
0%
0/6
N/A
3.87
WebalizerFilter
0%
0/6
0%
0/8
3.87
 
 1  
 package org.paneris.bibliomania.logs;
 2  
 
 3  
 import java.io.BufferedReader;
 4  
 import java.io.File;
 5  
 import java.io.FileReader;
 6  
 import java.io.FileWriter;
 7  
 import java.io.FilenameFilter;
 8  
 import java.io.InputStreamReader;
 9  
 import java.io.PrintWriter;
 10  
 import java.util.Calendar;
 11  
 import java.util.Date;
 12  
 import java.util.GregorianCalendar;
 13  
 
 14  
 import org.melati.poem.AccessToken;
 15  
 import org.melati.poem.NoSuchRowPoemException;
 16  
 import org.melati.poem.PoemTask;
 17  
 import org.melati.poem.UnexpectedExceptionPoemException;
 18  
 import org.melati.poem.util.StringUtils;
 19  
 import org.paneris.bibliomania.BibliomaniaDatabase;
 20  
 
 21  
 /*
 22  
  * This program will analyse the stats generated by the bibliomania.com site.
 23  
  * 
 24  
  * It is assumed to be run from cron at 2am EDT ie a crontab entry of
 25  
  * 0   2 * * *  /usr/local/logs/stats.sh 
 26  
  * where stats.sh runs this and outputs error and reports files.
 27  
  * 
 28  
  * It uses 2 analysis programs - weblaizer and analog.
 29  
  * It expects log files in a directory tree:
 30  
  * 
 31  
  * year/month/
 32  
  *
 33  
  * with each directory containing 1 log file per day.  
 34  
  * It will not do anything with today's file (as it will probably not be complete)
 35  
  * FIXME this means that the last day of every month is missed.
 36  
  *
 37  
  * Output for webalizer is according to the config file bibliomania.conf
 38  
  *
 39  
  * Output for analog is in the file analog.html in the directory for each month,
 40  
  * and the directory for the year.  The annual stats are recomputed every day.
 41  
  *
 42  
  * The stats are translated before being analysed stats to be trnaslated need
 43  
  * to be unzipped first.
 44  
  *
 45  
  * You can optionally supply a single parameter of the form yyyy/mm in order to 
 46  
  * process the stats for the specific month.  
 47  
  * Annual stats are still calculated.
 48  
  */  
 49  
 
 50  0
 public class LogAnalysis {
 51  
   
 52  
   public static void main(final String[] args) throws Exception {
 53  
     
 54  
     // get today's date
 55  0
     Calendar today = new GregorianCalendar();
 56  0
     int year = today.get(Calendar.YEAR);
 57  0
     int month = today.get(Calendar.MONTH) + 1;
 58  0
     String monthString = month + "";
 59  0
     if (month < 10) monthString = "0" + month;
 60  0
     int day = today.get(Calendar.DAY_OF_MONTH);
 61  0
     String dayString = day + "";
 62  0
     if (day < 10) dayString = "0" + day;
 63  0
     String todayYearmonth = year + File.separator + monthString;
 64  
     
 65  
     // default 
 66  0
     String yearmonth = todayYearmonth;
 67  
     
 68  
     // allow us to run month by month
 69  0
     if (args.length > 0) {
 70  0
       yearmonth = args[0];
 71  
     }
 72  
     
 73  0
     System.out.println("Running Stats for " + yearmonth);
 74  0
     System.out.println("Started at: " + new Date());
 75  0
     File dir = new File(yearmonth);
 76  
 
 77  0
     Translator trans = new Translator();
 78  
 
 79  
     // find files that need translating, translate them,
 80  
     // analize them, and then zip up the output
 81  0
     FilenameFilter webalizerFilter = new WebalizerFilter(todayYearmonth, dayString);
 82  0
     File[] files = dir.listFiles(webalizerFilter);
 83  0
     java.util.Arrays.sort(files);
 84  0
     if (files != null) {
 85  0
       for (int i=0; i<files.length; i++) {    
 86  0
         System.out.println("Translating " + files[i]);
 87  0
         File out = trans.doIt(files[i]);
 88  0
         String outName = out.toString();
 89  0
         out = null;
 90  0
         System.out.println("Webalizering " + outName);
 91  0
         run("webalizer -c bibliomania.conf " + outName);
 92  0
         System.out.println("Gzipping " + files[i]);
 93  0
         run("gzip " + files[i]);
 94  0
         System.out.println("Gzipping " + outName);
 95  0
         run("gzip " + outName);
 96  
       }
 97  
     }    
 98  
     
 99  
     // run the files for this month through analog
 100  0
     File[] dirs = new File[1];
 101  0
     dirs[0] = dir;
 102  0
     analog(dirs, todayYearmonth, dayString, yearmonth);
 103  
     
 104  
     // do the annual stuff.
 105  0
     int i = yearmonth.indexOf("/");
 106  0
     if (i > 0) {
 107  0
       String yeardir = yearmonth.substring(0,i);
 108  0
       File updir =  new File(yeardir);
 109  0
       files = updir.listFiles();
 110  0
       analog(files,todayYearmonth,dayString,yeardir);
 111  
     }
 112  
     
 113  0
     System.out.println("Completed at: " + new Date());
 114  0
   }
 115  
   
 116  
   
 117  
   /**
 118  
    * Run analog on all translated files in the supplied list of directories.
 119  
    */
 120  
   static void analog(File[] dirs, String yearmonth, String dayString, String destination) throws Exception {
 121  0
     FilenameFilter translatedFilter = new TranslatedFilter(yearmonth, dayString);
 122  0
     String filenames = "";
 123  0
     for (int i=0; i<dirs.length; i++) {    
 124  0
       File[] files = dirs[i].listFiles(translatedFilter);
 125  0
       if (files != null) {
 126  0
         for (int j=0; j<files.length; j++) {    
 127  0
           filenames += " " + files[j];
 128  
         }
 129  
       }
 130  
     }
 131  0
     System.out.println("Analogging " + filenames);
 132  0
     run("analog -G +ganalog.cfg -O" + destination + File.separator + "index.html" + filenames);
 133  0
   }   
 134  
   
 135  
   static void run(String command) throws Exception {
 136  0
     Process p = Runtime.getRuntime().exec(command);
 137  0
     InputStreamReader error = new InputStreamReader(p.getErrorStream());
 138  0
     InputStreamReader input = new InputStreamReader(p.getInputStream());
 139  0
     boolean ok = true;
 140  0
     while (ok) {
 141  0
       ok = ((error.read() != -1) || (input.read() != -1));
 142  
     }
 143  0
     p.waitFor();
 144  0
   }
 145  
 
 146  
 }
 147  
 
 148  
 /**
 149  
  * Select all files that are not for today.
 150  
  */
 151  
 class NotTodayFilter implements FilenameFilter {
 152  
 
 153  
   String yearmonth, day;
 154  
   
 155  0
   public NotTodayFilter(String yearmonth, String day) {
 156  0
     this.yearmonth = yearmonth;
 157  0
     this.day = day;
 158  0
   }
 159  
   
 160  
   public boolean accept(File dir, String name) {
 161  0
     if (!dir.toString().equals(yearmonth)) return true;
 162  0
     if (name.length() > 1) {
 163  0
       if (day.equals(name.substring(0,2))) return false;
 164  
     }
 165  0
     return true;
 166  
   }
 167  
 }  
 168  
 
 169  
 
 170  
  
 171  
 /**
 172  
  * Select files that have been translated.
 173  
  */
 174  
 class TranslatedFilter extends NotTodayFilter {
 175  
   
 176  
   public TranslatedFilter(String yearmonth, String day) {
 177  0
     super(yearmonth, day);
 178  0
   }
 179  
 
 180  
   public boolean accept(File dir, String name) {
 181  0
     if (name.endsWith("access.log.translated.gz") && super.accept(dir, name)) return true;
 182  0
     return false;
 183  
   }
 184  
 }
 185  
 
 186  
 /**
 187  
  *  Select files that need translating and webalizing.
 188  
  */
 189  
 class WebalizerFilter extends NotTodayFilter {
 190  
   
 191  
   public WebalizerFilter(String yearmonth, String day) {
 192  0
     super(yearmonth, day);
 193  0
   }
 194  
 
 195  
   public boolean accept(File dir, String name) {
 196  0
     File translated1 = new File(dir, name + ".translated.gz");
 197  0
     File translated2 = new File(dir, name + ".translated");
 198  0
     if (!translated1.exists() && !translated2.exists() && name.endsWith("access.log") && super.accept(dir, name)) return true;
 199  0
     return false;
 200  
   }
 201  
 }  
 202  
 
 203  
 /**
 204  
  * Translate a log file.
 205  
  */
 206  0
 class Translator {
 207  
 
 208  
   File fileoutput;
 209  
   final BibliomaniaDatabase db;
 210  
     
 211  0
   public Translator() {
 212  0
     db = new BibliomaniaDatabase(false);
 213  0
     db.connect("bibliomania", "org.melati.poem.dbms.Postgresql",
 214  
     "jdbc:postgresql:bibliomania", "postgres", "*",4);
 215  0
   }
 216  
   
 217  
   public File doIt(final File file) throws Exception {
 218  
     
 219  0
     db.inSession(
 220  
     AccessToken.root,       // FIXME
 221  0
     new PoemTask() {
 222  
       public void run() {
 223  
         try {
 224  0
           doTranslate(db, file);
 225  
         }
 226  0
         catch (Exception e) {
 227  0
           throw new UnexpectedExceptionPoemException(e);
 228  0
         }
 229  0
       }
 230  
     }
 231  
     );
 232  0
     return fileoutput;
 233  
   }
 234  
 
 235  
   private void doTranslate(BibliomaniaDatabase dbP, File fileinput) throws Exception {
 236  0
     System.out.println("Started translation at: " + new Date());
 237  0
     fileoutput = new File(fileinput.toString() + ".translated");
 238  0
     FileReader filein = new FileReader(fileinput);
 239  0
     PrintWriter fileout = new PrintWriter(new FileWriter(fileoutput));
 240  0
     BufferedReader in = new BufferedReader(filein);
 241  0
     int totalLines = 0;
 242  0
     int translatedLines = 0;
 243  0
     int failedLines = 0;
 244  0
     int failedSectionGroup = 0;
 245  0
     int failedSection = 0;
 246  0
     int failedAuthor = 0;
 247  0
     int failedBook = 0;
 248  0
     String line = "";
 249  0
     while ((line = in.readLine()) != null) {
 250  0
       totalLines++;
 251  0
       LogLine ln = new LogLine(dbP,line, totalLines);
 252  0
       fileout.println(ln.translate());
 253  0
       if (ln.translatedAll == LogLine.TRANSLATED) translatedLines++;
 254  0
       if (ln.translatedAll == LogLine.FAILED_TRANSLATED) failedLines++;
 255  0
       if (ln.translatedSectionGroup == LogLine.FAILED_TRANSLATED) failedSectionGroup++;
 256  0
       if (ln.translatedSection == LogLine.FAILED_TRANSLATED) failedSection++;
 257  0
       if (ln.translatedAuthor == LogLine.FAILED_TRANSLATED) failedAuthor++;
 258  0
       if (ln.translatedBook == LogLine.FAILED_TRANSLATED) failedBook++;
 259  0
     }
 260  0
     in.close();
 261  0
     fileout.close();
 262  0
     System.out.println("Found " + totalLines + " lines, translated " + translatedLines + " lines, failed on "+ failedLines + " lines.");
 263  0
     System.out.println("Failed:  SectionGroup " + failedSectionGroup + ", Section " + failedSection + ", Author " + failedAuthor + ", Book " + failedBook);
 264  0
   }
 265  
 
 266  
 }
 267  
 
 268  
   class LogLine {
 269  
     
 270  0
     static String lookForStart = "GET ";
 271  0
     static String lookForEnd = " HTTP"; 
 272  0
     static int lookForStartLength = lookForStart.length();
 273  
     
 274  0
     static int NOT_TRANSLATED = 0;
 275  0
     static int TRANSLATED = 1;
 276  0
     static int FAILED_TRANSLATED = 2;
 277  
     
 278  0
     int translatedAll = NOT_TRANSLATED;
 279  0
     int translatedSectionGroup = NOT_TRANSLATED;
 280  0
     int translatedSection = NOT_TRANSLATED;
 281  0
     int translatedAuthor = NOT_TRANSLATED;
 282  0
     int translatedBook = NOT_TRANSLATED;
 283  
     
 284  
     int lineno;
 285  
     
 286  
     String in;
 287  0
     String middle = "";
 288  0
     String end = "";
 289  
     BibliomaniaDatabase db;
 290  
     
 291  0
     public LogLine(BibliomaniaDatabase db, String in, int lineno) {
 292  0
       this.in = in;
 293  0
       this.db = db;
 294  0
       this.lineno = lineno;
 295  0
     }
 296  
     
 297  
     public String translate() {
 298  0
       String start = in;
 299  0
       String endBit = "";
 300  0
       String middleBit = "";
 301  0
       int length = in.length();
 302  0
       int startPoint = in.indexOf(lookForStart);
 303  0
       if (startPoint > -1) {
 304  0
         startPoint += lookForStartLength;
 305  0
         start = in.substring(0,startPoint);
 306  0
         endBit = in.substring(startPoint, length);
 307  0
         int middlePoint = in.indexOf(lookForEnd, startPoint);
 308  0
         if (middlePoint > -1) {
 309  0
           middleBit = in.substring(startPoint,middlePoint);
 310  0
           middleBit = lookup(middleBit);
 311  0
           endBit = in.substring(middlePoint, length);
 312  
         }
 313  
       }
 314  0
       if (translatedSectionGroup == TRANSLATED || 
 315  
           translatedSection == TRANSLATED ||
 316  
           translatedAuthor == TRANSLATED ||
 317  
           translatedBook == TRANSLATED) {
 318  0
         translatedAll = TRANSLATED;
 319  
       }
 320  0
       if (translatedSectionGroup == FAILED_TRANSLATED || 
 321  
           translatedSection == FAILED_TRANSLATED ||
 322  
           translatedAuthor == FAILED_TRANSLATED ||
 323  
           translatedBook == FAILED_TRANSLATED) {
 324  0
         translatedAll = FAILED_TRANSLATED;
 325  
       }
 326  0
       return start + middleBit + endBit;
 327  
     }
 328  
     
 329  
     public String lookup(String inP) {
 330  0
       if (inP.startsWith("/")) inP = inP.substring(1,inP.length());
 331  0
       String[] split = StringUtils.split(inP, '/');
 332  0
       int len = split.length;
 333  0
       if (len > 4) len = 4;
 334  
       try {
 335  0
         for (int i=0; i<len; i++) {
 336  0
           split[i] = look(i,split[i]);
 337  
         }
 338  0
       } catch (NumberFormatException e) {}
 339  0
       return join(split);
 340  
     }
 341  
     
 342  
     public String look(int place,String inP) throws NumberFormatException {
 343  0
       Integer i = new Integer(inP);
 344  0
       switch (place) {
 345  
         case 0: 
 346  0
           return lookupSectionGroup(i);
 347  
         case 1: 
 348  0
           return lookupSection(i);
 349  
         case 2: 
 350  0
           return lookupAuthor(i);
 351  
         case 3: 
 352  0
           return lookupBook(i);
 353  
         default:
 354  0
           return inP;
 355  
       }
 356  
     }
 357  
 
 358  
     public String lookupSectionGroup(Integer i) {
 359  
       try {
 360  0
         translatedSectionGroup = TRANSLATED;
 361  0
         return nospaces(db.getSectionGroupTable().getSectionGroupObject(i).getDisplayname());
 362  0
       } catch (NoSuchRowPoemException e) {
 363  0
         translatedSectionGroup = FAILED_TRANSLATED;
 364  0
         System.err.println("Line no " + lineno + "  Not Found: Section Group " + i);
 365  0
         System.err.println("        " + in);
 366  0
         return i + "";
 367  
       }
 368  
     }
 369  
 
 370  
     public String lookupSection(Integer i) throws NoSuchRowPoemException {
 371  
       try {
 372  0
         translatedSection = TRANSLATED;
 373  0
         return nospaces(db.getSectionTable().getSectionObject(i).getDisplayname());
 374  0
       } catch (NoSuchRowPoemException e) {
 375  0
         translatedSection = FAILED_TRANSLATED;
 376  0
         System.err.println("Line no " + lineno + "  Not Found:       Section " + i);
 377  0
         System.err.println("        " + in);
 378  0
         return i + "";
 379  
       }
 380  
     }
 381  
 
 382  
     public String lookupAuthor(Integer i) throws NoSuchRowPoemException {
 383  
       try {
 384  0
         translatedAuthor = TRANSLATED;
 385  0
         return nospaces(db.getAuthorTable().getAuthorObject(i).getSortname());
 386  0
       } catch (NoSuchRowPoemException e) {
 387  0
         translatedAuthor = FAILED_TRANSLATED;
 388  0
         System.err.println("Line no " + lineno + "  Not Found:        Author " + i);
 389  0
         System.err.println("        " + in);
 390  0
         return i + "";
 391  
       }
 392  
     }
 393  
 
 394  
     public String lookupBook(Integer i) throws NoSuchRowPoemException {
 395  
       try {
 396  0
         translatedBook = TRANSLATED;
 397  0
         return nospaces(db.getBookTable().getBookObject(i).getTitle());
 398  0
       } catch (NoSuchRowPoemException e) {
 399  0
         translatedBook = FAILED_TRANSLATED;
 400  0
         System.err.println("Line no " + lineno + "  Not Found:          Book " + i);
 401  0
         System.err.println("        " + in);
 402  0
         return i + "";
 403  
       }
 404  
     }
 405  
 
 406  
     public String join(String[] split) {
 407  0
       String out = "";
 408  0
       for (int i=0; i<split.length; i++) {
 409  0
         out += "/" + split[i];
 410  
       }
 411  0
       return out;
 412  
     }
 413  
     
 414  
     public static String nospaces(String s) {
 415  0
       if (s == null) return null;
 416  0
       String out = s.replace(' ','_');
 417  0
       return out.replace('&','n');
 418  
     }
 419  
   }