log4cxx Tutorial in Visual C++ ?

I’ve got quite a number of request in email requesting me for a simple log4cxx sample program that illustrate the usage of log4cxx ever since I’ve wrote this article. I’ve sent my sample program in emails to those request, but I guess it will be more productive if I would just put up a sample usage here.

This will be a to-the-point log4cxx tutorial, you will probably need the binary here before you proceed:

The log4cxx XML Configuration file ( MyLogConfig.xml ) will shows you the usage of following appender:

	org.apache.log4j.ConsoleAppender
	org.apache.log4j.FileAppender
	org.apache.log4j.rolling.RollingFileAppender
	org.apache.log4j.RollingFileAppender
	org.apache.log4j.net.SMTPAppender
	org.apache.log4j.jdbc.JDBCAppender
	org.apache.log4j.net.XMLSocketAppender

The MyLogConfig.xml is properly commented, so it should be self explanatory. ( and no, there is no typo, the configuration file actually refer it as log4j )

Writing The Code

Create a C++ Console project in VC++, and put in the following code:

#include <log4cxx\logger.h>
#include <log4cxx\xml\domconfigurator.h>
#include <windows.h>
 
using namespace log4cxx;
using namespace log4cxx::xml;
using namespace log4cxx::helpers;
 
// Define static logger variable
LoggerPtr loggerToFile(Logger::getLogger( _T("MyLogger") ) );
LoggerPtr loggerMyFunctionA(Logger::getLogger( _T("MyFunctionA") ));
 
void MyFunctionA()
{
    LOG4CXX_INFO(loggerMyFunctionA, _T("Executing MyFunctionA."));
}
 
int _tmain(int argc, _TCHAR* argv[])
{
    // Load configuration file
    DOMConfigurator::configure("MyLogConfig.xml");
 
    // Loop something
    for(int i=0; i&lt;10; ++i)
    {
        LOG4CXX_DEBUG(loggerToFile, _T("this is a debug message."));
        LOG4CXX_INFO (loggerToFile, _T("this is a info message, just ignore."));
        LOG4CXX_WARN (loggerToFile, _T("this is a warn message, dont worry too much."));
        LOG4CXX_ERROR(loggerToFile, _T("this is a error message, something serious is happening."));
        LOG4CXX_FATAL(loggerToFile, _T("this is a fatal message, crash and burn!!!"));
        MyFunctionA();
 
        Sleep(1000);
        printf("i = %d\n", i);
    }
 
    getchar();
    return 0;

Writing the log4cxx Configuration File

The log4cxx configuration file as follows ( MyLogConfig.xml ):

<?xml version="1.0" encoding="UTF-8" ?>
 <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> 
 	<!-- Output the log message to system console.
 	-->
 	<appender name="MyConsoleAppender" class="org.apache.log4j.ConsoleAppender"> 
 		<param name="Target" value="System.out"/> 
 		<layout class="org.apache.log4j.PatternLayout"> 
 			<param name="ConversionPattern" value="%-5p %c{1} - %m%n"/> 
 		</layout> 
 	</appender> 
 
 
 	<!-- Output the log message to a log file named "NormalLogFile.log"
 	-->
 	<appender name="MyNormalAppender" class="org.apache.log4j.FileAppender">
 		<param name="file" value="NormalLogFile.log" />
 		<param name="append" value="true" />
 		<layout class="org.apache.log4j.PatternLayout"> 
 			<param name="ConversionPattern" value="%d %5p %c{1} - %m%n" /> 
 		</layout>
 	</appender>
 
 
 	<!-- the following appender with the name "TimeBasedLog.log", every night a few seconds after
 	     12::00PM the old log will be renamed with append the date in filename, and a new log file
 	     with the name "TimeBasedLog.log" will be create. 
 	     notice the RollingFileAppender is under "org.apache.log4j.rolling" namespace
 	-->
 	<appender name="MyRollingAppenderDaily" class="org.apache.log4j.rolling.RollingFileAppender">
 		<rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
 			<param name="FileNamePattern" value="TimeBasedLog.%d{yyyy-MM-dd}.log"/>
 			<param name="activeFileName" value="TimeBasedLog.log"/>
 		</rollingPolicy>
 
 		<layout class="org.apache.log4j.PatternLayout">
 			<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} %x [%p] (%F:%L) %m%n"/>
 		</layout> 
 		<param name="file" value="TimeBasedLog.log"/>
 		<param name="append" value="true"/>
 	</appender>
 
 
 	<!-- On application startup, a log file named "SizeBasedLog.log" will be create if not exist.
 	     When the log file reach beyond 5KB, it will be renamed "SizeBasedLog.log.1", when the log
 	     index reach "SizeBasedLog.log.5", the next rename will be back to "SizeBasedLog.log.1" and
 	     overite the old log. 
 	-->
 	<appender name="MyRollingAppenderSize" class="org.apache.log4j.RollingFileAppender">
 		<param name="file" value="SizeBasedLog.log"/>
 		<param name="append" value="true"/>
 		<param name="MaxFileSize" value="5KB"/>
 		<param name="MaxBackupIndex" value="5"/>
 		<layout class="org.apache.log4j.PatternLayout">
 			<param name="ConversionPattern" value="%d %-5p [%c] %m%n"/>
 		</layout>
 	</appender> 
 
 
 	<!-- the following appender creates a logfile in log4j XML format, suitable to viewing with
 	     XML log viewer such as Chainsaw (http://logging.apache.org/log4j/docs/chainsaw.html)
 	-->
 	<appender name="MyLogFileAppenderXml" class="org.apache.log4j.RollingFileAppender">
 		<param name="file" value="XmlLog.txt" />
 		<param name="append" value="true" />
 		<param name="ImmediateFlush" value="true" />
 		<layout class="org.apache.log4j.xml.XMLLayout" />
 	</appender> 
 
 
 	<!-- This appender will send email through SMTP server.
 	-->
 	<appender name="MySMTPAppenderEmail" class="org.apache.log4j.net.SMTPAppender">
 		<param name="BufferSize" value="512" />
 		<param name="SMTPHost" value="smtp.youremailserver.com" />
 		<param name="SMTPPort" value="25" />
 		<param name="From" value="yourname@youremailserver.com (mailto:yourname@youremailserver.com)" />
 		<param name="To" value="yourname@youremailserver.com (mailto:yourname@youremailserver.com)" />
 		<param name="CC" value="someoneelse@youremailserver.com (mailto:someoneelse@youremailserver.com)" />
 		<param name="SMTPUsername" value="yourusername" />
 		<param name="SMTPPassword" value="yourpassword" />
 		<layout class="org.apache.log4j.PatternLayout">
 			<param name="ConversionPattern" value="[%d{ABSOLUTE},%c{1}] %m%n"/>
 		</layout>
 	</appender> 
 
 
 	<!-- This appender will write the log message to a database through ODBC connection
 	     to Database.
 	-->
 	<appender name="MyOdbcMysqlAppender" class="org.apache.log4j.odbc.ODBCAppender">
 		<param name="URL" value="Driver={MySQL ODBC 5.1 Driver};Server=localhost;Database=errorlog;User=logger;Password=abc123;Option=3;"/>
 		<layout class="org.apache.log4j.PatternLayout">
 			<param name="ConversionPattern" value="INSERT INTO errorlog (errormessage) VALUES ('%d - %c - %p - %m')"/>
 		</layout>
 	</appender> 
 
 	<appender name="MyOdbcPostgresAppender" class="org.apache.log4j.odbc.ODBCAppender">
 		<param name="URL" value="Driver={PostgreSQL UNICODE};Server=localhost;Port=5432;Database=errorlog;Uid=postgres;Pwd=abc123;"/>
 		<layout class="org.apache.log4j.PatternLayout">
 			<param name="ConversionPattern" value="INSERT INTO errorlog (errormessage) VALUES ('%d - %c - %p - %m')"/>
 		</layout>
 	</appender> 
 
 
 	<!-- This appender will write log message and send it through XMLSocketAppender, a receiver
 	     with XMLSocketReceiver such as Chainsaw (http://logging.apache.org/log4j/docs/chainsaw.html)
 	     will be able to receive and interpret it.
 	-->
 	<appender name="MyXmlAppender" class="org.apache.log4j.net.XMLSocketAppender">
 		<param name="Port" value="1234"/>
 		<param name="RemoteHost" value="10.7.5.15"/>
 		<param name="ReconnectionDelay" value="60000"/>
 		<param name="LocationInfo" value="true" />
 	</appender> 
 
 
 	<!-- Using XMLSocketAppender couple with pattern layout to send text lines to 
 	     a ordinary TCP listener, any application that implements TCP socuket listening
 	     will be able to receive the log message.
 	-->
 	<appender name="MyXmlAppenderFormated" class="org.apache.log4j.net.XMLSocketAppender">
 		<param name="Port" value="1235"/>
 		<param name="RemoteHost" value="localhost"/>
 		<param name="ReconnectionDelay" value="60000"/>
 		<param name="LocationInfo" value="true" />
 		<layout class="org.apache.log4j.PatternLayout">
 			<param name="ConversionPattern" value="%d %-5p [%c] %m%n"/>
 		</layout>
 	</appender>
 
 
 	<!-- Setup the root category, add the appenders and set the default level 
 	     5 level of logging,  ALL < DEBUG < INFO < WARN < ERROR < FATAL 
 	     The root level is set with INFO, which mean any message greater or same
 	     as INFO will be log down, in this case, DEBUG is not logged. 
 	     To log all regardless of logging level, set <priority value="ALL">
 	-->
 	<root>
 		<priority value="all" />
 		<appender-ref ref="MyRollingAppenderDaily"/>
 		<appender-ref ref="MyRollingAppenderSize"/>
 		<appender-ref ref="MyConsoleAppender"/>
 		<appender-ref ref="MyLogFileAppenderXml"/>
 		<appender-ref ref="MyXmlAppenderFormated"/>
 		<appender-ref ref="MyXmlAppender"/>
 		<appender-ref ref="MyOdbcMysqlAppender"/>
 		<appender-ref ref="MyOdbcPostgresAppender"/>
 	</root> 
 
 	<!-- Specify the level for some specific categories -->
 	<category name="MyFunctionA" >
 		<priority value ="info" />
 		<appender-ref ref="MyNormalAppender" />
 	</category> 
 
 </log4j:configuration>

You may notice that the tag has log4j in it, that’s fine, it’ll work :)

I can’t remember where did I gather all the pieces up there, but I kind of remember that it was pieces from different sources before I have put in together. Let me know if you need additional instructions. Is this helpful? Happy Logging ; )

Updated 27 Nov 2008:

As Greg pointed out, the JdbcAppender is not working with log4cxx. I did some digging in the source and realize that the real reason is that JdbcAppender is not there in the source at all, or it has not been ported yet. However, I’ve updated the XML config file to use OdbcAppender to write to PostgreSQL and MySql database. Sorry for the misleading sample of XML configuration :)

Note: When testing with Mysql5 with log4cxx through odbc, my test app would crash on exit, the call stack shows that myodbc5.dll crashed. Everything works fine with PostgreSQL, so I’m not sure if this is MySql specific problem or not… hm…

About these ads

85 thoughts on “log4cxx Tutorial in Visual C++ ?

  1. testsuite-standalone.dsw is the solution I’m trying to build.

    From the looks of it, apr builds, aprutil builds, and xml builds.

    don’t know where to turn to next

  2. Update: Whew… After long hours of confusingly following steps, I took a step back and restarted the project. I did each step carefully and… it compiled! Sorry for spamming this blog… Now all I’ve got to do is figure out why I’m getting the errors,

    log4cxx: Could not open file [MyLogConfig.xml].<br />
    log4cxx: No appender could be found for logger (MyLogger).<br />
    log4cxx: Please initialize the log4cxx system properly.

    and I’m set. Thanks all.

  3. Hey Henry, nice to know you got it to work, your last steps, its probably just because your MyLogConfig.xml file is not within the search path of your binary that all. cheers~

  4. HI , I built log4cxx-0.10.0-win32 source on VS2005, I can see tht only log4cxx.dll getting generated no log4cxx.lib, kindly le me know do I need to set any properties in the VS2005 to get log4cxx.lib

    Thanks in Advance

  5. I got it , its a small option which we need to set in VS2005
    Configuration Properties -> General in tht right side under
    Projects Defaults edit Configuration Type.

  6. Hi,

    I tried to use you sample code to debug a COM object.
    Build is OK but I cant see any ouputs (console or files), nothing.
    I really dont know where is the problem ! … any idea ?

    I use VS 2005 …

    Thx,
    jetronic

    PSS

  7. Hi,

    I do whatever you say, but I am getting an warning "log4cxx: Could not open file[MyLogConfig.xml]"

    It is he code

    int _tmain(int argc, _TCHAR* argv[])
    {
    // Load configuration file
    DOMConfigurator::configure("MyLogConfig.xml");

    // Loop something
    for(int i=0; i<10; ++i)
    {
    LOG4CXX_DEBUG(loggerToFile, _T("this is a debug message."));
    LOG4CXX_INFO (loggerToFile, _T("this is a info message, just ignore."));
    LOG4CXX_WARN (loggerToFile, _T("this is a warn message, dont worry too much."));
    LOG4CXX_ERROR(loggerToFile, _T("this is a error message, something serious is happening."));
    LOG4CXX_FATAL(loggerToFile, _T("this is a fatal message, crash and burn!!!"));
    MyFunctionA();

    Sleep(1000);
    printf("i = %dn", i);
    }
    getchar();
    return 0;
    }

  8. How do you turn logging off and on? I may have missed something obvious but I have been looking…

    Also, can you disable/enable specific loggers?
    And, can you disable/enable specific appenders?

    Thanks,
    Sam

  9. … To answer my second (and sort of first) question:

    Use OFF severity level.

    It seems you have to set this for all loggers. Setting root’s severity level to off does not have an effect on sub-loggers. Is there a better way to disable all logging?

    That still leaves my third question, disabling appenders…?

  10. This would really be helpful in the log4cxx wiki or something…

    Also, if you want your log file to go to a timestamped file, but you don’t want it to roll over, then you can add a manual triggering policy to the rolling::rollingfileappender

    &lt;triggeringPolicy class=&quot;org.apache.log4j.rolling.ManualTriggeringPolicy&quot; /&gt;

    Add that to your appender.

    • *ouch*… you’re right! The include statement are blank! I think the missing statement happen when I convert my blog engine from nucleusCMS to WordPress.

      But anyway, I’ve put the include statement back in : ) Thanks!

  11. I tried but get alot of link errors even though I am pretty sure the lib is specified correctly. Can you supply a Visual Studio 2008 project file with the correct setup as an example? :(

  12. Yes, I got it working. What I meant was with a malform xml, it just crashes which is not debuggable for someone unfamiliar with the xml format.

  13. Hi merc,

    1. Plase help to create only one backup file for log named as log.txt.old(No Index using %i or %d).The active file name is : log.txt

    2. Also After backuping, need to write a Header Information in each log file as follows,

    ******************************
    My Solution – TimeStamp
    ******************************

    How can i configure the xml file for the same?

  14. I think for that kind of custom behavior it’s best to make your own appender, if I remember correctly there is this AppenderSkeleton that you can inherit and craft your own appender behavior.

  15. Hi Merc,

    For the rollingfileappender, the param tag instead of . Can I dun specify any name for the log file? I realise a .1 file will be created if no value is specified. Anyway to prevent the .1 file from being created?

  16. Hey Brandon, unfortunately that behavior is coded directly into the RollingFileAppender itself, I guess if you need a specific behavior you could derive your own appender based on the RollingFileAppender and modify the rename to .1 behavior.

  17. how and wr i have to include the header files???

    where i will need that given binary build and what to do after that can any one tell me plsssss….thanks in advance

    I just followed the steps as given
    win32 empty project>(code)in some cpp file >added .xml file in same folder>build

    i am getting the error::

    fatal error C1083: Cannot open include file: ‘log4cxxlogger.h’: No such file or directory

  18. I am working with log4cxx with VS 2005.EVERYTHING went fine.The new test project built successfully but while running one runtime eception is coming…..

    DOMConfigurator::configure(“MyLogConfig.xml”);

    Thanks in advance
    Rakesh

  19. Hi all,

    i want to log into the ORACLE , MS Access , DB2 database but i am not able to fing appenders for the same can any one help me out please….any hint will be appriciated…Thanks in advance

Comments are closed.