HomeAbout Mark van HedelContact Mark van Hedel



Log4j in ColdFusion

As I did say last week I was working on a small project to see how log4j could fit in ColdFusion. What I did was create one CFC to talk to log4j and do all the logging. This CFC is finished now and I have it implemented in some small ColdFusion application to see how it works.

The idea about using log4j is that I do want to be more flexible with logging when I create applications. With using it I can define myself what kind of logging I want in application, so for instance on a development machine I do want debug, info, warning, error and fatal to log, when I'm in a production environment I only want to see error and fatal messages in my logfile. And the very good thing is that log4j is already part of the ColdFusion install, so everyone using ColdFusion can just start using it.

What I did to start with is create a CFC that does the initialization for log4j and sets the entire thing up. After that I can just push log messages there to be saved. Let's start to look at this CFC At first there is an init method that has to be called when instantiating the CFC to set the configuration for the logger. This init method can receive 3 variables but needs at least one.

  1. XML configuration file : This is a configuration file that determines what to log, I'll get to that a little later
  2. cache : a true or false to determine if log categories should be cached in the component
  3. refreshRate : A numeric variable to determine if the logger should automatically refresh and reset the configuration after the number of seconds entered here. You probably don't want to set this because it might cost you a lot of performance but if you want to you can.
Now for the call :

logger = createObject("component", "org.flexpair.logger.JLogger").init("C:\mark\logConfig.xml", true, 0);
application.markLogger = logger;

So with this code I initialized my logger to use logConfig.xml and it is ready to go, I've placed this instance of the logger in my application scope to be able to use it everywhere I need it. Now there are a number of different methods I can call here to log information.

application.markLogger.debug("debug log message", "org.flexpair.logger");
application.markLogger.info("info log message", "org.flexpair.logger");
application.markLogger.warn("warn log message", "org.flexpair.logger");
application.markLogger.error("error log message", "org.flexpair.logger");
application.markLogger.fatal("fatal log message", "org.flexpair.logger");

The first string I enter is the string I want to have in my logfile, the second one is the category that I want it logged in. This category can of course also just be defined by using metadata to find the name of the component we are calling this log method from. Now that everything is set up to work there are a few more things to do. First I'll tell you there is another method in the component called reInit. This doesn't expect any variables but will reload the log4j configuration file and use the new settings for your logging.

The strongest part about log4j is that by default it has a lot of different ways to log and is very flexible. This all sits in the configuration file that we can use. Our configuration file should be placed somewhere outside the webroot, so nobody can just browse and see it. Now in this configuration file we can determine how we want things logged. We start by adding appenders to our configuration file. These appenders decide what is going to happen to a specific logmessage. So in this example I've added 4 different appender, one for outputting everything to standard out, this is a JRun logfile.

After that 2 different logfiles on my system, where I for one of them decided what the levels are that are to be logged there and I've decided how big the log file can be at maximum. Last appender here defines a mail server to use for messages. So in the case I receive some fatal error I'll receive an email with the log message. After this part I've added a number of different settings to show how you can log specific messages. So for instance there is one saying that I want messages of warn or higher logged to logfile, but only if the category for the message is org, so this would say that org.flexpair.logger is in that category and should be logged.

After that I say error in org.flexpair should be logged to an error file and finally I say that I want all fatal messages emailed to me. The last part is the rootlogger and there you can define what the minimum level of logging is that I want now, if I set this to error it would say that info, debug and warn are skipped and not logged.

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

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" 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{1} - %m%n"/>
</layout>
</appender>

<appender name="logfile" class="org.apache.log4j.RollingFileAppender">
<param name="file" value="C:/mark/logging.log"/>
<param name="MaxFileSize" value="100KB"/>
<!-- Keep one backup file -->
<param name="MaxBackupIndex" value="5"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{ISO8601}] %p %t %c - %m%n"/>
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="INFO"/>
<param name="LevelMax" value="WARN"/>
</filter>
</appender>

<appender name="errorFile" class="org.apache.log4j.RollingFileAppender">
<param name="file" value="C:/mark/errorFile.log"/>
<param name="MaxFileSize" value="100KB"/>
<!-- Keep one backup file -->
<param name="MaxBackupIndex" value="5"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{ISO8601}] %p %t %c - %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="" />
<param name="SMTPPassword" value=""/>
<param name="SMTPUsername" value="" />
<param name="From" value="" />
<param name="To" value="" />
<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="ERROR"/>
<param name="LevelMax" value="FATAL"/>
</filter>
</appender>

<logger name="org">
<!-- Print only messages of level warn or above in the package org -->
<level value="warn"/>
<appender-ref ref="logfile" />
</logger>
<logger name="org.flexpair">
<!-- Print only messages of level error or above in the package org.flexpair -->
<level value="error"/>
<appender-ref ref="errorFile" />
</logger>
<logger name="org.flexpair">
<!-- Print only fatal messages to an email -->
<level value="fatal"/>
<appender-ref ref="EMAIL" />
</logger>
<root>
<level value="all" />
<appender-ref ref="console" />
</root>
</log4j:configuration>

Now that this part is done, how can this all help us. So far everyone is using cflog to log and this is working fine except when you want changes when your application is running. So if you look at the post about creating EAR files for deployment this would mean that you will not be able to access your code after it is written. So mostly for debugging it is a problem if you can't find the location where something is going wrong. Now if you would place a debug log message in every method you create and any kind of log message in your application where you need it you would be able to find problems quite fast.

Since you can have an external configuration file now outside your EAR file that you can change you can change the level of logging dependent on what environment you are running on. On a test environment you can log debug and on a production just error and fatal, and you can determine how you want your messages, like by email or in a log file or in a database, this is not in my example but do a little search and you'll be able to find the information.

Now the most important par, you can refresh your configuration with the reInit method , so if there is a problem on your production environment you can set your log level to info or debug and reInit the log configuration, this could give you enough information to work with and fix the problem, just put it all back after your changes and everything will be working fine again.

Of course logging does cost you performance, so less logging gives you better performance, that is why you only want to log error and fatal in a production environment, and not everything. If you are not logging debug messages it will not cost you any performance if the call is in your code, since log4j doesn't use any time for logging those messages and they will just be ignored, so this will be fast and not cause problems.

One last thing on this, don't use email when there are a lot of messages to be mailed because email is quite slow to use, so only use it in some fatal cases if you need to. I believe this is about it, please use this CFC if you can and let me know how everything is working out.

Related Blog Entries

Comments
BlogCFC was created by Raymond Camden. This blog is running version 5.9.002. Contact Blog Owner