Patch to add good logging support to PMS

Discuss issues related to PS3 Media Server development (only for programmers)

Patch to add good logging support to PMS

Postby innot » Thu Aug 19, 2010 5:29 pm

Hi,

the following patch has been also published on the issue tracker under Issue 829.
But I would like to present the patch here in more detail for your feedback.

There are already some issues with similar patches (561, 625 and 724), why another one?

Well, first of all none of these patches have been accepted or even commented on. But as PMS becomes more popular on embedded systems, which basically run forever causing debug.log to also grow infinitively, there is a definite need for some more advanced logging.

Some highlights of my patch:
  • It is based on the SLF4J API and the Logback implementation.
    SLF4J/Logback is the (unofficial) successor to Log4j, which is currently not maintained anymore. Logback is actively developed (by the original Log4j developer) and has some much better 'Appenders' for rolling log files.
    I have chosen this Framework because it could do daily logfile rollovers without writing any additional code.

  • The patch does allow for a user modifiable configuration file. If the file 'logback.xml' exists in the PMS application directory, then it will be used instead of the build-in default config file.
    Users running headless can optionally use a file called 'logback.headless.xml'

  • With the default settings the logging output is exactly the same as before, including the endless growing debug.log in the same format. I gather that this is enough for most casual users who run and stop PMS with a GUI on Windows. But changing just two lines in logback.xml will change this to max 5x10MB.

  • Like Issue 625, the impact on PMS has been kept to a minimum. Only PMS.java and the build scripts are changed. All the advanced stuff is done in a few new classes under net.pms.logging and in the logback.xml file.

The patch has been tested with Windows and Linux (it is currently running on my NAS)

The patch requires the files
  • slf4j-api-1.6.x.jar
  • logback-core-0.9.xx.jar
  • logback-classic-0.9.xx.jar
    (tested with x=1 and xx=24)
to be present in lib directory. If you are using Eclipse then add all three jar files to the Build Path.

SLF4J can be downloaded here: http://www.slf4j.org/download.html
Logback can be downloaded at: http://logback.qos.ch/download.html

Your feedback is welcome!

Cheers,

Thomas

-----------------------------------------------------------------------------------------------------------

Here are the single pieces of the patch in detail for your review.
For easier reading I have included all new files as is, not as patches and also I have abbreviated the copyright / license comments.
Download the full patch from Issue 829 if you want to integrate logging capabilites in your codebase.

Code: Select all
Index: net/pms/PMS.java
===================================================================
--- net/pms/PMS.java   (revision 410)
+++ net/pms/PMS.java   (working copy)
@@ -25,18 +25,15 @@
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.LineNumberReader;
 import java.io.PrintStream;
-import java.io.PrintWriter;
 import java.net.BindException;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.Locale;
 import java.util.StringTokenizer;
 import java.util.UUID;
@@ -99,6 +96,7 @@
 import net.pms.io.OutputTextConsumer;
 import net.pms.io.ProcessWrapperImpl;
 import net.pms.io.WinUtils;
+import net.pms.logging.LoggingConfigFileLoader;
 import net.pms.network.HTTPServer;
 import net.pms.network.ProxyServer;
 import net.pms.network.UPNPHelper;
@@ -115,6 +113,8 @@
 import java.net.URI;
 import java.net.URLDecoder;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.notNullValue;
@@ -124,6 +124,9 @@
    private static final String UPDATE_SERVER_URL = "http://ps3mediaserver.googlecode.com/svn/trunk/ps3mediaserver/update.data"; //$NON-NLS-1$
    public static final String VERSION = "1.20.409"; //$NON-NLS-1$
    public static final String AVS_SEPARATOR = "\1"; //$NON-NLS-1$
+   
+   // (innot): The logger used for all logging.
+   public static final Logger logger = LoggerFactory.getLogger(PMS.class);
 
    // TODO(tcox):  This shouldn't be static
    private static PmsConfiguration configuration;
@@ -183,12 +186,6 @@
    public static SimpleDateFormat sdfDate;
    public static SimpleDateFormat sdfHour;
    
-   public static final int DEBUG = 0;
-   public static final int INFO = 1;
-   public static final int MINIMAL = 2;
-   
-   private PrintWriter pw;
-   
    public ArrayList<Process> currentProcesses = new ArrayList<Process>();
    
    private PMS() {
@@ -282,21 +279,6 @@
       
       registry = new WinUtils();
                
-      File debug = null;
-      try {
-         debug = new File("debug.log"); //$NON-NLS-1$
-         pw = new PrintWriter(new FileWriter(debug)); //$NON-NLS-1$
-      } catch (Throwable e) {
-         PMS.minimal("Error in accessing debug.log..."); //$NON-NLS-1$
-         pw = null;
-      } finally {
-         if (pw == null) {
-            PMS.minimal("Using temp folder for debug.log..."); //$NON-NLS-1$
-            debug = new File(configuration.getTempFolder(), "debug.log"); //$NON-NLS-1$
-            pw = new PrintWriter(new FileWriter(debug)); //$NON-NLS-1$
-         }
-      }
-      
       AutoUpdater autoUpdater = new AutoUpdater(UPDATE_SERVER_URL, PMS.VERSION);
       if (System.getProperty("console") == null) {//$NON-NLS-1$
          frame = new LooksFrame(autoUpdater, configuration);
@@ -322,6 +304,7 @@
       minimal("OS " + System.getProperty("os.name") + " " + System.getProperty("os.arch")  + " " + System.getProperty("os.version")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
       minimal("Encoding: " + System.getProperty("file.encoding")); //$NON-NLS-1$ //$NON-NLS-2$
       minimal("Temp folder: " + configuration.getTempFolder()); //$NON-NLS-1$
+      minimal("Logging config file: "+LoggingConfigFileLoader.getConfigFilePath()); //$NON-NLS-1$
       
       RendererConfiguration.loadRendererConfigurations();
       
@@ -454,8 +437,6 @@
                   }
                }
                PMS.get().getServer().stop();
-               if (pw != null)
-                  pw.close();
                Thread.sleep(500);
             } catch (Exception e) { }
          }
@@ -964,82 +945,30 @@
       UPNPHelper.sendAlive();
    }
    
+   // (innot): A note on Level names: PMS uses the following logging levels
+   // in ascending order: DEBUG, INFO, TRACE/minimal, ERROR
+   // The SLF4J logging API uses TRACE, DEBUG, INFO, WARN and ERROR
+   // So the levels get remapped in the following convenience methods.
+   // TODO: remove these methods and replace their callers with direct
+   // calls to the SLF4J API.
    public static void debug(String msg) {
-      if (instance != null)
-         instance.message(DEBUG, msg);
-      else
-         System.out.println(msg);
+      if (logger != null)
+         logger.trace(msg);
    }
    
    public static void info(String msg) {
-      if (instance != null)
-         instance.message(INFO, msg);
+      if (logger != null)
+         logger.debug(msg);
    }
    
    public static void minimal(String msg) {
-      if (instance != null)
-         instance.message(MINIMAL, msg);
+      if (logger != null)
+         logger.info(msg);
    }
    
    public static void error(String msg, Throwable t) {
-      if (instance != null)
-         instance.message(msg, t);
-   }
-   
-   private void message(int l, String message) {
-      
-         String name = Thread.currentThread().getName();
-         if (name != null && message != null) {
-            String lev = "DEBUG "; //$NON-NLS-1$
-            if (l == 1)
-               lev = "INFO  "; //$NON-NLS-1$
-            if (l == 2)
-               lev = "TRACE "; //$NON-NLS-1$
-            
-            
-            message = "[" + name + "] " + lev + sdfHour.format(new Date(System.currentTimeMillis())) + " " + message;  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-            if (l == 2)
-               System.out.println(message);
-            if (l >= configuration.getLoggingLevel()) {
-               
-               if (frame != null) {
-                  frame.append(message.trim() + "\n"); //$NON-NLS-1$
-               }
-            }
-            if (pw != null) {
-               pw.println(message);
-               pw.flush();
-            }
-         }
-      
-   }
-   
-   private void message(String error, Throwable t) {
-      
-         String name = Thread.currentThread().getName();
-         if (error != null) {
-            String throwableMsg = ""; //$NON-NLS-1$
-            if (t != null)
-               throwableMsg = ": " + t.getMessage(); //$NON-NLS-1$
-            error = "[" + name + "] " + sdfHour.format(new Date(System.currentTimeMillis())) + " " + error + throwableMsg; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-         }
-         if (error != null) {
-            if (pw != null)
-               pw.println(error);
-            if (frame != null) {
-               frame.append(error.trim() + "\n"); //$NON-NLS-1$
-            }
-         }
-         if (t != null && pw != null) {
-            t.printStackTrace(pw);
-            t.printStackTrace();
-         }
-         if (pw != null)
-            pw.flush();
-         
-         if (error != null)
-            System.err.println(error);
-      
+      if (logger != null)
+         logger.error(msg,t);
    }
 
    private String uuid;
@@ -1166,7 +1095,13 @@
          if (System.getProperty("noconsole") == null) //$NON-NLS-1$
             System.setProperty("console", "true"); //$NON-NLS-1$ //$NON-NLS-2$
       }
+      
       configuration = new PmsConfiguration();
+
+      // Load the (optional) logback config file. This has to be called after 'new PmsConfiguration'
+      // as the logging starts immediately and some filters need the PmsConfiguration.
+      LoggingConfigFileLoader.load();   
+
       PMS.get();
       try {
          // let's allow us time to show up serious errors in the GUI before quitting



In PMS.java the major changes are that the calls to the static methods debug(), info(), and minimal() now call a SLF4J logger. Other than that all code for the debugging output has been deleted. The only other addition is a call to LoggingConfigFileLoader.load(), which is a new class located in net.pms.logging.

This class will configure the logback logger from the 'logback.xml' config file if it exists. I thought about using a user supplied filename, but I think this is sufficient.

Code: Select all
Index: net/pms/logging/LoggingConfigFileLoader.java
===================================================================
/*
 * PS3 Media Server, for streaming any medias to your PS3.
 */

package net.pms.logging;

import java.io.File;

import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;

/**
 * Simple loader for logback configuration files.
 *
 * @author thomas@innot.de
 */
public class LoggingConfigFileLoader {

   private static final String DEFAULT_CONFIGFILENAME = "logback.xml";
   private static final String HEADLESS_CONFIGFILENAME = "logback.headless.xml";

   private static String filepath = null;

   /**
    * Gets the full path of a successfully loaded Logback configuration file.
    *
    * If the configuration file could not be loaded the string
    * <code>internal defaults</code> is returned.
    *
    * @return pathname or <code>null</code>
    */
   public static String getConfigFilePath() {
      if (filepath != null) {
         return filepath;
      } else {
         return "internal defaults";
      }
   }

   /**
    * Loads the (optional) Logback configuration file.
    *
    * It loads the file {@link #DEFAULT_CONFIGFILENAME} from the current
    * directory and (re-)initializes Logback with this file. If running
    * headless (<code>System.Property("console")</code> set), then the
    * alternative config file {@link #HEADLESS_CONFIGFILENAME} is tried first.
    *
    * If no config file can be found in the CWD, then nothing is loaded and
    * Logback will use the logback.xml file on the classpath as a default. If
    * this doesn't exist then a basic console appender is used as fallback.
    *
    * <strong>Note:</strong> Any error messages generated while parsing the
    * config file are dumped only to <code>stdout</code>.
    */
   public static void load() {

      // Note: Do not use any logging method in this method!
      // Any logging would cause PMS.get() to be called from the
      // FrameAppender, which in turn would start the PMS instance, which
      // would cause further log messages. This will lead to either lost
      // log messages or Deadlocks!
      // Any status output needs to go to the console.

      boolean headless = !(System.getProperty("console") == null);

      File file = null;

      if (headless) {
         file = new File(HEADLESS_CONFIGFILENAME);
      }

      if (file == null || !file.exists()) {
         file = new File(DEFAULT_CONFIGFILENAME);
      }

      if (!file.exists()) {
         // No problem, the internal logback.xml is used.
         return;
      }

      // Now get logback to actually use the config file

      ILoggerFactory ilf = LoggerFactory.getILoggerFactory();
      if (!(ilf instanceof LoggerContext)) {
         // Not using LogBack.
         // Can't configure the logger, so just exit
         return;
      }

      LoggerContext lc = (LoggerContext) ilf;

      try {
         JoranConfigurator configurator = new JoranConfigurator();
         configurator.setContext(lc);
         // the context was probably already configured by
         // default configuration rules
         lc.reset();
         configurator.doConfigure(file);

         // Save the filepath after loading the file
         filepath = file.getAbsolutePath();

      } catch (JoranException je) {
         // StatusPrinter will handle this
      }
      StatusPrinter.printInCaseOfErrorsOrWarnings(lc);
   }
}



Now lets take a look at the 'logback.xml' file, which I think is easy to understand. If you don't then take a look at the (very good) logback manual.

Code: Select all
Index: logback.xml
===================================================================
<configuration debug="true" >

  <!-- See http://logback.qos.ch/manual/index.html for more information -->
  <!-- on how to configure the Logback logging engine. -->


  <define name="debugLogPath" class="net.pms.logging.DebugLogPathDefiner" />
 
  <appender name="frame" class="net.pms.logging.FrameAppender">
    <!-- only show event of the user selected level on the gui -->
    <filter class="net.pms.logging.PMSThresholdFilter" />
 
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder" >
      <!-- This pattern mimics the original PMS debug.log format -->
          <pattern>%-13([%thread]) %-5level %d{HH:mm:ss.SSS} -- %msg%n</pattern>
    </encoder>
  </appender>

 
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- only show event INFO and higher on the Console. -->
    <!-- This is equivalent to the old behaviour -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>INFO</level>
    </filter>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder" >
      <!-- This pattern mimics the original PMS debug.log format -->
          <pattern>%-13([%thread]) %-5level %d{HH:mm:ss.SSS} -- %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="debug.log" class="ch.qos.logback.core.FileAppender">
    <file>${debugLogPath}debug.log</file>
    <append>false</append>  <!-- Start new debug.log on each application start -->
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder" >
      <!-- This pattern mimics the original PMS debug.log format -->
        <pattern>%-13([%thread]) %-5level %d{HH:mm:ss.SSS} -- %msg%n</pattern>
    </encoder>
  </appender>

  <!-- If unlimited growth of debug.log is not desired use the "debug.log.10MB" -->
  <!-- appender instead of the "debug.log" appender. This appender will -->
  <!-- roll the debug.log file after 10 MBytes and will keep a maximum -->
  <!-- of 5 old and compressed logs. -->
    <appender name="debug.log.10MB" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${debugLogPath}debug.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>${debugLogPath}debug.log.%i.zip</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>5</maxIndex>
    </rollingPolicy>
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder" >
        <pattern>%-13([%thread]) %-5level %d{HH:mm:ss.SSS} -- %msg%n</pattern>
    </encoder>
  </appender>

  <root level="ALL">
    <appender-ref ref="frame" />
    <appender-ref ref="STDOUT" />
    <appender-ref ref="debug.log" />
    <!-- <appender-ref ref="debug.log.10MB" /> -->
  </root>
 
</configuration>

This configuration file uses some custom classes, namely
  • net.pms.logging.DebugLogPathDefiner
  • net.pms.logging.FrameAppender
  • net.pms.logging.PMSThresholdFilter
The first one is used to set the path to use for debug.log. Like PMS now it will use the current directory or, if it is not writable, the system temp dir.
Code: Select all
Index: net/pms/logging/DebugLogPathDefiner.java
===================================================================
/*
 * PS3 Media Server, for streaming any medias to your PS3.
 */
package net.pms.logging;

import java.io.File;

import ch.qos.logback.core.PropertyDefinerBase;

/**
 * Logback PropertyDefiner to set the path for the <code>debug.log</code> file.
 * <p>
 * If the current working directory is writable it returns an empty string. If
 * not then the path to the system temp directory is returned.
 * </p>
 * <p>
 * This is equivalent to the old behavior of PMS.
 * </p>
 *
 * @see System.getProperty("java.io.tmpdir");
 * @author thomas@innot.de
 *
 */
public class DebugLogPathDefiner extends PropertyDefinerBase {

   /*
    * (non-Javadoc)
    *
    * @see ch.qos.logback.core.spi.PropertyDefiner#getPropertyValue()
    */
   @Override
   public String getPropertyValue() {

      // Check if current directory is writable
      File file = new File("write_test_file");
      try {
         file.createNewFile();
         if (file.canWrite()) {
            file.delete();
            return "";
         }
      } catch (Exception e) {
         // Could not create / write the file
      }

      // Return path to temp folder, which should be writeable
      return System.getProperty("java.io.tmpdir");
   }

}

net.pms.logging.FrameAppender is a special 'Appender' that will send all received logging messages to the PMS GUI (if it is open). Note that this appender is configured in 'logback.xml' to behave exactly as today, even with the same output format, but this could be easily changed if required, even by the user.
Code: Select all
Index: net/pms/logging/FrameAppender.java
===================================================================
/*
 * PS3 Media Server, for streaming any medias to your PS3.
 */

package net.pms.logging;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import net.pms.PMS;
import net.pms.gui.IFrame;

import ch.qos.logback.core.UnsynchronizedAppenderBase;
import ch.qos.logback.core.encoder.Encoder;
import ch.qos.logback.core.status.ErrorStatus;

/**
 * Special Logback appender to 'print' log messages on the PMS GUI.
 *
 * @author thomas@innot.de
 *
 */
public class FrameAppender<E> extends UnsynchronizedAppenderBase<E> {

   private IFrame frame;

   private Encoder<E> encoder;

   private final ByteArrayOutputStream outputstream = new ByteArrayOutputStream(
         256);

   private final Object lock = new Object();

   /**
    * Checks that the required parameters are set and if everything is in
    * order, activates this appender.
    */
   @Override
   public void start() {
      int error = 0;
      if (this.encoder == null) {
         addStatus(new ErrorStatus(
               "No encoder set for the appender named \"" + name + "\".",
               this));
         error++;
      } else {
         try {
            encoder.init(outputstream);
         } catch (IOException ioe) {
            addStatus(new ErrorStatus(
                  "Failed to initialize encoder for appender named ["
                        + name + "].", this, ioe));
            error++;
         }
      }

      if (error == 0) {
         super.start();
      }
   }

   /* (non-Javadoc)
    * @see ch.qos.logback.core.UnsynchronizedAppenderBase#append(java.lang.Object)
    */
   @Override
   protected void append(E eventObject) {

      if (frame == null) {
         // TODO: somehow ensure that PMS.get() does not get called before
         // PMS has been instantiated. Otherwise PMS will be instantiated by
         // this call and any log messages generated during PMS startup will
         // be ignored.
         // Currently the PMS API does not have a method to check if it
         // has been instantiated.

         frame = PMS.get().getFrame();
      }

      try {
         if (frame != null) {
            synchronized (lock) {
               this.encoder.doEncode(eventObject);
               String msg = outputstream.toString("UTF-8");
               frame.append(msg);
               outputstream.reset();
            }

         }
      } catch (IOException ioe) {
         addStatus(new ErrorStatus("IO failure in appender", this, ioe));
      }

   }

   /**
    * @return The encoder associated with this appender, or <code>null</code>
    *         if no encoder has been set.
    */
   public Encoder<E> getEncoder() {
      return encoder;
   }

   /**
    * Set the logback Encoder for the appender.
    *
    * Needs to be called (via the <encoder class="..."> element in the
    * logback.xml config file) before the appender can be started.
    *
    * @param
    */
   public void setEncoder(Encoder<E> encoder) {
      this.encoder = encoder;
   }
}

The FrameAppender is used in conjunction with the PMSThresholdFilter. This class is basically just for being 100% compatible with the current behaviour of having an PMS.conf entry 'level=x' with x=[0..2] to limit the logging output on the GUI. I don't know if anyone has ever used this entry (it is not settable from the GUI), but hey -- it's still supported.
On the other hand, together with some GUI code this class could be used to set the GUI logging level from the GUI itself.
Code: Select all
Index: net/pms/logging/PMSThresholdFilter.java
===================================================================
/*
 * PS3 Media Server, for streaming any medias to your PS3.
 */
package net.pms.logging;

import net.pms.PMS;
import net.pms.configuration.PmsConfiguration;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;

/**
 * Special Logback Filter that filters according to the level set in the
 * PmsConfiguration.
 *
 * The threshold level is taken from {@link PmsConfiguration#getLoggingLevel()},
 * and is an integer between 0 and 3. These levels are used historically be PMS
 * and have the following mappings:
 *
 * <table border="1">
 * <tr><th>value</th><th>PMS</th><th>SLF4J</th></tr>
 * <tr><td>0</td><td>DEBUG</td><td>TRACE</td></tr>
 * <tr><td>1</td><td>INFO</td><td>DEBUG</td></tr>
 * <tr><td>2</td><td>TRACE</td><td>INFO</td></tr>
 * <tr><td>3</td><td>--</td><td>OFF</td></tr>
 * </table>
 * All logging events below the threshold are denied, all others are accepted.
 *
 * @author thomas@innot.de
 *
 */
public class PMSThresholdFilter extends Filter<ILoggingEvent> {

   // The old PMS logging levels.
   // Only used to convert from the old levels that might be used in some
   // pms.conf files to the SLF4J levels.
   private static final int PMS_DEBUG = 0;
   private static final int PMS_INFO = 1;
   private static final int PMS_MINIMAL = 2;

   @Override
   public FilterReply decide(ILoggingEvent event) {
      if (!isStarted()) {
         return FilterReply.NEUTRAL;
      }

      // Don't cache the configuration and/or logging level as
      // they might be changed anytime via the GUI (in future
      // versions of the GUI)
      int pmslevel = PMS.getConfiguration().getLoggingLevel();

      // Convert old PMSConfiguration Level to SLF4J level
      Level level;
      switch (pmslevel) {
      case PMS_DEBUG:
         level = Level.TRACE;
         break;
      case PMS_INFO:
         level = Level.DEBUG;
         break;
      case PMS_MINIMAL:
         level = Level.INFO;
         break;
      default:
         level = Level.OFF;
      }
      if (event.getLevel().isGreaterOrEqual(level)) {
         return FilterReply.ACCEPT;
      } else {
         return FilterReply.DENY;
      }
   }

}


To show how powerful logback is, I included a second logback configuration file. This is targeted at users like me, who run PMS as a real server without any GUI. With this config file nothing goes to the console and the debug.log file is rolled over daily at midnight, while keeping the logs of the last 7 days.
The patched PMS will use this file automatically when running in headless mode.
Code: Select all
Index: logback.headless.xml
===================================================================
<configuration debug="true" >

  <!-- This is more of a sample. Tweak as required. -->
  <!-- See http://logback.qos.ch/manual/index.html for more information -->
  <!-- on how to configure the Logback logging engine. -->

  <appender name="debug.log" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>debug.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- daily rollover -->
      <fileNamePattern>debug.%d{yyyy-MM-dd}.log</fileNamePattern>
      <!-- keep 7 days' worth of history -->
      <maxHistory>7</maxHistory>
    </rollingPolicy>
    <encoder>
      <!-- This pattern mimics the original PMS debug.log format -->
      <pattern>%-13([%thread]) %-5level %d{HH:mm:ss.SSS} -- %msg%n</pattern>
    </encoder>
    <!-- Log all events to debug.log -->
    <!-- Change the level if the constant logging activity is undesirable -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>ALL</level>
    </filter>
  </appender>

  <root level="ALL">
    <appender-ref ref="debug.log" />
  </root>
</configuration>

The final three files contain just housekeeping items for the build files. Basically its just the addition of 'logback.xml' to the generated pms.jar. As this copy is on the classpath logback will use it automatically as a default. The same file and its headless variant are again included in the distribution package as a template for user edits.
Code: Select all
Index: build.xml
===================================================================
--- build.xml   (revision 410)
+++ build.xml   (working copy)
@@ -38,12 +38,12 @@
           <zipgroupfileset refid="libs.ref" />
          
          <fileset dir="net" includes="**/*.class" excludes="**/*.java" />
-         <fileset dir="." includes="resources/**,net/pms/*.properties" />
+         <fileset dir="." includes="resources/**,net/pms/*.properties,logback.xml" />
       </jar>
       
        <mkdir dir="dist" />
        <tar destfile="dist/pms-generic-linux-unix-${version}.tgz" compression="gzip">
-          <tarfileset dir="." mode="777" prefix="pms-linux-${version}" includes="LICENSE.txt,README,CHANGELOG,pms.jar,FAQ,PMS.sh,WEB.conf,renderers/**,plugins/*.txt,linux/**"/>
+          <tarfileset dir="." mode="777" prefix="pms-linux-${version}" includes="LICENSE.txt,README,CHANGELOG,pms.jar,FAQ,PMS.sh,WEB.conf,renderers/**,plugins/*.txt,linux/**,logback*.xml"/>
        </tar>
           
        <delete dir="classes" />


Code: Select all
 
Index: osx/build.xml
===================================================================
--- osx/build.xml   (revision 410)
+++ osx/build.xml   (working copy)
@@ -41,7 +41,7 @@
          </manifest>       
          <zipgroupfileset refid="libs.ref" />
          <fileset dir="net" includes="**/*.class" excludes="**/*.java" />
-         <fileset dir="." includes="resources/**,net/pms/*.properties" />
+         <fileset dir="." includes="resources/**,net/pms/*.properties,logback.xml" />
       </jar>
       
           <delete dir="osx/classes" quiet="true" />
@@ -77,6 +77,8 @@
            <copy file="FAQ" todir="${javaResources}"/>
            <copy file="README" todir="${javaResources}"/>
            <copy file="WEB.conf" todir="${javaResources}"/>
+         <copy file="logback.xml" todir="${javaResources}"/>
+         <copy file="logback.headless.xml" todir="${javaResources}"/>
            <mkdir dir="${javaResources}/plugins"/>
       <copy todir="${javaResources}/plugins">
              <fileset dir="plugins"/>



Note: I didn't include the 'logback.headless.xml' configuration into the Windows distribution because I think that PMS is rarely used headless on Windows. Might be wrong though.
Code: Select all
Index: nsis/setup.nsi
===================================================================
--- nsis/setup.nsi   (revision 410)
+++ nsis/setup.nsi   (working copy)
@@ -47,6 +47,7 @@
   File "LICENSE.txt"
   SetOverwrite off
   File "WEB.conf"
+  File "logback.xml"
   
   ;Store install folder
   WriteRegStr HKCU "${REG_KEY_SOFTWARE}" "" $INSTDIR
@@ -89,6 +90,7 @@
   Delete /REBOOTOK "$INSTDIR\FAQ"
   Delete /REBOOTOK "$INSTDIR\LICENSE.txt"
   Delete /REBOOTOK "$INSTDIR\debug.log"
+  Delete /REBOOTOK "$INSTDIR\logback.xml"
   RMDir /REBOOTOK "$INSTDIR"
   

innot
 
Posts: 8
Joined: Thu Jul 29, 2010 2:52 pm

Re: Patch to add good logging support to PMS

Postby WorldOfHurt » Thu Aug 19, 2010 9:14 pm

Cheers. That has often bugged me, but I could never face the prospect of changing it.

I figured someone else would eventually get sick of it, and now they have ;)

Good stuff.
User avatar
WorldOfHurt
 
Posts: 252
Joined: Thu Mar 19, 2009 10:49 pm
Location: Winchester, UK


Return to Developers

Who is online

Users browsing this forum: Yahoo [Bot] and 2 guests