2006-09-16
Revision History | ||
---|---|---|
Revision 1.3.6 | 2006-09-16 | kwd |
Revision 1.3.5 | 2006-08-19 | kwd |
Revision 1.3.4 | 2006-03-25 | kwd |
Revision 1.3.3 | 2006-02-11 | kwd |
Revision 1.3.2 | 2006-01-18 | kwd |
Revision 1.3.0 | 2005-08-15 | kwd |
Revision 1.2.6 | 2005-02-01 | kwd |
Revision 1.2.4 | 2004-12-28 | kwd |
Abstract
log4sh is a logging framework for shell scripts that works similar to the other wonderful logging products available from the Apache Software Foundation (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
List of Tables
List of Examples
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
Cygwin
FreeBSD
Linux
Solaris 8, 9, 10
Tested Shells
Bourne Shell (sh)
Bourne Again Shell (bash)
Korn Shell (ksh, pdksh)
A list of contributors to log4sh can be found in the source archive as doc/contributors.txt
. I want to personally thank all those who have contributed to make this a better tool.
Feedback is most certainly welcome for this document. Send your additions, comments and criticisms to the following email address: <kate.ward@forestent.com>
.
First things first. Go to the directory from which you extracted the log4sh software. In there, you should find a Makefile
. If you find one, you are in the right place. We need to setup the environment for running tests, so from this directory, execute the make test-prep command as shown below. Once this is done, a test
directory will be created and prepared with everything needed to run the log4sh tests.
Prepare your environment.
$ make test-prep $ cd test
Example 2.1. Hello, World!
Ok. What kind of a quickstart would this be if the first example wasn't a "Hello, World!" example? Who knows, but this isn't one of those kind of quickstarts.
Run the Hello World test.
$ ./hello_world 1 [main] INFO shell - Hello, world!
You should have seen output similar to that above. If not, make sure you are in the right location and such. If you really had problems, please send a letter to the log4sh maintainers. Who knows, maybe you already found a bug. Hopefully not!
The Hello, World! test is about as simple as it gets. If you take a look at the test, all it does is load log4sh
, reset the default logging level from ERROR
to INFO
, and the logs a "Hello, world!" message. As you can see, it didn't take much to setup and use log4sh.
Example 2.2. Properties Configuration Test
In this example, a log4sh.properties
configuraiton file will be used to pre-configure log4sh before any logging messages are output. It demonstrates that a configuration file can be used to alter the behavior of log4sh without having to change any shell code.
Run the properties configuration test.
$ ./test-prop-config INFO - We are the Simpsons! INFO - Mmmmmm .... Chocolate. INFO - Homer likes chocolate ...
You should see much more output on your terminal that what was listed above. What is actually happening is log4sh is outputting information to STDERR using logging statements that were stored in the test-common
script. In addition, there were multiple logfiles generated (take a look in the test
directory), and output was written also written via Syslog. Take a look at both the property configuration script (test-prop-config
) and the common script (test-common
) if you would like to see what is happening. If you do, you will notice that nowhere in code was it configured to write to the any of those different locations. The log4sh.properties
configuration file did all of that work for us. Go ahead and take a look at it too. You might be amazed with how easy it was to write to so many locations with such a small amount of code.
Example 2.3. Runtime Configuration Test
This example is exactly like the last example as far as output is concerned (they both execute the same test-common
script), but this one is configured instead at runtime with function calls. It demonstrates that log4sh is fully configurable at runtime.
Run the runtime configuration test.
$ ./test-runtime-config INFO - We are the Simpsons! INFO - Mmmmmm .... Chocolate. INFO - Homer likes chocolate ...
You should again see much more output on your terminal that what was listed above. The output should also have been exactly the same (except that the times were different) as the above example. This is because the same logging commands were used. If you take a look a look in the test-runtime-config
script though, you will see that this time log4sh was configured completly at runtime. The log4sh.properties
was not used. It shows that log4sh can be fully configured without a pre-existing configuration file. This isn't nearly as friendly as using the configuration file, but there are times when it is needed.
Table of Contents
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 into the shell script
configure log4sh in code (optional)
call logging statements
To preconfigure log4sh, create a properties file (see the Properties File later in this document). If the properties file is not located in the same directory as log4sh, set the LOG4SH_CONFIGURATION
environment variable to the full path to the properties file. If you do not wish to preconfigure log4sh, please read the Configure log4sh in code section later in this chapter.
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 3.1. 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 3.2. Hello, world (using properties file)
#! /bin/sh # # log4sh example: Hello, world # myDir=`dirname $0` # find and source log4sh if [ -r "$myDir/log4sh" ]; then log4shDir=$myDir elif [ -r "./log4sh" ]; then log4shDir=. else echo "fatal: could not find log4sh" >&2 exit 1 fi . $log4shDir/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 3.3. 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=%-4r [%t] %-5p %c %x - %m%n
If log4sh was not preconfigured, the default configuration will be equivalent the config shown below.
Note: log4sh will complain if no configuration file was specified or found. If you meant for the default configuration to be used, or you want to configure log4sh via code, make sure to define the LOG4SH_CONFIGURATION
with the value of 'none
'.
log4sh.rootLogger=ERROR, stdout log4sh.appender.stdout=ConsoleAppender log4sh.appender.stdout.layout=PatternLayout log4sh.appender.stdout.layout.ConversionPattern=%-4r [%t] %-5p %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 3.4. Hello, world (configured in code)
#! /bin/sh # # log4sh example: Hello, world # # source log4sh (disabling properties file warning) LOG4SH_CONFIGURATION='none' . ./log4sh # set the global logging level logger_setLevel INFO # close the default STDOUT appender, and add a new STDERR appender appender_close stdout logger_addAppender stderr appender_setType stderr FileAppender appender_file_setFile stderr STDERR # say Hello to the world logger_info 'Hello, world'
Once log4sh is loaded, logging is as simple as calling the appropriate logging function with a message to be logged. Take a look at the above examples to see just how easy it was to log the statement "Hello, world"
at an INFO
level.
The samples above show the standard way of logging a message via log4sh. That standard method is by calling the appropriate function, and passing the message as a parameter.
There is a second way of logging as well. The second method is via pipes. What this method is really good for is logging the standard output (STDOUT
) of a command to the logfile. Piping echo statements is a bit silly, but something like piping the output of a ls is more practical (e.g. ls -l |logger_info
).
Table of Contents
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 4.1. 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 4.1. Logging Levels (from most output to least)
Level | Definition |
---|---|
TRACE | The TRACE 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.
Table 4.2. Appender Types
Type | Definition | Supported? |
---|---|---|
ConsoleAppender | output sent to console (STDOUT) | yes |
FileAppender | output sent to a file | yes |
DailyRollingFileAppender | output sent to a file that rolls over daily | partial; logs written, but not rotated |
RollingFileAppender | output sent to a file that rolls over by size | partial; works, but nees improvement |
SMTPAppender | output sent via email | parital; works, but needs improvement |
SyslogAppender | output sent to a remote syslog daemon | partial; only localhost supported |
An appender can take several different options.
Table 4.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.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 4.6. Setting an appender's layout pattern
log4sh.appender.A1.layout.ConversionPattern=%d [%p] %c - %m%n
Table 4.5. Pattern Options
Option | Definition | Supported? |
---|---|---|
c | Used to output the category of logging request. As this is not applicable in shell, the conversion character will always returns 'shell'. | partial (fixed) |
d |
Used to output the date of the logging event. The date conversion specifier may be followed by a date format specifier enclosed between braces, but this specifier will be ignored. For example, The default format of the date returned is equavilant to the output of the Unix date command with a format of | yes |
F |
Used to output the file name where the logging request was issued. The default value is equavilent basename $0. | yes |
L | This option is for compatibility with log4j properties files. | no (ignored) |
m | Used to output the script supplied message associated with the logging event. | yes |
n | This option is for compatibility with log4j properties files. | no (ignored) |
p | Used to output the priority of the logging event. | yes |
r | Used to output the number of seconds elapsed since the start of the script until the creation of the logging event. | yes |
t |
Used to output the current executing thread. As shell doesn't actually support threads, this is simply a value that can be set that can be put into the messages.i The default value is 'main'. | yes |
x | This option is for compatibility with log4j properties files. | no (ignored) |
X | Used to output the MDC (mapped diagnostic context) associated with the thread that generated the logging event. The X conversion character must be followed by an environment variable name placed between braces, as in %X{clientNumber} where clientNumber is the name of the environment variable. The value in the MDC corresponding to the environment variable will be output. | no (ignored) |
% | The sequence %% outputs a single percent sign. | yes |
There are some environment variables that can be used to pre-configure log4sh, or to change some of its default behavior. These variables should be set before log4sh is sourced so that they are immediately available to log4sh.
Here is the full list of supported variables.
Table 4.6. log4sh environment variables
Variable | Usage |
---|---|
LOG4SH_CONFIGURATION |
This variable is used to tell log4sh what the name of (and possibly the full path to) the configuration (a.k.a properties) file that should be used to configure log4sh at the time log4sh is sourced. If the value 'none' is passed, than log4sh will expect to be configured at a later time via run-time configuration. |
LOG4SH_CONFIG_PREFIX |
This variable is used to tell log4sh what prefix it should use when parsing the configuration file. Normally, the default value is 'log4sh' (e.g. 'log4sh.rootLogger'), but the value can be redefined so that a configuration file from another logging frame work such as log4j can be read. |
Table of Contents
Table 5.1. Appender
void |
Activate an appender's configuration. This should be called after reconfiguring an appender via code. It needs only to be called once before any logging statements are called. This calling of this function will be required in log4sh 1.4.x. appender_activateAppender myAppender | ||||||||||
void |
Disable any further logging via an appender. Once closed, the appender can be reopened by setting it to any logging Level (e.g. INFO). appender_close myAppender | ||||||||||
boolean
|
Checks for the existance of a named appender exists=`appender_exists myAppender` | ||||||||||
string
|
Deprecated as of 1.3.1 Gets the Type of an Appender at the given array index type=`appender_getAppenderType 3` | ||||||||||
string
|
Gets the Layout of an Appender type=`appender_getLayout myAppender` | ||||||||||
string
|
Gets the current logging Level of an Appender type=`appender_getLevel myAppender` | ||||||||||
string
|
Gets the Pattern of an Appender pattern=`appender_getPattern myAppender` | ||||||||||
string
|
Gets the Type of an Appender type=`appender_getType myAppender` | ||||||||||
void |
Deprecated as of 1.3.1 Sets the Type of an Appender (e.g. FileAppender) appender_setAppenderType myAppender FileAppender | ||||||||||
void |
Sets the Layout of an Appender (e.g. PatternLayout) appender_setLayout myAppender PatternLayout | ||||||||||
void/boolean |
Sets the Level of an Appender (e.g. INFO) appender_setLevel myAppender INFO | ||||||||||
void/boolean |
Sets the Pattern of an Appender appender_setPattern myAppender '%d %p - %m%n' | ||||||||||
void/boolean |
Sets the Type of an Appender (e.g. FileAppender) appender_setType myAppender FileAppender |
Table 5.2. FileAppender
string
|
Get the filename of a FileAppender appender_file_getFile myAppender | ||||||||||
void |
Set the filename for a FileAppender (e.g. "STDERR" or "/var/log/log4sh.log") appender_file_setFile myAppender STDERR | ||||||||||
void |
Not yet implemented Set the maximum backup index for a DailyRollingFileAppender or RollingFileAppender. appender_file_setMaxBackupIndex myAppender 3 | ||||||||||
void |
Not yet implemented Set the maximum backup file size for a DailyRollingFileAppender or RollingFileAppender. appender_file_setMaxBackupIndex myAppender 100KiB | ||||||||||
void |
Deprecated as of 1.3.2 Set the filename for a FileAppender (e.g. "STDERR" or "/var/log/log4sh.log") appender_setAppenderFile myAppender STDERR |
Table 5.3. Level
integer
|
Converts an externally used level tag into its integer equivalent levelInt=`logger_level_toInt WARN` | |||||
string
|
Converts an internally used level integer into its external level equivalent level=`logger_level_toLevel 3` |
Table 5.5. Logger
void |
The base logging command that logs a message to all defined appenders log DEBUG "This is a test message"` | ||||||||||
void/boolean |
Add and initialize a new appender logger_addAppender $appender | ||||||||||
void |
Deprecated as of 1.3.6 Add and initialize a new appender with a specific PatternLayout logger_addAppenderWithPattern $appender '%d %p - %m%n' | ||||||||||
void |
This is a helper function for logging a message at the DEBUG priority logger_debug "This is a debug message"` | ||||||||||
void |
This is a helper function for logging a message at the ERROR priority logger_error "This is a error message"` | ||||||||||
void |
This is a helper function for logging a message at the FATAL priority logger_fatal This is a fatal message` | ||||||||||
string
|
Get the filename that would be shown when the '%F' conversion character is used in a PatternLayout. myFilename=`logger_setFilename` | ||||||||||
string
|
Get the global default logging level (e.g. DEBUG). level=`logger_getLevel` | ||||||||||
void |
This is a helper function for logging a message at the INFO priority logger_info "This is a info message"` | ||||||||||
void |
Set the filename to be shown when the '%F' conversion character is used in a PatternLayout. logger_setFilename "myScript.sh" | ||||||||||
void |
Sets the global default logging level (e.g. DEBUG). logger_setLevel INFO | ||||||||||
void |
This is a helper function for logging a message at the TRACE priority logger_trace "This is a trace message"` | ||||||||||
void |
This is a helper function for logging a message at the WARN priority logger_warn "This is a warn message"` |
Table 5.6. Property
void |
Read configuration from a file. The existing
configuration is not cleared or reset. If you require a
different behavior, then call the log4sh_doConfigure myconfig.properties | |||||
void |
Deprecated as of 1.3.6
See log4sh_readProperties myconfig.properties | |||||
void |
This function completely resets the log4sh configuration to have no appenders with a global logging level of ERROR. log4sh_resetConfiguration |
Table 5.7. SMTPAppender
void |
Deprecated as of 1.3.1 Set the to address for the given appender appender_smtp_setTo myAppender user@example.com | ||||||||||
void/boolean |
Deprecated as of 1.3.1 Sets the email subject for an SMTP appender appender_setAppenderSubject myAppender "This is a test" | ||||||||||
string
|
Get the email subject for the given appender subject=`appender_smtp_getSubject myAppender` | ||||||||||
string
|
Get the to address for the given appender email=`appender_smtp_getTo myAppender` | ||||||||||
void/boolean |
Sets the email subject for an SMTP appender appender_smtp_setSubject myAppender "This is a test" | ||||||||||
void |
Set the to address for the given appender appender_smtp_setTo myAppender user@example.com |
Table 5.8. SyslogAppender
string
|
Deprecated as of 1.3.1 Get the syslog facility of the specified appender by index facility=`appender_getSyslogFacility 3` | ||||||||||
void |
Deprecated as of 1.3.2 Set the syslog facility for the given appender appender_setSyslogFacility myAppender local4` | ||||||||||
void |
Get the syslog facility for the given appender facility=`appender_syslog_getFacility myAppender` | ||||||||||
string
|
(stub function) Get the syslog host of the specified appender host=`appender_syslog_getHost myAppender` | ||||||||||
void |
Set the syslog facility for the given appender appender_syslog_setFacility myAppender local4` | ||||||||||
void |
(stub function) Set the syslog host for the given appender appender_syslog_setHost myAppender localhost` |
Table 5.9. Thread
string
|
Gets the current thread name. threadName=`logger_getThreadName` | |||||
void |
Removes the topmost thread name from the stack. The next thread name on the stack is then placed in the __log4sh_threadName variable. If the stack is empty, or has only one element left, then a warning is given that no more thread names can be popped from the stack. logger_popThreadName | |||||
void |
Sets the thread name (eg. the name of the script) and pushes the old on to a stack for later use. This thread name can be used with the '%t' conversion character within a PatternLayout. logger_pushThreadName "myThread" | |||||
void |
Sets the thread name (e.g. the name of the script). This thread name can be used with the '%t' conversion character within a PatternLayout. logger_setThreadName "myThread" |
Table 5.10. Trap
void |
This is a cleanup function to remove the temporary directory used by log4sh. It is provided for scripts who want to do log4sh cleanup work themselves rather than using the automated cleanup of log4sh that is invoked upon a normal exit of the script. log4sh_cleanup |
The idea of log4sh is obviously not novel, but the availibility of such a powerful logging framework that is available in (nearly) pure shell is. Hopefully you will find it useful in one of your projects as well.
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>
.
Log4sh is licensed under the GNU Lesser Public License. The contents and copyright of this document and all provided source code are owned by Kate Ward.