Simple Log was written out of frustration with other "logging frameworks". Many of its features are designed to contrast with the way in which these frameworks are used. Because of this, it seemed sensible to show a comparison between Simple Log and one of these frameworks so that you can see the difference before committing to using Simple Log in your own code. I have chosen Log4J for the comparison, as this is undoubtedly the most widely used logging framework.
Not all of the comparisons will show Simple Log having an advantage. Some of them are just there to say "Simple Log can do the same" or "here's how you'd do that in Simple Log". I intend this to be a fair comparison, so if you see something that's not fair or is wrong, please let me know.
Don't Make Me Think !
One of the driving principles of Simple Log is "Don't Make Me Think!". You should be able to use all but the most advanced features of your logger without having to think about what you're doing.
In the examples below, if I consider the work necessary to use a feature in one of the logging packages requires more thinking than should be necessary, I have flagged it using the "Don't Make Me Think" banner shown on the right. There aren't many of these, but I think they flag some of the most important differences.
private static final SimpleLogger log = new SimpleLogger(Test.class);
private static final Logger log = Logger.getLogger(Test.class);
Given no configuration file, Simple Log produces no output. This is a feature that allows you to easily have zero output in deployment if desired.
log4j:WARN No appenders could be found for logger (test.Test).
log4j:WARN Please initialize the log4j system properly.
Given an empty configuration file, Simple Log writes all messages logged at the Fatal, Error, Warn and Info levels to System.err.
With Log4J, the minimum configuration required to produce output (ignoring the BasicConfigurator) is:
log4j.rootLogger=INFO,Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.SimpleLayout
Don't Make Me Think !
However, even that only gets you the most basic of output. To generate the same format of output that Simple Log gives by default, you'd need to switch to a PatternLayout and then author something like this as well:
log4j.appender.Console.layout.ConversionPattern=%d{EEE yyyy/MM/dd HH:mm:ss.SSS}| |%t|%c|%m%n
The default output of a Simple Log text log message is shown below.
Thu 2006/02/09 08:38:11.269| |main|Test|Plain message
The output shows:
Each field of the output is separated by a pipe, making it easy to parse the line (if necessary) using a simple string parsing method like split().
All this information was produced with only an empty properties file and one line of code:
log.info("Plain message");
Log4J has no default output, except error messages saying that it hasn't been configured.
The easiest way to get output from Log4J is to install a BasicConfigurator, which gives the following output:
0 [main] INFO test.Test - Plain message
The output shows:
Log4J's default output uses three different types of separators; there are spaces between most fields, brackets around the thread name and an extra hyphen and space before the log message. This output would require a regular expression or some custom code in order to be parsed into its components.
To get this information, I needed to configure Log4J from within my code:
BasicConfigurator.configure();
log.info("Plain message");
Simple Log maintains a concept of having different types of log messages. There are plain text messages, object values, exceptions and tracing (entry and exit). Each of these five message types has its own format, making it simple to have different message types being easily distinguished from each other in your log file.
Shown below is an example of one of each type of message: an entry, a plain message, an object value, an exception and an exit.
Thu 2006/02/09 08:38:11.269|>>>|main|Test|main()
Thu 2006/02/09 08:38:11.269| |main|Test|Plain message
Thu 2006/02/09 08:38:11.269|---|main|Test|object|java.lang.Object@bf2d5e
Thu 2006/02/09 08:38:11.269|***|main|Test|java.lang.Exception: Test Exception
java.lang.Exception: Test Exception
at test.Test.main(Test.java:52)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:90)
Thu 2006/02/09 08:38:11.279|<<<|main|Test|main()
Below is the code used to produce the above output.
log.entry("main()");
log.info("Plain message");
log.infoObject("object", new Object());
log.errorException(new Exception("Test Exception"));
log.exit("main()");
Log4J has no concept of different log message types. All messages, regardless of whether they are text, an object, an exception or tracing, are seen as just a message, and get rendered using a single Layout (per Appender) with a single pattern.
The output produced by Simple Log could probably be achieved with Log4J by having five Loggers for every category (i.e. five Loggers per class), one for each type of message, though this would undoubtedly not be worth the effort.
log.info("Message");
log.info("Message");
Simple Log provides methods for debugging objects that will check the log level before performing any concatenation. As long as the object or value you are passing is not produced using concatenation, this means you don't need to check the level yourself before logging your object.
Note that, while the below example shows the use of the method debugObject(), there are corresponding convenience methods for all DebugLevels at which you might normally debug an object, e.g. infoObject(), as well as the generic dbo() methods that take any level.
log.debugObject("new Object()", new Object());
Log4J doesn't provide any methods specifically designed for debugging objects. Most people use something like the following code.
if (log.isDebugEnabled())
log.debug("new Object(): " + new Object());
Don't Make Me Think !
Note that, because you have to perform a concatenation in order to log both the object's name and value, best practice is to check whether the message will actually be logged before performing the concatenation.
I should note that it is actually possible to do something like what Simple Log does with objects by using Log4J's l7dlog() method:
log.l7dlog(Level.DEBUG, "newObject", new Object[] {new Object()}, null);
However, to make this happen you have to do two other undesirable things:
Because of the latter point, you probably would still want to do the pre-logging level-check anyway, which totally defeats the point of trying to use this method.
In case you're wondering, it is possible to check whether a message will be logged before logging it using Simple Log.
if (log.wouldLog(DebugLevel.L5_DEBUG))
...
And I can't think why you'd ever want to use this one, but it's there just in case:
if (log.isTracing())
...
if (log.isDebugEnabled())
...
Basically the same story as above; Simple Log lets you log any primitive just by passing it in. There are convenience methods for int and boolean at the most frequently-used levels (e.g. debugObject(String, int), verboseObject(String, boolean)). For other primitive types or for other levels, you can pass any DebugLevel and primitive to a dbo() method.
List kittens = ...;
long id = ...;
log.debugObject("kittens", kittens.size());
log.dbo(DebugLevel.L5_DEBUG, "id", id);
Don't Make Me Think !
As with objects, Log4J requires you to concatenate primitives yourself, after the all-important level-check:
List kittens = ...;
long id = ...;
if (log.isDebugEnabled()) {
log.debug("kittens: " + kittens.size());
log.debug("id: " + id);
}
Simple Log applies special formatting to object arrays, byte arrays and char arrays to ensure you always get useful output without having to think about it.
log.debugObject("strings", new String[] {"One", "Two", "Three"});
log.debugObject("chars",
new char[] {'c', 'h', 'a', 'r', 'a', 'c', 't', 'e', 'r', 's'});
byte[] bytes = new byte[10];
new Random().nextBytes(bytes);
log.debugObject("bytes", bytes);
The above code produces the following output:
Fri 2006/02/10 07:51:41.250|---|main|Test|strings|[One, Two, Three]
Fri 2006/02/10 07:51:41.250|---|main|Test|chars|characters
Fri 2006/02/10 07:51:41.250|---|main|Test|bytes|0x[06, 9A, 2D, FC, BA, A0, F1, 63, B9, E0]
Because Log4J has no special allowances for printing objects, you either get the hashcode of your array as the output, or you have to do the work yourself to get it's contents.
Printing the array normally will get you its hashcode:
if (log.isDebugEnabled())
log.debug("strings: " + new String[] {"One", "Two", "Three"});
Fri 2006/02/10 08:01:15.256| |main|test.Test|strings: [Ljava.lang.String;@df8ff1
Don't Make Me Think !
Or you can do the work yourself to get the same output for object arrays as Simple Log gives by
default:
// If you can guarantee Java 5 you could do it like this:
if (log.isDebugEnabled())
log.debug("strings: " + Arrays.toString(new String[] {"One", "Two", "Three"}));
// Otherwise you have to do this:
String[] strings = ...;
if (log.isDebugEnabled()) {
StringBuffer buffer = new StringBuffer("[");
for (int i = 0; i < strings.length; i++) {
if (i != 0)
buffer.append(", ");
buffer.append(strings[i]);
}
buffer.append("]");
log.debug("strings: " + buffer);
}
As Arrays.toString() has no special formatting for bytes, to get the same hexadecimal output as Simple Log you would have to write your own code to produce it. For character arrays you can use String.valueOf().
Simple Log has specific methods for logging exceptions. There are convenience methods for the Fatal, Error and Warn levels, and the dbe() method for less-frequently used levels.
log.errorException(new Throwable("Pretty Bad Exception"));
log.warnException(new Throwable("Recoverable Exception"));
log.debug("Caught Expected Exception");
log.dbe(DebugLevel.L6_VERBOSE, new Throwable("Expected Exception"));
Log4J also has specific method signatures for logging exceptions. With Log4J, you are encouraged to log your message at the same time as your exception, meaning that your exception will always be printed at the same level as your message.
log.debug("Caught Expected Exception", new Throwable("Expected Exception"));
In Simple Log, the level for a particular logger is set by creating a property with the name of the logger and a value of either of the desired DebugLevel's name or numeric value. The level names are case-insensitive.
org.grlea.application.Main = Error
org.grlea.application.Processor = 5
As in most logging packages, the levels specified are inherited based on the package name hierarchy. So, for example, you can set the level for all classes in the org.glea.application package and its subpackages using the following:
org.grlea.application = Info
Loggers for which there is no level set - either for the class of the logger or for any of the packages in which the class resides - will have their level set to the default level, which is defined and configured by the property simplelog.defaultLevel.
simplelog.defaultLevel = Warn
Note that, while the levels for loggers in Simple Log are calculated using a hierarchical namespace, there is not actually any logger hierarchy maintained within the code.
In Log4J, the level for a particular logger is set by creating a property that is the name of the logger prefixed with 'log4j.logger.'. The value must be the name of the desired Level. The level names are case-insensitive.
log4j.rootLogger=Info,Console
log4j.logger.org.grlea.application.Main = Error
log4j.logger.org.grlea.application.Processor = Debug
In my experience, people are using XML to configure Log4J more often than a properties file. The equivalent XML is of course more verbose than simple properties, and looks like this:
<category name="org.grlea.application.Main">
<priority value="Error" />
</category>
<category name="org.grlea.application.Processor">
<priority value="Debug" />
</category>
<root>
<priority value="Info" />
<appender-ref ref="Console" />
</root>
Using this format will mean that, every time you need to specify a level for a class or package that isn't currently specified, you need to copy three or four lines (depending on whether you like whitespace) and then change information on two of those lines. The values you need to change are also not at the start or the end of any lines, but kind of towards the end of them. The amount of boilerplate text in this style of configuration also makes it much harder to scan with the naked eye, as the important data is obscured by repetitious tags.
I know I'm being picky and anal here, but in the past when I've been debugging code and needing to change the log levels frequently, I've experienced that reading, scanning and editing this file is really annoying. Being able to just type (or copy/paste) the FQ name of a class followed by a level is much easier.
As shown above, the default level and output semantics of Log4J are set by configuring the "Root Logger", an object which sits at the top of Log4J's internally-maintained hierarchy of Logger objects.
Simple Log provides seven distinct DebugLevels. The first five levels are common to many logging packages. The last two are intended to allow more fine-grained control of debugging output.
Debugging is what most developers are using logging packages for, so having three grades of debugging levels makes a lot of sense. Some packages, Log4J included, are distributed with only one debugging level, which means that debugging for any particular class must be either on or off. If you have a class that is doing a lot of work, e.g. an implementation of a SAX DefaultHandler, then it very useful to be able to turn on only the most important debug information first. Then, if more detail is required to solve the problem, you can get that detail by setting the level up to Verbose or Ludicrous. The alternative is to always receive reems of output and to waste time trawling through mounds of useless detail messages that may come in use one day but most of the time will just get in your way.
The Simple Log API Documentation is very specific about the intended use of each level. It gives examples of what kinds of messages and objects should be logged at each of the levels. This removes the need for team meetings on the topic "What should we be logging at each level".
Tracing, that is, printing information about the entering and exiting of methods, is configured indepently of debug levels.
Simple Log's DebugLevel class is not extendable. No one has ever complained about this. Seven appears to be the holy number.
Log4J provides five Levels intended to be used for logging.
It also has two levels intended for use only in configuration.
As discussed above, there are a number of disadvantages associated with only having one level for debug statements.
Unlike Simple Log's DebugLevel, Log4J's Level class can be extended. Many developers use this fact to rectify Log4J's lack of debugging granularity by creating their own level below Debug, very often called "Trace". I have seen this cause a number of other issues, including more repetetive text in the configuration file (and silent errors when this repetetive text is omitted) and difficulty in upgrading Log4J versions due to the need to have your own build of Log4J.
The core package of Simple Log contains three classes, of which most people will only ever need to know about two - SimpleLogger and perhaps DebugLevel.
In total, Simple Log version 2 will be released with about 9 public classes, of which:
The core package of Log4J contains 23 classes. Of these, you will need to use or at least understand the concept of the following before getting Log4J to do anything useful:
In total, the Log4J 1.2.9 API includes 123 public classes and interfaces.
Simple Log has explicit support for tracing, which means logging messages when the thread of control enters and exits a method, or any arbitrary section you might be interested in. Tracing is performed using the entry() and exit() methods, demostrated below and followed by the default output.
log.entry("main()");
log.exit("main()");
Thu 2006/02/09 08:38:11.269|>>>|main|Test|main()
Thu 2006/02/09 08:38:11.279|<<<|main|Test|main()
As Log4J has no concept of different types of log messages, it has no concept of a tracing message. It doesn't have a level specifically for tracing, and as a result many people end up creating their own custom level just for this purpose.
People who want to do tracing in Log4J usually do it something like the following.
log.debug("IN: main()");
log.debug("OUT: main()");
In Simple Log, tracing is configured independently of the debug levels. To enable or disable tracing for any class or package you create a property with the name of the logger followed by #trace (a hash followed by the word 'trace'), and with a value of either true or false.
org.grlea.application#trace = false
org.grlea.application.Main#trace = true
org.grlea.application.processing#trace = true
org.grlea.application.processing.XmlHandler#trace = false
As with debug levels, tracing is inherited through the package name hierarchy. So, using the above example, tracing would be active for the Main class and for all classes in and under the processing package, except for the XmlHandler class.
The default for tracing is configured by setting the simplelog.defaultTrace property.
simplelog.defaultTrace = true
Seeing as Log4J has no dedicated tracing capability, you can't configure your trace statements separately. The impact of this is that, if you have tracing-like debug logs in a class, you have to get all the debug output for that class to see the tracing.
As noted, some people create their own custom Level so that they can have tracing at a different level to their debug logs. While doing so will allow you to see your debug messages without seeing your tracing (assuming you make your Trace level lower than your Debug level), the opposite will not be true, so to see your tracing you will also have to see all your debug statements.
To log to a file using Simple Log, simply specify the name of the file with the simplelog.logFile property.
simplelog.logFile = application.log
The log file name can be either absolute or relative. If relative, it will be interpreted as being relative to the working directory of the application.
To log to a file using Log4J, you need to properties for:
In the simplest scenario, you'd have something like this:
log4j.rootLogger=info,File
log4j.appender.File=org.apache.log4j.FileAppender
log4j.appender.File.File=application.log
log4j.appender.File.layout=org.apache.log4j.SimpleLayout
By default, Simple Log will always append to its log file. To change this, set the simplelog.logFile.append property to false.
simplelog.logFile.append = false
Log4J's FileAppender also appends to its file by default, but can be prevented from doing so by setting the 'append' property of the appender to false.
log4j.appender.File.append = false
Simple Log can be made to log to both a file and to the console by setting the value of the simplelog.logFile.andConsole property.
simplelog.logFile = application.log
simplelog.logFile.andConsole = true
To log to both a file and the console using Log4J, you have to define two Appenders and add both of them to your Logger hierarchy.
log4j.rootLogger=info,File,Console
log4j.appender.File=org.apache.log4j.FileAppender
log4j.appender.File.File=application.log
log4j.appender.File.layout=org.apache.log4j.SimpleLayout
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.SimpleLayout
To place the date of a log file's creation in the file's name using Simple Log, you need to place {0} in the log file where you want the date to appear. If you want to specify the format of the date, simply use the standard formatting parameters specified by java.text.MessageFormat.
simplelog.logFile = application-{0,date,yyyy_MM_dd}.log
So far as I can determine from the documentation, there is no way to include the date in the name of Log4J's main log file, but only in its rolled over logs.
To enable log rolling in Simple Log, you need to:
simplelog.logFile=application.log
simplelog.rollover=timeOfDay
By default, rolled log files are placed in the same directory as the main log file, and are given the same name as the main log file, prefixed with a unique, incrementing number and a hyphen (e.g. 00-application.log). The directory for rolled log files can be configured by setting the simplelog.rollover.directory property. The name of rolled log files can be configured by setting the simplelog.rollover.filename, which can include the incrementing number and/or the date the rollover file is created.
Simple Log comes bundled with two strategies: File Size and Time of Day. By default, the File Size strategy rolls the log every 100 megabytes and the Time of Day strategy rolls at midnight based on the timezone of the local machine. The strategies can be configured using the properties simplelog.rollover.fileSize.size, simplelog.rollover.timeOfDay.time and simplelog.rollover.timeOfDay.timezone.
simplelog.logFile=application.log
simplelog.rollover=timeOfDay
simplelog.rollover.timeOfDay.time=02:00
simplelog.rollover.timeOfDay.timezone=GMT+10:00
You can also easily write your own implementation of RolloverStrategy which will have access to all the Simple Log properties.
To enable log rolling in Log4J, you need to:
log4j.rootLogger=info,File
log4j.appender.File=org.apache.log4j.RollingFileAppender
log4j.appender.File.File=application.log
log4j.appender.File.layout=org.apache.log4j.SimpleLayout
Rolled log files are placed in the same directory as the main log file. They are given the same name as the main log file, with an increasing number appended. The directory for rolled log files cannot be configured. The name of rolled log files cannot be configured.
Log4J's RollingFileAppender only maintains a specified number of rolled log files, defaulting to one. This can be configured by setting the log4j.appender.File.MaxBackupIndex property. By default, the Rolling File Appender rolls the log every 10 megabytes. This can be configured by setting the property MaxFileSize property of the appender.
log4j.rootLogger=info,File
log4j.appender.File=org.apache.log4j.RollingFileAppender
log4j.appender.File.File=application.log
log4j.appender.File.layout=org.apache.log4j.SimpleLayout
log4j.appender.File.MaxFileSize=100MB
log4j.appender.File.MaxBackupIndex=9
Log4J also comes with a DailyRollingFileAppender, which uses a date pattern that enables rolling on a monthly, weekly, daily, half-daily, hourly, or minutely schedule.
log4j.rootLogger=info,File
log4j.appender.File=org.apache.log4j.DailyRollingFileAppender
log4j.appender.File.File=application.log
log4j.appender.File.layout=org.apache.log4j.SimpleLayout
log4j.appender.File.DatePattern='.'yyyy-MM-dd
Simple Log has no mechanism for displaying the file, method or line number from which a message was logged. Note that, in order to do this, it is necessary to create a new Throwable every time a message is printed.
Log4J's PatternLayout can output this information using the %F, %M and %L conversion characters.
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d{EEE yyyy/MM/dd HH:mm:ss.SSS}| |%t|%c|%M|%F:%L|%m%n
Note that the Log4J documentation warns against using this feature.
WARNING Generating caller location information is extremely slow. It's use should be avoided unless execution speed is not an issue
Simple Log can periodically check the modified date of the configuration file in use and reconfigure itself if the file changes. To enable this, you set the simplelog.reloading property to true.
simplelog.reloading = true
The free Log4J documentation doesn't contain anything about reloading your log configuration at runtime.
The org.apache.log4j.varia package contains a class called ReloadingPropertyConfigurator, though the class is entirely undocumented, so I've no idea how it works or how you'd install it. There is no ReloadingDOMConfigurator.
The format of Simple Log's output can be customised by setting the value of the simplelog.format.* properties. There are two properties for each type of log message (debug, debugObject, debugException, entry and exit) - one for each of the instance and non-instance log message types.
The syntax of the output properties are specified by java.text.MessageFormat.
The arguments passed into the MessageFormat are documented in the configuration file, which is what you'll already be looking at when you need them.
simplelog.format.debug = {0} [{1}] {4} {2} - {5}
The format of Log4J's output is customised by specifying a PatternLayout on your Appender and specifying the ConversionPattern for this layout.
The syntax of the output is "closely related to the conversion pattern of the printf function in C" and is documented in the API documentation of the PatternLayout class.
The "conversion specifiers" available to the PatternLayout are also documented in the API documentation of the PatternLayout class.
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %p %c - %m%n
With Simple Log, the decision to ouput the fully-qualified name of a class is specified, in code, on a per-class basis. (And defaults to 'false'.)
private static final SimpleLogger log = new SimpleLogger(Test.class, true);
It is implemented like this based on the assumption that most class names are relatively unique and that, should you ever need to see the fully-qualified name of a class, you will only want to see it for those classes whose non-qualified names are ambiguous.
In Log4J, the decision about how much of the qualified class name to output is specified in the Conversion Pattern.
log4j.appender.Console.layout.ConversionPattern=%d{EEE yyyy/MM/dd HH:mm:ss.SSS}| |%t|%C|%m%n
This means that every message output by that Appender will show the same amount of detail about the class name. You can limit the information based on either a maximum character length or a number of package names. However, whenever you want to see information about the package of one class that's logging, you will have to output the same amount of information for all classes.
Simple Log supports a concept of "instance logging", where a SimpleLogger is created for each instance of a class and messages output from that instance include the (specified) ID of the object.
public class
TreeNode
{
private final SimpleLogger log;
public
TreeNode(String name)
{
log = new SimpleLogger(TreeNode.class, name);
}
}
As well as printing the instance ID in the log, Simple Log allows you to configure the log level and trace flag based on the instance ID.
TreeNode = Debug
TreeNode.frequentlyOccurringLeaf = Info
While Log4J doesn't have the concept of instance logging, it is possible to achieve a similar functionality by concatenating the object's ID onto the class name.
public class
TreeNode
{
private final Logger log;
public
TreeNode(String name)
{
log = Logger.getLogger(TreeNode.class.getName() + "." + name);
}
}
Log4J will then allow you to configure the level for these "instance" loggers in the same way as any other category.
log4j.logger.TreeNode = Debug
log4j.logger.TreeNode.frequentlyOccurringLeaf = Info
The shortfall here with Log4J is that, because it doesn't know about the "instance ID", and just sees it as one more level in the logger name hierarchy, the unqualified "class name" of statements printed by this logger will be just the instance ID, without the class name. To see the class name as well, you would need to print more than one level of the logger name. As discussed above under 'Outputting Fully-Qualified Class Names', configuring your pattern layout like this in Log4J will mean that you will see the class logger name information for all your loggers, not just the ones with instance IDs.
Simple Log does not have any explicit capability for print its output in fixed-width fields. It is easy enough to define a fixed-width date format (as the default format is). The default output will has a fixed width up until the class name for all messages from the same thread, and up until the message contents for all messages from the same class in the same thread.
Thu 2006/02/09 08:38:11.269|>>>|main|Test|main()
Thu 2006/02/09 08:38:11.269| |main|Test|About to print arguments
Thu 2006/02/09 08:38:11.269|---|main|Test|argv|[test, arguments]
Thu 2006/02/09 08:38:11.269|>>>|Event Dispatch Thread|ApplicationMainWindow|main()
Thu 2006/02/09 08:38:11.269|<<<|Event Dispatch Thread|ApplicationMainWindow|main()
Thu 2006/02/09 08:38:11.269|<<<|main|Test|main()
Log4J's PatternLayout allows you to specify fixed-width conversion specifiers for each field, allowing you to define a precisely fixed-width log mesage, provided you are happy to lose a bit of detail if the value being printed is larger than the specified widt of the field.
log4j.appender.Console.layout.ConversionPattern=%d{EEE yyyy/MM/dd HH:mm:ss.SSS}|%-20.20t|%-20.20C|%m%n
Thu 2006/02/09 08:38:11.269|main |Test |IN: main()
Thu 2006/02/09 08:38:11.269|main |Test |About to print arguments
Thu 2006/02/09 08:38:11.269|main |Test |argv: [test, arguments]
Thu 2006/02/09 08:38:11.269|Event Dispatch Threa|ApplicationMainWindo|IN: ApplicationMainWindow()
Thu 2006/02/09 08:38:11.269|Event Dispatch Threa|ApplicationMainWindo|OUT: ApplicationMainWindow()
Thu 2006/02/09 08:38:11.269|main |Test |OUT: main()
The Simple Log release includes properties files containing all the properties you will ever need to configure Simple Log, except, of course, those or configuring the levels for your classes. The properties files also contain full documentation of each of the properties.
The Log4J release includes a number of example properties files that show how to set up Log4J for a number of different common logging requirements. It does not include a "complete" properties file or set of files that give all the properties that can be used for configuration. The documentation for many of the configuration options for Log4J are contained within the API documentation.
When you log a message using Simple Log (e.g. to SimpleLogger.debug(String)), and that message is going to be output, the following actions take place:
When you log a message using Log4J (e.g. to Category.debug(String)), and that message is going to be output, the following actions take place:
Copyright (c) 2006-2015 Graham Lea. All rights reserved.