Log4cxx Description:
Apache Log4cxx is a framework which provides the capability to log messages in a variety of formats to the local console, local files, streamed over a socket or even launch an email based on a hierarchy of notification levels.
Log Level | Description |
FATAL | Severe errors that cause premature termination. |
ERROR | Other run-time errors or unexpected conditions. |
WARN | Run-time situations that are undesirable or unexpected, but not necessarily "wrong". |
INFO | Interesting run-time events (start-up/shutdown). |
DEBUG | Detailed information on the flow through the system. |
TRACE | More detailed information. |
Also defined are
ALL and
OFF.
Log4cxx Installation:
Prerequisites:
Red Hat Enterprise Linux 5/6 (RHEL5/RHEL6) and Ubuntu 8.10/12.04:
- autoconf and automake
- RHEL5: yum install autoconf automake
- Ubuntu: apt-get install autoconf automake
- libxml2 and libxml2-devel (http://xmlsoft.org/)
- RHEL5 (2.6.26): yum install libxml2 libxml2-devel
- Ubuntu 12.04 (2.7.8): sudo apt-get install libxml2 libxml2-dev
- Ubuntu 8.10 (2.6.32): sudo apt-get install libxml2 libxml2-dev
- gmp (http://swox.com/gmp/)
- RHEL5 (gmp 4.1.4): yum install gmp
- Ubuntu 8.10 (4.2.2): sudo apt-get install libgmpxx4 libgmp3-dev
- Ubuntu 12.04 (2.7.8): sudo apt-get install libgmpxx4ldbl libgmp3-dev
- boost and boost-devel
- RHEL5 (boost: 1.33.1): yum install boost boost-devel
- Ubuntu 8.10 (libboost-dev: 1.34.1): sudo apt-get install libboost-dev
- Ubuntu 12.04 (libboost-dev: 1.46): sudo apt-get install libboost-dev
- cppunit
- RHEL5 (1.12.0): Download RPMs from RepoForge
Download packages cppunit and cppunit-devel
- Ubuntu 8.10 (1.12.1): sudo apt-get install libcppunit-dev
- Ubuntu 12.04 (1.12.1-4): sudo apt-get install libcppunit-dev
- apr, apr-devel and apr-util, apr-util-devel
- RHEL5 (1.2.7): yum install apr apr-devel apr-util apr-util-devel
- Ubuntu 8.10 (1.2.12): sudo apt-get install libapr1 libaprutil1
- Ubuntu 12.04 (1.4.6-1): sudo apt-get install libapr1 libaprutil1
Log4cxx binary installation:
RPMs for Red Hat Enterprise Linux are available from the Extra Packages for Enterprise Linux (EPEL) website for RHEL5 and RHEL6:
https://fedoraproject.org/wiki/EPEL
Ubuntu: sudo apt-get install liblog4cxx10 liblog4cxx10-dev
Build Log4cxx from source:
Requires Maven for the build (and thus requires Java):
- Install Java: (Maven is a Java program) See the YoLinux Java installation tutorial
- Install Maven (untar Java jar files. Java program and thus platform independent)
(Called by XmlBeans configure script and Makefile)
- Download compiled Java program: http://maven.apache.org/download.html
- cd /opt
- wget http://apache.opensourceresources.org/maven/binaries/apache-maven-2.2.1-bin.tar.gz
- tar xzf apache-maven-2.2.1-bin.tar.gz
This creates /opt/apache-maven-2.2.1
- Configuration:
- Add path to shell environment: export PATH=$PATH:/opt/apache-maven-2.2.1/bin
This allows execution of the Maven build command mvn
- If using a proxy, edit /opt/apache-maven-2.2.1/conf/settings.xml
Set proxy: <proxies> <proxy> ...
Ubuntu/Debian Note: Maven can also be installed using apt-get: sudo apt-get install maven2
Build and install:
[Potential Pitfall]:
If you get the following error when attempting to use log4cxx:
error while loading shared libraries: liblog4cxx.so.10: cannot open shared object file: No such file or directory
Register the libraries with the system. Run the following command as root:
ldconfig -n /usr/lib
[Potential Pitfall]:
RHEL 6.3 gave me the following build error:
[prompt]$ ./configure --prefix=/usr LDFLAGS="-L/lib64 -L/usr/lib64"
[prompt]$ make
inputstreamreader.cpp:66: error: 'memmove' was not declared in this scope
make[3]: *** [inputstreamreader.lo] Error 1
Edit the following three files to fix this error:
- File: apache-log4cxx-0.10.0/src/main/cpp/inputstreamreader.cpp
Add the include file: #include <string.h>
Change from:
#include <log4cxx/logstring.h>
#include <log4cxx/helpers/inputstreamreader.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/helpers/pool.h>
#include <log4cxx/helpers/bytebuffer.h>
to:
#include <string.h>
#include <log4cxx/logstring.h>
#include <log4cxx/helpers/inputstreamreader.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/helpers/pool.h>
#include <log4cxx/helpers/bytebuffer.h>
- File: apache-log4cxx-0.10.0/src/main/cpp/socketoutputstream.cpp
Add the include file: #include <string.h>
- File: apache-log4cxx-0.10.0/src/examples/cpp/console.cpp
Add the include file: #include <stdio.h>
Add the include file: #include <string.h>
[Potential Pitfall]:
If you get the following error during the
configure process:
checking for APR... configure: error: --with-apr requires a directory or file to be provided
you may have neglected to install the packages apr-devel and apr-util-devel.
Install (RHEL5 example): rpm -ivh apr-devel-1.2.7-11.el5_3.1.i386.rpm apr-util-devel-1.2.7-7.el5_3.2.i386.rpm
[Potential Pitfall]:
Building on a 64 bit x86_64 Linux system - You may get the following link error:
/usr/lib/libaprutil-1.so: could not read symbols: File in wrong format
This is because the linker is trying to link with
/usr/lib/libaprutil-1.so.0 instead of
/usr/lib64/libaprutil-1.so.0
One can force a link with 64 bit libraries by using one of the following techniques:
- Add the following directives to the configure script: ./configure --prefix=/usr LDFLAGS="-L/lib64 -L/usr/lib64"
- Set the following environment variable: export LDFLAGS="-L/lib64 -L/usr/lib64"
- Edit the gcc/g++ compile/link statement and add the following directives: -L/lib64 -L/usr/lib64
or go all 32 bit (cross compile from 64 bit system). Build 32 bit code on the 64 bit system:
./configure CXXFLAGS="-m32" LDFLAGS="-m32"
Example of C++ logging using Log4cxx:
File:
testLog4cxx.cpp
01 | #include <log4cxx/logger.h> |
02 | #include <log4cxx/xml/domconfigurator.h> |
04 | using namespace log4cxx; |
05 | using namespace log4cxx::xml; |
06 | using namespace log4cxx::helpers; |
09 | LoggerPtr loggerMyMain(Logger::getLogger( "main" )); |
10 | LoggerPtr loggerFunctionA(Logger::getLogger( "functionA" )); |
14 | LOG4CXX_INFO(loggerFunctionA, "Executing functionA." ); |
22 | DOMConfigurator::configure( "Log4cxxConfig.xml" ); |
24 | LOG4CXX_TRACE(loggerMyMain, "this is a debug message for detailed code discovery. Value=" << value); |
25 | LOG4CXX_DEBUG(loggerMyMain, "this is a debug message." ); |
26 | LOG4CXX_INFO (loggerMyMain, "this is a info message, ignore. Value=" << value); |
27 | LOG4CXX_WARN (loggerMyMain, "this is a warn message, not too bad." ); |
28 | LOG4CXX_ERROR(loggerMyMain, "this is a error message, something serious is happening." ); |
29 | LOG4CXX_FATAL(loggerMyMain, "this is a fatal message!!!" ); |
Compile and static link:
- Installed from binary packages:
- g++ testLog4cxx.cpp -lapr-1 -laprutil-1 -llog4cxx
- Installed from source:
- 64 bit: g++ testLog4cxx.cpp -I/opt/include /opt/lib64/liblog4cxx.a -lapr-1 -laprutil-1
- 32 bit: g++ testLog4cxx.cpp -I/opt/include /opt/lib/liblog4cxx.a -lapr-1 -laprutil-1
Log4cxx Configuration File:
Log4cxxConfig.xml
(Specified in line 22 in the program above)
01 | <? xml version = "1.0" encoding = "UTF-8" ?> |
05 | < appender name="<b>appxConsoleAppender</ b >" class="org.apache.log4j.ConsoleAppender"> |
06 | < param name = "Target" value = "System.out" /> |
07 | < layout class = "org.apache.log4j.PatternLayout" > |
08 | < param name = "ConversionPattern" value = "%-5p %c{1} - %m%n" /> |
14 | < appender name="<b>appxNormalAppender</ b >" class="org.apache.log4j.FileAppender"> |
15 | < param name = "file" value="<b>appxLogFile.log</ b >" /> |
16 | < param name = "append" value = "true" /> |
17 | < layout class = "org.apache.log4j.PatternLayout" > |
18 | < param name = "ConversionPattern" value = "%d %-5p %C{2} (%F:%L) - %m%n" /> |
23 | < priority value = "all" /> |
24 | < appender-ref ref="<b>appxNormalAppender</ b >"/> |
25 | < appender-ref ref="<b>appxConsoleAppender</ b >"/> |
29 | < category name = "functionA" > |
30 | < priority value = "info" /> |
31 | < appender-ref ref="<b>appxNormalAppender</ b >"/> |
32 | < appender-ref ref="<b>appxConsoleAppender</ b >"/> |
35 | </ log4j:configuration > |
Note this configuration file logs all messages to the console and log file.
In general one configures the three major components of Log4cxx: loggers, appenders and layouts.
Console run output:
a.out
TRACE main - this is a debug message for detailed code discovery. Value=5
DEBUG main - this is a debug message.
INFO main - this is a info message, ignore. Value=5
WARN main - this is a warn message, not too bad.
ERROR main - this is a error message, something serious is happening.
FATAL main - this is a fatal message!!!
INFO functionA - Executing functionA.
INFO functionA - Executing functionA.
Log file output:
appxLogFile.log
2013-08-23 10:58:46,331 TRACE (testLog4cxx.cpp:24) - this is a debug message for detailed code discovery. Value=5
2013-08-23 10:58:46,331 DEBUG (testLog4cxx.cpp:25) - this is a debug message.
2013-08-23 10:58:46,331 INFO (testLog4cxx.cpp:26) - this is a info message, ignore. Value=5
2013-08-23 10:58:46,331 WARN (testLog4cxx.cpp:27) - this is a warn message, not too bad.
2013-08-23 10:58:46,331 ERROR (testLog4cxx.cpp:28) - this is a error message, something serious is happening.
2013-08-23 10:58:46,331 FATAL (testLog4cxx.cpp:29) - this is a fatal message!!!
2013-08-23 10:58:46,331 INFO (testLog4cxx.cpp:14) - Executing functionA.
2013-08-23 10:58:46,331 INFO (testLog4cxx.cpp:14) - Executing functionA.
Log4cxx configuration file options:
Log4cxx can log to the console and log file as shown above. It can also log in XML format or a specified format, it can log time intervals with each interval logged to its' own log file (time based logs with TimeBasedRollingPolicy) or log to files of a specified size where upon reaching the size limit, it logs to a new file (size based logs set MaxFileSize).
Log4cxx can log to the Linux syslogd (local or remote) or to a socket stream.
Email alerts can be sent (e.g. send email upon total extreme failure).
Logs can be sent via ODBC to MySQL or Postgres.
Add the "appender" definition and "root" "appender-ref" snippets below to the above configuration file.
Time based logs (daily) snippet:
01 | < appender name="<b>appxRollingAppenderDaily</ b >" class="org.apache.log4j.rolling.RollingFileAppender"> |
02 | < rollingPolicy class = "org.apache.log4j.rolling.TimeBasedRollingPolicy" > |
03 | < param name = "FileNamePattern" value = "TimeBasedLog.%d{yyyy-MM-dd}.log" /> |
04 | < param name = "activeFileName" value="<b>appxDailyLog.log</ b >"/> |
07 | < layout class = "org.apache.log4j.PatternLayout" > |
08 | < param name = "ConversionPattern" value = "%d{yyyy-MM-dd HH:mm:ss,SSS} %x [%p] (%F:%L) %m%n" /> |
10 | < param name = "file" value="<b>appxDailyLog.log</ b >"/> |
11 | < param name = "append" value = "true" /> |
18 | < priority value = "all" /> |
19 | < appender-ref ref="<b>appxRollingAppenderDaily</ b >"/> |
Size based logs snippet:
01 | < appender name="<b>appxRollingAppenderSize</ b >" class="org.apache.log4j.RollingFileAppender"> |
02 | < param name = "file" value="<b>appxSizeBasedLog.log</ b >"/> |
03 | < param name = "append" value = "true" /> |
04 | < param name = "MaxFileSize" value = "5KB" /> |
05 | < param name = "MaxBackupIndex" value = "5" /> |
06 | < layout class = "org.apache.log4j.PatternLayout" > |
07 | < param name = "ConversionPattern" value = "%d %-5p [%c] %m%n" /> |
15 | < priority value = "all" /> |
16 | < appender-ref ref="<b>appxRollingAppenderSize</ b >"/> |
Note:
- Set file size limit MaxFileSize to 5KB
- Keep five rolling log files with MaxBackupIndex: appxSizeBasedLog.log.1, ... appxSizeBasedLog.log.5
Log4cxx will operate "round robin" and overwrite the previous log file.
XML outputlogs: (used by Chainsaw)
01 | < appender name="<b>appxLogFileAppenderXml</ b >" class="org.apache.log4j.RollingFileAppender"> |
02 | < param name = "file" value="<b>appxXmlLog.txt</ b >" /> |
03 | < param name = "append" value = "true" /> |
04 | < param name = "ImmediateFlush" value = "true" /> |
05 | < layout class = "org.apache.log4j.xml.XMLLayout" /> |
12 | < priority value = "all" /> |
13 | < appender-ref ref="<b>appxLogFileAppenderXml</ b >"/> |
Send an email via SMTP:
01 | < appender name="<b>appxSMTPAppenderEmail</ b >" class="org.apache.log4j.net.SMTPAppender"> |
02 | < param name = "BufferSize" value = "512" /> |
03 | < param name = "SMTPHost" value="<b>smtp.yourdomain.com</ b >" /> |
04 | < param name = "SMTPPort" value = "25" /> |
05 | < param name = "From" value="<b>sysadmin@yourdomain.com</ b > (mailto:< b >sysadmin@yourdomain.com</ b >)" /> |
06 | < param name = "To" value="<b>bigboss@yourdomain.com</ b > (mailto:< b >bigboss@yourdomain.com</ b >)" /> |
07 | < param name = "CC" value="<b>otheruser@yourdomain.com</ b > (mailto:< b >otheruser@yourdomain.com</ b >)" /> |
08 | < param name = "SMTPUsername" value="<b>adminaccount</ b >" /> |
09 | < param name = "SMTPPassword" value="<b>supersecret</ b >" /> |
10 | < layout class = "org.apache.log4j.PatternLayout" > |
11 | < param name = "ConversionPattern" value = "[%d{ABSOLUTE},%c{1}] %m%n" /> |
19 | < priority value = "all" /> |
20 | < appender-ref ref="<b>appxSMTPAppenderEmail</ b >"/> |
Using Java properties style configuration file:
The best approach is to use the XML configuration file and the DOMConfigurator class to read it. XML and this class can support more complex configurations which may include Filters, custom ErrorHandlers, nested appenders, etc ...
For some, the Java properties style configuration file is their preference but note that some advanced features are not supported.
Note that log4cxx and log4j both support XML config files.
File:
testLog4cxx.cpp
01 | #include <log4cxx/logger.h> |
02 | #include <log4cxx/propertyconfigurator.h> |
04 | using namespace log4cxx; |
05 | using namespace log4cxx::helpers; |
08 | LoggerPtr loggerMyMain(Logger::getLogger( "main" )); |
09 | LoggerPtr loggerFunctionA(Logger::getLogger( "functionA" )); |
13 | LOG4CXX_INFO(loggerFunctionA, "Executing functionA." ); |
21 | PropertyConfigurator::configure( "Log4cxxConfig.cfg" ); |
23 | LOG4CXX_TRACE(loggerMyMain, "this is a debug message for detailed code discovery. Value=" << value); |
24 | LOG4CXX_DEBUG(loggerMyMain, "this is a debug message." ); |
25 | LOG4CXX_INFO (loggerMyMain, "this is a info message, ignore. Value=" << value); |
26 | LOG4CXX_WARN (loggerMyMain, "this is a warn message, not too bad." ); |
27 | LOG4CXX_ERROR(loggerMyMain, "this is a error message, something serious is happening." ); |
28 | LOG4CXX_FATAL(loggerMyMain, "this is a fatal message!!!" ); |
Compile and static link:
g++ testLog4cxx.cpp -I/opt/include /opt/lib/liblog4cxx.a -lapr-1 -laprutil-1
Java style properties configuration file:
Log4cxxConfig.cfg
log4j.rootLogger=INFO, rootConsoleAppender, rootFileAppender
# Name of appender is the fully.qualified.name.of.appender.class
log4j.appender.rootConsoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.rootConsoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.rootConsoleAppender.layout.ConversionPattern=%d %-5p [%c] - %m%n
log4j.appender.rootFileAppender=org.apache.log4j.FileAppender
log4j.appender.rootFileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.rootFileAppender.layout.ConversionPattern=%d %-5p [%c] - %m%n
log4j.appender.rootFileAppender.File=test.log
# set whether to overwrite or append to the file
log4j.appender.rootFileAppender.Append=false
This configuration will log messages to the terminal console and to a file
test.log
Run program:
a.out
Log file output from program execution:
test.log
2013-08-22 19:44:01,033 INFO [main] - this is a info message, ignore. Value=5
2013-08-22 19:44:01,033 WARN [main] - this is a warn message, not too bad.
2013-08-22 19:44:01,033 ERROR [main] - this is a error message, something serious is happening.
2013-08-22 19:44:01,033 FATAL [main] - this is a fatal message!!!
2013-08-22 19:44:01,033 INFO [functionA] - Executing functionA.
Using Log4cxx with the "Chainsaw" log file viewer:
Apache Chainsaw is a Java GUI application for viewing log files.
It can accept streaming logs or view a log file.
Chainsaw will filter, color code and display log messages. When streaming log messages over a socket, use the XMLLayout sent with the XMLSocketAppender.
The screenshot below is showing two applications streaming log messages to two "receivers" configured in Chainsaw.
Controls on the left panel set the logging focus to the log pointers of your choice.
Right click on the logger pointer and select "Focus on logger-ptr-name" to turn off all other logger pointers you don't want to see including logging from the Chainsaw application itself.
The controls on the right panel define the log levels for the application.
The Apache Chainsaw log viewer runs in a socket server configuration while the log producing application runs as a socket client.
Thus start and configure Chainsaw before the application generating the log messages or "restart the receivers" in Chainsaw before starting the applications to be logged.
Dialog box presented upon start-up:
The Log4cxx configuration below will set-up the application to be the socket client which will attach to the Chainsaw socket server.
Configure Chainsaw:
- Right click on "Receivers"
- Select option "New Receivers"
- Select "New XMLSocketReceiver" (This will open up a new dialog window shown below)
- Populate the options:
- active: false (default)
- class: org.apache.log4j.net.XMLSocketReceiver (default)
- decoder: org.apache.log4j.xml.XMLDecoder
- name: name-of-application-you-are-logging
- paused: false (default)
- port: 4448 (default)
- threshold: TRACE (Select from TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF, ALL)
When restarting applications logging with XMLSocketReceiver appender, you must reset the chainsaw socket server receivers.
After the applications have attached logged, and exited (closed the socket connections), one must right click “Receivers” and restart the receivers to relaunch the socket servers if one is to restart the applications and begin logging again.
Other user configurations: Select the "chainsaw-log" tab to display the logging pane. From here the configuration menu is available from the tool bar "Current tab" + "LogPanel preferences ...".
I find that log levels shown as text messages are more clear than the icons.
Display Columns Configuration:
Toolbar “Current Tab” + “Log panel preferences”
The application Log4cxx configuration to log to the console as well as to connect and stream XML log messages to Chainsaw:
01 | <? xml version = "1.0" encoding = "UTF-8" ?> |
05 | < appender name = "ConsoleAppender" class = "org.apache.log4j.ConsoleAppender" > |
06 | < param name = "Target" value = "System.out" /> |
07 | < layout class = "org.apache.log4j.PatternLayout" > |
08 | < param name = "ConversionPattern" value = "%-5p %c{1} - %m%n" /> |
12 | < appender name = "ChainsawAppenderXML" class = "org.apache.log4j.XMLSocketAppender" > |
13 | < param name = "RemoteHost" value = "192.168.1.101" /> |
14 | < param name = "Port" value = "4448" /> |
15 | < layout class = "org.apache.log4j.xml.XMLLayout" > |
16 | < param name = "properties" value = "true" /> |
17 | < param name = "locationinfo" value = "true" /> |
22 | < priority value = "error" /> |
23 | < appender-ref ref = "ConsoleAppender" /> |
24 | < appender-ref ref = "ChainsawAppenderXML" /> |
27 | < category name = "main" > |
28 | < priority value = "trace" /> |
31 | < category name = "MySAX2Handler" > |
32 | < priority value = "error" /> |
35 | </ log4j:configuration > |
Other Logger Display Programs:
Apache Chainsaw is my personal preference as it can display streaming logs from multiple programs, can ingest log files and works.
Many of the programs listed below have their personal strengths but often omit a key basic feature possessed by Chainsaw.
Log Viewer | Description |
Apache Chainsaw | Display log files, socket streams, filters, supports multiple applications. |
Apache LogFactor5 | Display log files, socket streams, filters, supports multiple applications. LogFactor5 ships with Apache Log4j. Download from http://logging.apache.org/log4j/ RHEL6 install: yum install log4j RHEL6 script: /usr/bin/logfactor5 Run: java -cp log4j-1.2.8.jar org.apache.log4j.lf5.StartLogFactor5 |
jLogViewer | Java log viewer - log files only. No network access. Downloads: http://sourceforge.net/projects/jlogviewer/ Run: java -jar jlogviewer_1_0_0d.jar |
Lilith | Ports predefined for various uses. Use port 11000 for XML LoggingEvent.
Downloads: http://sourceforge.net/projects/lilith/files/lilith/0.9.43/
cd /opt
tar xzf lilith-0.9.43-bin.tgz
/opt/lilith-0.9.43/bin/lilith
Got connection error:
log4cxx: Could not connect to remote log4cxx server log4cxx: IO Exception : status code = 111
|
LogViewer4J | Allows only one streaming connection, one port. Installation:
Download: http://sourceforge.net/projects/logview4j/
cd /opt
sudo unzip /home/greg/Downloads/logview4j-1.0.3.zip
cd /opt/logview4j-1.0.3; java -jar logview4j-1.0.3.jar
No interactive configuration. Uses port config and limits set in:
/opt/logview4j-1.0.3/config/logview4j.properties
Got connection errors:
log4cxx: Could not connect to remote log4cxx server log4cxx: IO Exception : status code = 111
|
OLV: Otros log viewer and parser | Seems very configurable, capable, customizable but I could not get it to work.
cd /opt
unzip olv-2013-01-24.zip
cd /opt/olv-2013-01-24
/opt/olv-2013-01-24/olv.sh
Socket listener: Tools + Staart socket listener + select log importer:
XMLFormatter
|
Vigilog | Log files only. No network logging.
Downloads: http://sourceforge.net/projects/vigilog/files/
cd /opt
unzip vigilog-1.3.1-assembly.zip
Start script:
#!/bin/bash
if [ -d /usr/java/latest ]
then
PATH=/usr/java/latest/bin:$PATH
export JAVA_HOME=/usr/java/latest
export CLASSPATH=/usr/java/latest/lib/tools.jar:./
export MANPATH=$JAVA_HOME/man:/opt/man:$MANPATH
fi
cd /opt/vigilog-1.3.1
java -jar vigilog-1.3.1.jar
|
JLV: Java logging viewer | Stand alone app or Eclipe plugin. |
LogSaw | Based on Eclipse platform using Apache Lucene. |
Log.io | Web based display. Powered by node.js + socket.io Downloads: https://github.com/NarrativeScience/Log.io |
Log2Web | Web based - beta, not worked on since 2008 |
Programatic Log4cxx Configuration:
Log4cxx can be configured using a configuration file or by using API calls.
As a feature to add robustess to the application, it is often wise to allow an application to continue with some simple basic log4cxx configuration if the configuration file is absent.
Basic code snipets to detect if the configuration file exists.
If not, set a basic log4cxx configuration programatically.
13 | #include <log4cxx/logger.h> |
14 | #include <log4cxx/xml/domconfigurator.h> |
15 | #include <log4cxx/simplelayout.h> |
16 | #include <log4cxx/consoleappender.h> |
19 | log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger( "logger4cxx" )); |
25 | log4cxx::AppenderPtr defaultAppender = NULL; |
26 | log4cxx::LayoutPtr defaultLayout = NULL; |
32 | std::string topicName(name); |
33 | std::string log4cxxConfigFile = "Log4cxxConfig.xml" ; |
36 | if ( stat(log4cxxConfigFile.c_str(), &fileStat) == 0) |
38 | if (fileStat.st_mode & S_IRUSR) |
40 | log4cxx::xml::DOMConfigurator::configure(log4cxxConfigFile); |
45 | defaultLayout = new log4cxx::SimpleLayout(); |
46 | defaultAppender = new log4cxx::ConsoleAppender(defaultLayout); |
47 | logger->addAppender(defaultAppender); |
48 | logger->setLevel(log4cxx::Level::getInfo()); |
50 | std::cout << "Could not open Log4cxx configuration XML file: " << log4cxxConfigFile << std::endl; |
51 | perror ( "Problem opening log4cxx config file" ); |
52 | char cCurrentPath[200]; |
53 | std::cout << "Current Path: " << GETCWD(cCurrentPath, 200) << std::endl; |
60 | if (defaultAppender) delete defaultAppender; |
61 | if (defaultLayout) delete defaultLayout; |
Creating a Custom Log4cxx Appender:
Log4cxx includes appenders to write to the console, files, sockets, etc.
Log4cxx can be extended to include your own custom appender to write log messages to a medium of your choice such as a message queue or NoSQL data store.
The following is an example of a simple custom appender upon which one may generate a useful Log4cxx appender specific to their needs.
The solution includes a new C++ class which derives from the SkeletonAppender class.
This class is compiled and linked with your application even though you do not instantiate an object from this class.
It is the Log4cxx framework which instantiates and invokes the appender based on the configuration file directives.
File:
MyCustomAppender.hpp
04 | #include <log4cxx/appenderskeleton.h> |
05 | #include <log4cxx/spi/loggingevent.h> |
11 | class MyCustomAppender : public AppenderSkeleton |
14 | DECLARE_LOG4CXX_OBJECT(MyCustomAppender) |
16 | BEGIN_LOG4CXX_CAST_MAP() |
17 | LOG4CXX_CAST_ENTRY(MyCustomAppender) |
18 | LOG4CXX_CAST_ENTRY_CHAIN(AppenderSkeleton) |
19 | END_LOG4CXX_CAST_MAP() |
25 | void append( const spi::LoggingEventPtr& event, log4cxx::helpers::Pool& p); |
29 | bool isClosed() const { return closed; } |
31 | bool requiresLayout() const { return true ; } |
File:
MyCustomAppender.cpp
01 | #include "MyCustomAppender.hpp" |
04 | using namespace log4cxx; |
05 | using namespace log4cxx::helpers; |
08 | IMPLEMENT_LOG4CXX_OBJECT(MyCustomAppender) |
10 | MyCustomAppender::MyCustomAppender() {} |
12 | MyCustomAppender::~MyCustomAppender() {} |
14 | void MyCustomAppender::append( const spi::LoggingEventPtr& event, Pool& p) |
16 | if ( this ->layout == NULL ) { |
17 | LOG4CXX_ENCODE_CHAR(nameStr, name); |
18 | std::string msg( "No Layout set for the appender named [ " ); |
22 | LOG4CXX_DECODE_CHAR(msgL, msg); |
23 | errorHandler->error(msgL); |
27 | log4cxx::LogString fMsg; |
29 | this ->layout->format(fMsg, event, p); |
31 | LOG4CXX_ENCODE_CHAR(fMsgStr, fMsg); |
35 | printf ( "MyCustomAppender: %s" , fMsgStr.c_str()); |
37 | LogVector.push_back(fMsgStr); |
40 | void MyCustomAppender::close() |
The main program testLog4cxx.cpp from the prior example above, will work.
There is no change to the code. Invocation of the new appender is through the configuration file which references the new appender.
The new appender does have to be linked with the application in order to be available.
Compile and static link: g++ testLog4cxx.cpp MyCustomAppender.cpp -I/opt/include /opt/lib/liblog4cxx.a -lapr-1 -laprutil-1
Note that the registration of the appender is done by using the macro IMPLEMENT_LOG4CXX_OBJECT(MyCustomAppender)
This macro adds factory classes for the appenders to a hashmap which utilizes libdl to load the appender dynamically when it is called for use in the configuration file.
(This is how they do it: libdl and dynamic linking)
The following configuration file will invoke the custom appender.
01 | <? xml version = "1.0" encoding = "UTF-8" ?> |
05 | < appender name="<b>ConsoleAppender</ b >" class="org.apache.log4j.ConsoleAppender"> |
06 | < param name = "Target" value = "System.out" /> |
07 | < param name = "Threshold" value = "WARN" /> |
08 | < layout class = "org.apache.log4j.PatternLayout" > |
09 | < param name = "ConversionPattern" value = "%-5p - %m (%F:%L) %n" /> |
13 | < appender name="<b>MyCustomAppender</ b >" class="MyCustomAppender"> |
14 | < param name = "Target" value = "System.out" /> |
15 | < param name = "Threshold" value = "WARN" /> |
16 | < layout class = "org.apache.log4j.PatternLayout" > |
17 | < param name = "ConversionPattern" value = "%d{MMM dd yyyy HH:mm:ss,SSS} [%-5p] - %m %n" /> |
22 | < priority value = "ALL" /> |
23 | < appender-ref ref="<b>ConsoleAppender</ b >"/> |
24 | < appender-ref ref="<b>MyCustomAppender</ b >"/> |
27 | </ log4j:configuration > |
To shut off the logging of all messages, set
<priority value="OFF">
Run:
./a.out
WARN - this is a warn message, not too bad. (test.cpp:33)
MyCustomAppender: Aug 27 2013 15:04:18,837 [WARN ] - this is a warn message, not too bad.
ERROR - this is a error message, something serious is happening. (test.cpp:34)
MyCustomAppender: Aug 27 2013 15:04:18,837 [ERROR] - this is a error message, something serious is happening.
FATAL - this is a fatal message!!! (test.cpp:35)
MyCustomAppender: Aug 27 2013 15:04:18,837 [FATAL] - this is a fatal message!!!
Links:

Books: