JetCracker

Life-time learner's blog

Logging in Java: Fast introduction. Basic standards and guidelines

Logging is used to track the execution flow of the programme. It’s is also useful for debugging your application when an interactive debugging is not available (e.g. in web-applications).

All good and sophisticated programmes have logging. Just look into Windows Event log and you will find a lot of interesting things, which are happening under the covers of friendly GUI of this amazing OS. 😉

The simplest way to log is to write what is happening to Console or to file. But you always have to think about the format of log. And there is a problem of storing log files on disk. Today, there are lots of logging engines for different programming languages and platforms, which significantly simplify dealing with logs. In Java the most popular are Apache log4j, SLF4J, LogBACK. Last two ones are becoming more and more popular, but I’d like to be conservative and tell about log4j. It’s classic!

Main features

  • Controlling over which logging enabled or disabled.
  • Managing output destinations (console, file, system log, remote servers, e-mail).
  • Managing output format.
  • Different levels of logging (TRACE, DEBUG, INFO, WARN, ERROR, SEVERE).
  • Simple and flexible configuration.
More than that, logging systems doesn’t influence the execution flow of the programme. Logging is executed in different thread (threads) and your code runs as there is not any logging.

Setting up and configuring log4j

To set up log4j you just need to download it’s jar-package and add it into your class path.

Extract the archive and you will find a jar-file in it.

Usually, if you are working in Eclipse or Netbeans IDE, you just have to copy log4j.jar to /lib directory of the project and/or add it in project properties.

If you are using Maven, you can add a dependency into your pom.xml and it will grab the log4j library for you automatically.

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.15</version>
</dependency>

Configuring log4j

There are different ways to configure log4j. I’ll try to describe the most universal (in my opinion) one.

1. Create a Java package in your project “logging”.

2. Place the following files into that package.

log4j.dtd

<!-- Authors: Chris Taylor, Ceki Gulcu. -->

<!-- Version: 1.2 -->

<!-- A configuration element consists of optional renderer
elements,appender elements, categories and an optional root
element. -->

<!ELEMENT log4j:configuration (renderer*, appender*,(category|logger)*,root?,
                               categoryFactory?)>

<!-- The "threshold" attribute takes a level value such that all -->
<!-- logging statements with a level equal or below this value are -->
<!-- disabled. -->

<!-- Setting the "debug" or "configDebug" attributes enable the printing -->
<!-- of internal log4j logging statements.                               -->

<!-- By default, debug attribute is "null", meaning that we not touch    -->
<!-- internal log4j logging settings.                                    -->

<!ATTLIST log4j:configuration
  xmlns:log4j              CDATA #FIXED "http://jakarta.apache.org/log4j/"
  threshold                (all|debug|info|warn|error|fatal|off|null) "null"
  debug                    (true|false|null)  "null"
>

<!-- renderer elements allow the user to customize the conversion of  -->
<!-- message objects to String.                                       -->

<!ELEMENT renderer EMPTY>
<!ATTLIST renderer
  renderedClass  CDATA #REQUIRED
  renderingClass CDATA #REQUIRED
>

<!-- Appenders must have a name and a class. -->
<!-- Appenders may contain an error handler, a layout, optional parameters -->
<!-- and filters. They may also reference (or include) other appenders. -->
<!ELEMENT appender (errorHandler?, param*, layout?, filter*, appender-ref*)>
<!ATTLIST appender
  name 		ID 	#REQUIRED
  class 	CDATA	#REQUIRED
>

<!ELEMENT layout (param*)>
<!ATTLIST layout
  class		CDATA	#REQUIRED
>

<!ELEMENT filter (param*)>
<!ATTLIST filter
  class		CDATA	#REQUIRED
>

<!-- ErrorHandlers can be of any class. They can admit any number of -->
<!-- parameters. -->

<!ELEMENT errorHandler (param*, root-ref?, logger-ref*,  appender-ref?)>
<!ATTLIST errorHandler
   class        CDATA   #REQUIRED
>

<!ELEMENT root-ref EMPTY>

<!ELEMENT logger-ref EMPTY>
<!ATTLIST logger-ref
  ref IDREF #REQUIRED
>

<!ELEMENT param EMPTY>
<!ATTLIST param
  name		CDATA   #REQUIRED
  value		CDATA	#REQUIRED
>

<!-- The priority class is org.apache.log4j.Level by default -->
<!ELEMENT priority (param*)>
<!ATTLIST priority
  class   CDATA	#IMPLIED
  value	  CDATA #REQUIRED
>

<!-- The level class is org.apache.log4j.Level by default -->
<!ELEMENT level (param*)>
<!ATTLIST level
  class   CDATA	#IMPLIED
  value	  CDATA #REQUIRED
>

<!-- If no level element is specified, then the configurator MUST not -->
<!-- touch the level of the named category. -->
<!ELEMENT category (param*,(priority|level)?,appender-ref*)>
<!ATTLIST category
  class         CDATA   #IMPLIED
  name		CDATA	#REQUIRED
  additivity	(true|false) "true"
>

<!-- If no level element is specified, then the configurator MUST not -->
<!-- touch the level of the named logger. -->
<!ELEMENT logger (level?,appender-ref*)>
<!ATTLIST logger
  name		ID	#REQUIRED
  additivity	(true|false) "true"
>

<!ELEMENT categoryFactory (param*)>
<!ATTLIST categoryFactory
   class        CDATA #REQUIRED>

<!ELEMENT appender-ref EMPTY>
<!ATTLIST appender-ref
  ref IDREF #REQUIRED
>

<!-- If no priority element is specified, then the configurator MUST not -->
<!-- touch the priority of root. -->
<!-- The root category always exists and cannot be subclassed. -->
<!ELEMENT root (param*, (priority|level)?, appender-ref*)>

<!-- ==================================================================== -->
<!--                       A logging event                                -->
<!-- ==================================================================== -->
<!ELEMENT log4j:eventSet (log4j:event*)>
<!ATTLIST log4j:eventSet
  xmlns:log4j             CDATA #FIXED "http://jakarta.apache.org/log4j/"
  version                (1.1|1.2) "1.2"
  includesLocationInfo   (true|false) "true"
>

<!ELEMENT log4j:event (log4j:message, log4j:NDC?, log4j:throwable?,
                       log4j:locationInfo?) >

<!-- The timestamp format is application dependent. -->
<!ATTLIST log4j:event
    logger     CDATA #REQUIRED
    level      CDATA #REQUIRED
    thread     CDATA #REQUIRED
    timestamp  CDATA #REQUIRED
>

<!ELEMENT log4j:message (#PCDATA)>
<!ELEMENT log4j:NDC (#PCDATA)>

<!ELEMENT log4j:throwable (#PCDATA)>

<!ELEMENT log4j:locationInfo EMPTY>
<!ATTLIST log4j:locationInfo
  class  CDATA	#REQUIRED
  method CDATA	#REQUIRED
  file   CDATA	#REQUIRED
  line   CDATA	#REQUIRED
>

log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
<log4j:configuration>

    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out" />
        <layout class="org.apache.log4j.PatternLayout">
            <!--
            <param name="ConversionPattern"
                value="[%d{ISO8601}] %-5p [%c] %m %n" />
            -->
            <param name="ConversionPattern" value="%-5p [%c] %m %n" />
        </layout>
    </appender>

    <appender name="STDERR" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.err" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                value="[%d{ISO8601}] %-5p [%c] %m %n" />
        </layout>
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMin" value="ERROR" />
            <param name="LevelMax" value="FATAL" />
        </filter>
    </appender>

    <appender name="SW_ROLLING_FILE"
        class="org.apache.log4j.RollingFileAppender">
        <param name="File" value="/users/codyburleson/dev/sw.log" />
        <param name="Append" value="true" />
        <param name="MaxFileSize" value="500KB" />
        <param name="MaxBackupIndex" value="5" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                value="[%d{ISO8601}] %-5p %m%n" />
        </layout>
    </appender>

    <!--                          -->
    <!-- Declare the SMTPAppender -->
    <!--                          -->
    <!--
    <appender name="EMAIL" class="org.apache.log4j.net.SMTPAppender">
        <param name="BufferSize" value="512" />
        <param name="SMTPHost" value="smtp.mail.yahoo.com" />
        <param name="From" value="whoever@wherever.com" />
        <param name="To" value="whoever@wherever.com" />
        <param name="Subject"
            value="[SMTPAppender] Application message" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                value="[%d{ISO8601}]%n%n%-5p%n%n%c%n%n%m%n%n" />
        </layout>
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMin" value="FATAL" />
            <param name="LevelMax" value="FATAL" />
        </filter>
    </appender>
     -->

    <logger name="com.base22" additivity="false">
        <level value="trace" />
        <appender-ref ref="SW_ROLLING_FILE" />
        <appender-ref ref="STDOUT" />
    </logger>

    <!-- ** -->

    <root>
        <level value="ERROR" />
        <appender-ref ref="STDERR" />
        <!--  <appender-ref ref="EMAIL" /> -->
    </root>

</log4j:configuration>

3. Find SSB_ROLLING_FILE and change the name to reflect your own application’s name. Change both the declaration and the reference to it in the logger that is defined later in the file.

4. In the file appender, customize the File parameter so that the logs will get written to a path of your own choosing.

5. The first logger defined is set to log on anything under the package com.base22. Change that to reflect some parent package in your own application.

6. Initialize your logging system in your application. Execute the following code when your application starts up.

    URL url = Startup.class.getResource("/logging/log4j.xml");
    DOMConfigurator.configure(url.getFile());

7. To use logger just add the following static field into class:

private static Logger log = Logger.getLogger(AttachmentManager.class.getName());

Basic logging guidelines

1. All messages that describe the execution flow (e.g. enter the method, exit from method, etc) should have a logging level TRACE. Also, you should choose a format of your messages and stick to it. A good idea is to always specify the method name.

Example:

int myMethod(int param) {
    if(log.isTraceEnabled())
    log.trace(">> myMethod(): "+param);

    // some code ...

    if(log.isTraceEnabled())
        log.trace("myMethod(): log message");

    // some code ...

    if(log.isTraceEnabled())
        log.trace("<< myMethod(): "+returnValue);
    return returnValue;
}

For messages from within a constructor then write (** means that it is from a constructor)

    if(log.isTraceEnabled())
        log.trace("** ConstructorName(): log message");

2. Messages that are needed for debugging your code, should have a logging level DEBUG.

Пример:

void myMethod(int param) {
    // some code

    if(log.isDebugEnabled()) log.debug("myMethod(): "+param);

    // some code ...
}

3. Messages addressed to the user or administrator of your application should have the level at least INFO:

  • INFO – information message;
  • WARN – not significant error (usually expected error);
  • ERROR – significant error that is not expected in normal scenario;
  • FATAL – fatal error. Further execution of a program is not possible.
If it is a error message, specify a reason of it (e.g. the exception which cause the error).

4. The variable of logger of the class should be declared the first.

5. You can also add a code templates for logging in your IDE. For Netbeans I would recommend that you add (Tools -> Options -> Editor -> Code Templates) the following ones (not tested in Eclipse):

  1. trace
    if(${LOG instanceof="org.apache.log4j.Logger" default="log" editable=false}.isTraceEnabled()) {
        ${LOG}.trace("${enclosing_method}(): ${cursor}");
    }
  2. debug
    if(${LOG instanceof="org.apache.log4j.Logger" default="log" editable=false}.isDebugEnabled()) {
        ${LOG}.debug("${enclosing_method}(): ${cursor}");
    }
  3. info
    ${LOG instanceof="org.apache.log4j.Logger" default="log" editable=false}.info("${encosing_method}(): ${cursor}");
    
  4. warn
    ${LOG instanceof="org.apache.log4j.Logger" default="log" editable=false}.warn("${encosing_method}(): ${cursor}");
    
  5. error
    ${LOG instanceof="org.apache.log4j.Logger" default="log" editable=false}.error("${encosing_method}(): ${cursor}");
    

Usage: write the name of a template and press TAB.

I hope this post helps.

Anton Danshin

Sources

Advertisements

2 responses to “Logging in Java: Fast introduction. Basic standards and guidelines

  1. Pingback: Spring 2012: Overall stats « JetCracker

  2. jetcracker March 7, 2013 at 22:38

    Reblogged this on EasyTag.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: