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:

  1. org.apache.log4j.ConsoleAppender
  2. org.apache.log4j.FileAppender
  3. org.apache.log4j.rolling.RollingFileAppender
  4. org.apache.log4j.RollingFileAppender
  5. org.apache.log4j.net.SMTPAppender
  6. org.apache.log4j.jdbc.JDBCAppender
  7. 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…

Comments

  1. Henry Chow says:

    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. Henry Chow says:

    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,

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

    and I’m set. Thanks all.

  3. merc says:

    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. sanath says:

    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. sanath says:

    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. jetronic says:

    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. merc says:

    jetronic, did you have the config file setup correctly?

  8. 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 = %d\n", i);
    }
    getchar();
    return 0;
    }

  9. merc says:

    Yes…. You need to create a file name "MyLogConfig.xml" with your configuration inside… without it the log4cxx would not know what to do.

  10. Sam says:

    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

  11. Sam says:

    … 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…?

  12. Hi Merc,
    If I want to send email using Microsoft Exchange Server, what should I do?
    Regards
    Son

  13. jeffeb3 says:

    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

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

    Add that to your appender.

  14. merc says:

    @son: best way probably will be enable SMTP service on your exchange and use that to courier your mail.

    @jeffeb3: Thanks! : )

  15. blake says:

    Thanks!

  16. Nik says:

    In the program, why are the include statements blank?

  17. merc says:

    *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!

  18. Robin says:

    Does it run in vs2008? I’ve compiled and setup properly. I get a crash upon starting.

    I’ve stripped the xml to bare minimum

    –>

  19. merc says:

    yeah… this will work with vs2008. Crash upon starting, are you linking to a wrong lib or perhaps the wrong xml format?

  20. Adam says:

    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? :(

  21. merc says:

    How was your linking error looks like?

  22. Robin says:

    > yeah… this will work with vs2008. Crash upon starting, are you linking to a wrong lib or perhaps the wrong xml format?

    No, the xml is correct. I sent you an email via the contact page, but I think you did not get it. Anyway the xml is here

    http://cyberversion.com/private/config.zip

  23. merc says:

    Ah… you’re missing the closing element </log4j:configuration> at the end in your config file.

  24. Robin says:

    Oh man… it’s just crashes, it’s not debuggable. Thanks!

  25. merc says:

    I’ve download your config file and looked at it, it’s missing the closing element. Once I’ve added the closing element and put it with a test project it loaded just fine. Have a look at the test project with your config file in it: http://www.dreamcubes.com/webdrive/TestLog4cxx.rar

  26. Robin says:

    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.

  27. merc says:

    yeah that’s true :)

  28. Rajkumar V says:

    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?

  29. Rajkumar V says:

    Hi,

    One more query.

    How can i use both “Activefilename” and “filenamepattern” in SizeBasedRollingPloicy?

    Thanks advance.

  30. merc says:

    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.

  31. Brandon says:

    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?

  32. merc says:

    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.

  33. rakesh says:

    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: ‘log4cxx\logger.h’: No such file or directory

  34. rakesh says:

    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

  35. rakesh says:

    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