2004-12-31
Revision History | ||
---|---|---|
Revision 1.2.4 | 2004-12-28 | kwa |
Initial DocBook conversion. |
Abstract
log4sh is a logging framework for shell scripts that works similar to the other wonderful logging products available from the Apache Software Foundataion (eg. log4j, log4perl). Although not as powerful as the others, it can make the task of adding advanced logging to shell scripts easier. It has much more power than just using simple "echo" commands throughout. In addition, it can be configured from a properties file so that scripts in a production environment do not need to be altered to change the amount of logging they produce.
Table of Contents
Log4sh has been developed under the Bourne Again Shell (bash) on Linux, but great care has been taken to make sure it works under the default Bourne Shell of Solaris (sh) as this happens to be the primary platform used by myself.
Tested Operating Systems
Linux
Solaris 8, 9, 10
Tested Shells
Bourne Shell (sh)
Bourne Again Shell (bash)
In this document, I have the pleasure of acknowledging:
Nobody in particular
Feedback is most certainly welcome for this document. Send your additions, comments and criticisms to the following email address: <kate.ward@forestent.com>
.
To get started quickly with log4sh, take a look at the test-log4sh
sample script in the src/test
directory of the distribution. You will need to copy the log4sh
script itself from the src/shell
directory into the test
directory before running the test. This test script is designed to configure log4sh via code. Later on, we will configure log4sh with a properties file, very similar to how log4j is configured.
By default, log4sh is configured with a ConsoleAppender
which logs to STDOUT using a SimpleLayout
and a logging level of ERROR
. If no configuration file is found, a warning message will be given letting the user know that no configuration file was found. This warning can be supressed by setting the LOG4SH_CONFIGURATION
environment variable to none
.
Run the first test.
After a first run you will see quite a bit of output to the display, and as well two log files will be created (log4sh-pattern.log
and log4sh-simple.log
). The display contains a mix of output to STDOUT and STDERR, and the files contain various versions of the same data, but with output that changes as various layouts and patterns are tested. To clean up the display a bit, you could send all of the output of the STDERR appender off to /dev/null
.
Run the test again, but redirecting STDERR to /dev/null
.
Go ahead a take a look at the test script to get a feel of what the script is trying to accomplish. Hopefully, you can see just how simple log4sh is to use and control.
For our next test, we will configure log4sh with a properties file. This test is incredibly simple as it is designed to show the power of the properties file. Copy first example properties file log4sh.properties.ex1
from the src/examples
directory into the test
directory, and give it the name log4sh.properties
.
Run the second test.
This test is designed to look very much like one of the log4j examples. The properties file is taken almost verbatum from the log4j short manual, with only small changes required to make it work for log4sh. Log4sh is configured with a ConsoleAppender
that has a PatternLayout
. One limitation of log4sh is that it does not have access to a timer with millisecond accuracy, so it logs only with an accuracy of one second. For most situations, this should be sufficient.
Copy the second example properties file (log4sh.properties.ex2
) as log4sh.properties
, and rerun the test. This second properties file is only slightly different from the first in that it adds the filename to the output, and removes the %x
pattern directive as that directive is not supported by log4sh.
The next example shows a typical situation where an administrator wants to run a script via a cron job, wants a log of the scripts actions, but only wants an email if the job failed.
Copy the third example properies file (log4sh.properties.ex3
) over, and rerun the test. This time, the output will be sent to two separate locations; STDERR and a file called example.log
. The output to STDERR is set to the ERROR
level, and output sent to the file is at the INFO
level. More output is written to the logfile (as expected) when the test is run.
In the situation of being run from a cron job, the logfile will always be written, but an email comes only when output from the script came at the ERROR
or FATAL
level. An administrator can even configure the log4sh.properties
file to a DEBUG
level so that more output is logged for testing or debugging purposes, and this change can happen without making any code changes to the script. Something very useful in a production environment!
The usage of log4sh is simple. There are only a few simple steps required to setup and use log4sh in your application.
preconfigure log4sh (properties file)
source the log4sh script code
configure log4sh in code (optional)
call logging statements
To preconfigure log4sh, create a properties file (see the Properties File Configuration later in this document). Optionally set the LOG4SH_CONFIGURATION
environment variable to point log4sh to the configuration file.
To source the code into your script (also known as including), one uses the sourcing ability of shell to source one script into another. See the following quick example for how easy this is done.
Example 4. Sourcing external shell code into current program
#! /bin/sh # source log4sh from current directory . ./log4sh
Here is some sample code that looks for log4sh in the same directory as the script is located, as well as the current directory. If log4sh could not be found, it exits with an error. If log4sh is found, it is loaded, along with the log4sh.properties
file in the current directory (see the following example). It then logs a message at the INFO
level to STDOUT.
Example 5. Hello, world (using properties)
#! /bin/sh # # log4sh example: Hello, world # myDir=`dirname $0` # find and source log4sh if [ -r "$myDir/log4sh" ]; then dir=$myDir elif [ -r "./log4sh" ]; then dir=. else echo "fatal: could not find log4sh" >&2 exit 1 fi . $dir/log4sh # say Hello to the world logger_info "Hello, world"
Here is the log4sh.properties
file for the previous example. Save it in the same directory you are running the above script from.
Example 6. Hello, world; properties file
# # log4sh example: Hello, world properties file # # Set root logger level to DEBUG and its only appender to A1 log4sh.rootLogger=INFO, A1 # A1 is set to be a ConsoleAppender. log4sh.appender.A1=ConsoleAppender # A1 uses a PatternLayout. log4sh.appender.A1.layout=PatternLayout log4sh.appender.A1.layout.ConversionPattern=%r [%t] %p %c %x - %m%n
To configure log4sh in code, simply call the appropriate functions in your code. The following code sample loads log4sh from the current directory, configures it for STDERR output, and the logs a message at the INFO
level.
Example 7. Hello, world (configured in code)
#! /bin/sh # # log4sh example: Hello, world # # source log4sh (disabling properties file warning) LOG4SH_CONFIGURATION="none" . ./log4sh # configure log4sh defaults logger_setLevel INFO # configure appenders appender_setAppend stdout false logger_addAppender stderr appender_setAppenderType stderr FileAppender appender_setAppenderFile stderr STDERR # say Hello to the world logger_info "Hello, world"
Log4sh can be configured with a properties file that is separate from the actual script where the logging takes place. By default, log4sh looks for its properties file called log4sh.properties
in the current directory. If the file is located elsewhere or with a different name, log4sh can be configured by setting the LOG4SH_CONFIGURATION
environment variable (eg. LOG4SH_CONFIGURATION="/etc/log4sh.conf"
).
A log4sh.properties
file that is completly empty is sufficient to configure log4sh. There will be absolutely no output however (which might just be what is desired). Usually though, some output is desired, so there is at least a recommended minimum configuration file. An explaination of the file follows the example.
Example 8. Recommended minimum log4sh.properties file
log4sh.rootLogger=INFO, stdout log4sh.appender.stdout=ConsoleAppender
In the first line, the root logger is configured by setting the default logging level, and defining the name of an appender. In the second line, the stdout appender is defined as a ConsoleAppender
.
Table 1. Logging Levels (from most output to least)
Level | Definition |
---|---|
ALL | The ALL level has the lowest possible rank and is intended to turn on all logging. |
DEBUG | The DEBUG level designates fine-grained informational events that are most useful to debug an application. |
INFO | The INFO level designates informational messages that highlight the progress of the application at coarse-grained level. |
WARN | The WARN level designates potentially harmful situations. |
ERROR | The ERROR level designates error events that might still allow the application to continue running. |
FATAL | The FATAL level designates very severe error events that will presumably lead the application to abort. |
OFF | The OFF level has the highest possible rank and is intended to turn off logging. |
An appender name can be any alpha-numeric string containing no spaces.
An appender can be set to one of several different types.
An appender can take several different options.
Table 3. Appender Options
Option | Definition | Supported? |
---|---|---|
DatePattern | configure a pattern for the output filename | no (ignored) |
File | output filename (special filename of STDERR used for logging to STDERR) | yes |
MaxBackupIndex | number of old logfiles to keep | no (ignored) |
MaxFileSize | maximum size of old logfiles | no (ignored) |
Threshold | logging level of the appender | yes |
An appender can be configured with various Layouts to customize how the output looks.
Table 4. Layouts
Layout | Definition | Supported? |
---|---|---|
HTMLLayout | layout using HTML | no (same as SimpleLayout) |
SimpleLayout | a simple default layout ('%p - %m') | yes |
PatternLayout | a patterned layout (default: '%d %p - %m%n') | yes |
An layout has many different options to configure how it appears. These are known as patterns.
Example 13. Setting an appender's layout pattern
log4sh.appender.A1.layout.ConversionPattern=%d [%p] %c - %m%n
Table 5. Pattern Options
Option | Definition | Supported? |
---|---|---|
%c | current class (not applicable in shell; always returns 'shell') | yes |
%d | current date (see *NIX date man page; '%Y-%m-%d %H:%M:%S') | yes |
%F | current script filename (default: `basename $0`) | yes |
%L | current line number in script | no (ignored) |
%m | logging message | yes |
%n | OS specific line ending | no (ignored) |
%p | logging level (aka priority) | yes |
%r | number of seconds since script was started | partial (bash) |
%t | current executing thread (default: 'main'; code changable) | yes |
%x | unknown | no (ignored) |
Table 6. Configuring the root logger
Function | Definition | Example |
---|---|---|
logger_addAppender | add an appender | logger_addAppender stdout |
logger_addAppenderWithPattern | shortcut for adding an appender with a specific pattern layout | logger_addAppenderWithLayout myPattern '%d [%p] %m' |
logger_setFilename | set the script filename for the %F pattern option | logger_setFilename "myFilename" |
logger_getLevel | get the current root logger priority level | level=`logger_getLevel` |
logger_setLevel | set the root logger priority level | logger_setLevel INFO |
logger_setThreadName | set the current thread name for the %t pattern option | logger_setThreadName "myThread" |
Table 7. Configuring appenders
Function | Definition | Example |
---|---|---|
appender_close | close an appender | appender_close myAppender |
appender_setAppenderFile | set the output filename for the named appender | appender_setAppenderFile myAppender "output.log" |
appender_setAppenderType | set the type of appender for the named appender | appender_setAppenderType myAppender FileAppender |
appender_getLayout | get an appender's layout type | layout=`appender_getLayout myAppender` |
appender_setLayout | set the layout type for the named appender | appender_setLayout myAppender PatternLayout |
appender_getLevel | get an appender's logging priority | level=`appender_getLevel myAppender` |
appender_setLevel | set the logging priority for the named appender | appender_setLevel myAppender DEBUG |
appender_setPattern | set the output pattern for named appender (appender must be set to a PatternLayout) | appender_setPattern myAppender '%d %p - %m%n' |
Table 8. Logging statements
Function | Definition | Example |
---|---|---|
log | log a message at a specific level | log DEBUG "Hello, world" |
logger_debug | log a message at the DEBUG level | logger_debug "This is a test" |
logger_info | log a message at the INFO level | logger_info "Did you get it?" |
logger_error | log a message at the ERROR level | logger_error "I sure hope you did" |
logger_warn | log a message at the WARN level | logger_warn "If not, you might be in trouble" |
logger_fatal | log a message at the FATAL level | logger_fatal "The end of the world is here!!!" |
Log4sh has been completly developed from scratch by myself, Kate Ward. I use it in production environments where logging from shell scripts is critical, and where I need more than just a simple "Hello, I worked" type of logging message. If you like what you see, or have any suggestions on improvements, please feel free to drop me an email at <kate.ward@forestent.com>
. If there is enough interest in the project, I will develop it further.
Log4sh is licensed under the GNU Lesser Public License. The contents and copyright of this site and all provided source code are owned by Kate Ward.