{"id":1213,"date":"2016-09-04T20:16:29","date_gmt":"2016-09-04T20:16:29","guid":{"rendered":"http:\/\/blog.paranoidprofessor.com\/?p=1213"},"modified":"2016-09-04T20:16:29","modified_gmt":"2016-09-04T20:16:29","slug":"better-logging-than-printf-using-log4j-part-iii","status":"publish","type":"post","link":"https:\/\/blog.paranoidprofessor.com\/index.php\/2016\/09\/04\/better-logging-than-printf-using-log4j-part-iii\/","title":{"rendered":"better logging than printf &#8211; using log4j &#8211; part III"},"content":{"rendered":"<p>Log4j is a very convenient tool to have in your programming arsenal. \u00a0This is true even if only the very basic functionality is being used. \u00a0 \u00a0The basic functionality of log4j2 was covered in my previous example.<\/p>\n<p>To be honest, the standard levels are truly good enough for most applications but the neat feature of version 2 is the ability to create custom output levels.<\/p>\n<p>In order to create some new levels we do need to know how the existing levels relate to each other.<\/p>\n<table class=\"w3-table-all\" style=\"height: 329px;\" width=\"392\">\n<tbody>\n<tr>\n<th>Standard Level<\/th>\n<th>Integer value<\/th>\n<\/tr>\n<tr>\n<td>OFF<\/td>\n<td>0<\/td>\n<\/tr>\n<tr>\n<td>FATAL<\/td>\n<td>100<\/td>\n<\/tr>\n<tr>\n<td>ERROR<\/td>\n<td>200<\/td>\n<\/tr>\n<tr>\n<td>WARN<\/td>\n<td>300<\/td>\n<\/tr>\n<tr>\n<td>INFO<\/td>\n<td>400<\/td>\n<\/tr>\n<tr>\n<td>DEBUG<\/td>\n<td>500<\/td>\n<\/tr>\n<tr>\n<td>TRACE<\/td>\n<td>600<\/td>\n<\/tr>\n<tr>\n<td>ALL<\/td>\n<td>Integer.MAX_VALUE<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>From looking at these levels there is quite a bit of space between each integer value for a new level to be slipped in. \u00a0These levels could be developer or site specific. \u00a0They could even be used to help track information for other internal needs (ie auditing).<\/p>\n<p>To provide an example of this, I will create two new levels, one being to generate a silly amount of debug information while the other would be to capture auditing information.<\/p>\n<h2>Custom Levels<\/h2>\n<p>Actually very little extra work is necessary to create new levels. \u00a0Simply use the new Level class to create a new level. \u00a0Simply decide where in the range of values the value should fit in.<\/p>\n<h3>Create new level<\/h3>\n<div class=\"sbody-code\">\n<pre><code>Level AUDIT = Level.forName(\"AUDIT\",350);\r\nLevel SPECIAL = Level.forName(\"SPECIAL\", 399);<\/code><\/pre>\n<\/div>\n<p>Apache also provides us with a new method on the Logger to call when using the new level. The method &#8220;log&#8221; accepts the new level as the first parameter followed by the data\u00a0to be logged.<\/p>\n<h3>Using new level<\/h3>\n<div class=\"sbody-code\">\n<pre><code>logfile.log(AUDIT, \"first auditing message\");\r\nlogfile.log(AUDIT, \"second auditing message\");<\/code><\/pre>\n<\/div>\n<p>There is really not a lot of special handling that is necessary when using these new levels but it is important that the new log levels are created before retrieving the logger from the LogManager.<\/p>\n<h2>Examining the output<\/h2>\n<p>The full program and configuration file listing is at the bottom. \u00a0What is\u00a0more interesting is looking at some of the output that would be generated by modifying the log4j2.xml file especially when we change the root level.<\/p>\n<p>The most basic setting\u00a0is to just use the normal info level.<\/p>\n<blockquote><p>&lt;Root level=&#8221;info&#8221;&gt;<br \/>\n&lt;AppenderRef ref=&#8221;STDOUT&#8221; \/&gt;<br \/>\n&lt;AppenderRef ref=&#8221;FILE&#8221; \/&gt;<br \/>\n&lt;\/Root&gt;<\/p><\/blockquote>\n<div class=\"sbody-code\">\n<p>We can see in our log output that one of our new debug levels, AUDIT, is now showing up in the middle of our log output. \u00a0This might not be appropriate for real auditing but might be useful for some sort of diagnostics for special hardware or special situations.<\/p>\n<\/div>\n<div class=\"sbody-code\">\n<pre><code>2016-09-02 21:22:56 | INFO  | utility.CustomVersion (CustomVersion.java:18) - Program starting at \r\n2016-09-02 21:22:56 | AUDIT | utility.CustomVersion (CustomVersion.java:21) - first auditing message\r\n2016-09-02 21:22:56 | AUDIT | utility.CustomVersion (CustomVersion.java:22) - second auditing message\r\n2016-09-02 21:22:56 | INFO  | utility.CustomVersion (CustomVersion.java:28) - 1 x 1 = 1\r\n2016-09-02 21:22:56 | INFO  | utility.CustomVersion (CustomVersion.java:28) - 2 x 2 = 4\r\n2016-09-02 21:22:56 | INFO  | utility.CustomVersion (CustomVersion.java:28) - 3 x 3 = 9\r\n2016-09-02 21:22:56 | AUDIT | utility.CustomMathV (CustomMathV.java:30) - extremely large numbers being used in math - value = '4'\r\n2016-09-02 21:22:56 | INFO  | utility.CustomVersion (CustomVersion.java:28) - 4 x 4 = 16\r\n2016-09-02 21:22:56 | AUDIT | utility.CustomMathV (CustomMathV.java:30) - extremely large numbers being used in math - value = '5'\r\n2016-09-02 21:22:56 | INFO  | utility.CustomVersion (CustomVersion.java:28) - 5 x 5 = 25\r\n2016-09-02 21:22:56 | INFO  | utility.CustomVersion (CustomVersion.java:40) - Program finishing at \r\n<\/code><\/pre>\n<\/div>\n<p>The most interesting change to the output was when setting the level in our configuration file to our new custom level &#8211; &#8220;audit&#8221;. \u00a0This actually performed as a filter for that level and only that level in the the output.<\/p>\n<p>This was pretty neat as by default log4j2 doesn&#8217;t know anything about our new level when it reads in the configuration file.<\/p>\n<blockquote><p>&lt;Root level=&#8221;audit&#8221;&gt;<br \/>\n&lt;AppenderRef ref=&#8221;STDOUT&#8221; \/&gt;<br \/>\n&lt;AppenderRef ref=&#8221;FILE&#8221; \/&gt;<br \/>\n&lt;\/Root&gt;<\/p><\/blockquote>\n<div class=\"sbody-code\">\n<pre><code>2016-09-02 21:24:23 | AUDIT | utility.CustomVersion (CustomVersion.java:21) - first auditing message\r\n2016-09-02 21:24:23 | AUDIT | utility.CustomVersion (CustomVersion.java:22) - second auditing message\r\n2016-09-02 21:24:23 | AUDIT | utility.CustomMathV (CustomMathV.java:30) - extremely large numbers being used in math - value = '4'\r\n2016-09-02 21:24:23 | AUDIT | utility.CustomMathV (CustomMathV.java:30) - extremely large numbers being used in math - value = '5'\r\n<\/code><\/pre>\n<\/div>\n<p>Finally when changing the filter level to all we will see all log output including our two new levels. \u00a0You can see both AUDIT and the SILLYDEBUG levels as expected.<\/p>\n<blockquote><p>&lt;Root level=&#8221;all&#8221;&gt;<br \/>\n&lt;AppenderRef ref=&#8221;STDOUT&#8221; \/&gt;<br \/>\n&lt;AppenderRef ref=&#8221;FILE&#8221; \/&gt;<br \/>\n&lt;\/Root&gt;<\/p><\/blockquote>\n<div class=\"sbody-code\">\n<pre><code>2016-09-02 21:25:09 | INFO  | utility.CustomVersion (CustomVersion.java:18) - Program starting at                                                                                                                                                                              \r\n2016-09-02 21:25:09 | TRACE | utility.CustomVersion (CustomVersion.java:19) - add a trace to log output                                                                                                                                                                        \r\n2016-09-02 21:25:09 | AUDIT | utility.CustomVersion (CustomVersion.java:21) - first auditing message                                                                                                                                                                           \r\n2016-09-02 21:25:09 | AUDIT | utility.CustomVersion (CustomVersion.java:22) - second auditing message                                                                                                                                                                          \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:20) - entering square                                                                                                                                                                                 \r\n2016-09-02 21:25:09 | DEBUG | utility.CustomMathV (CustomMathV.java:21) - parameter is 1                                                                                                                                                                                       \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:23) - mathmatics of squared using 1                                                                                                                                                                   \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:37) - exiting square                                                                                                                                                                                  \r\n2016-09-02 21:25:09 | INFO  | utility.CustomVersion (CustomVersion.java:28) - 1 x 1 = 1                                                                                                                                                                                        \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:20) - entering square                                                                                                                                                                                 \r\n2016-09-02 21:25:09 | DEBUG | utility.CustomMathV (CustomMathV.java:21) - parameter is 2                                                                                                                                                                                       \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:23) - mathmatics of squared using 2                                                                                                                                                                   \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:37) - exiting square                                                                                                                                                                                  \r\n2016-09-02 21:25:09 | INFO  | utility.CustomVersion (CustomVersion.java:28) - 2 x 2 = 4                                                                                                                                                                                        \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:20) - entering square                                                                                                                                                                                 \r\n2016-09-02 21:25:09 | DEBUG | utility.CustomMathV (CustomMathV.java:21) - parameter is 3                                                                                                                                                                                       \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:23) - mathmatics of squared using 3                                                                                                                                                                   \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:37) - exiting square                                                                                                                                                                                  \r\n2016-09-02 21:25:09 | INFO  | utility.CustomVersion (CustomVersion.java:28) - 3 x 3 = 9                                                                                                                                                                                        \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:20) - entering square                                                                                                                                                                                 \r\n2016-09-02 21:25:09 | DEBUG | utility.CustomMathV (CustomMathV.java:21) - parameter is 4                                                                                                                                                                                       \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:23) - mathmatics of squared using 4                                                                                                                                                                   \r\n2016-09-02 21:25:09 | AUDIT | utility.CustomMathV (CustomMathV.java:30) - extremely large numbers being used in math - value = '4'                                                                                                                                             \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:37) - exiting square                                                                                                                                                                                  \r\n2016-09-02 21:25:09 | INFO  | utility.CustomVersion (CustomVersion.java:28) - 4 x 4 = 16                                                                                                                                                                                       \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:20) - entering square                                                                                                                                                                                 \r\n2016-09-02 21:25:09 | DEBUG | utility.CustomMathV (CustomMathV.java:21) - parameter is 5                                                                                                                                                                                       \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:23) - mathmatics of squared using 5                                                                                                                                                                   \r\n2016-09-02 21:25:09 | AUDIT | utility.CustomMathV (CustomMathV.java:30) - extremely large numbers being used in math - value = '5'                                                                                                                                             \r\n2016-09-02 21:25:09 | SILLYDEBUG | utility.CustomMathV (CustomMathV.java:37) - exiting square                                                                                                                                                                                  \r\n2016-09-02 21:25:09 | INFO  | utility.CustomVersion (CustomVersion.java:28) - 5 x 5 = 25                                                                                                                                                                                       \r\n2016-09-02 21:25:09 | DEBUG | utility.CustomVersion (CustomVersion.java:39) - add some additional debug to output                                                                                                                                                              \r\n2016-09-02 21:25:09 | INFO  | utility.CustomVersion (CustomVersion.java:40) - Program finishing at                                                                                                                                                                             <\/code><\/pre>\n<\/div>\n<h2>Other discoveries<\/h2>\n<p>One rather interesting thing about these custom levels was how forgiving log4j2 seemed to be of levels that did not exist.<\/p>\n<p>During my testing I accidentally created an unknown level due to a typo only to discover that that message was silently ignored.\u00a0If I then created the level prior to it being used, the level functioned as exactly as it should. \u00a0It is not clear if this is a bug or a feature.<\/p>\n<p>I made another interesting discovery while fooling around with Apache\u00a0Ant. \u00a0It was surprising not to see the file and line number in my log output. \u00a0It took a few minutes of head scratching to realize that the program was not compiled with debugging on.<\/p>\n<div class=\"sbody-code\">\n<pre><code>DEBUG | 2016-08-30 23:08:56 | [main] de.companyname.helloworld.HelloWorld (null:-1) - HelloWorld starting\r\nINFO  | 2016-08-30 23:08:56 | [main] de.companyname.helloworld.HelloWorld (null:-1) - Hello World!\r\nDEBUG | 2016-08-30 23:08:56 | [main] de.companyname.helloworld.HelloWorld (null:-1) - HelloWorld finishing\r\n<\/code><\/pre>\n<\/div>\n<p>The solution was easy enough. \u00a0Simply change the ant script to turn on debugging while compiling and recompile\u00a0the\u00a0program.<\/p>\n<h2>Test program<\/h2>\n<p><strong>CustomProgram.java<\/strong><\/p>\n<div class=\"sbody-code\">\n<pre><code>package de.companyname.utility;\r\n\r\n\r\nimport org.apache.logging.log4j.Level;\r\nimport org.apache.logging.log4j.LogManager;\r\nimport org.apache.logging.log4j.Logger;\r\n\r\n\r\npublic class CustomVersion {\r\n\r\n\tstatic Level AUDIT = Level.forName(\"AUDIT\",350);\r\n\tstatic Level SPECIAL = Level.forName(\"SPECIAL\", 399);\r\n\t\r\n\tpublic static void main(String[] args) \r\n\t{\r\n\t\tLogger logfile = LogManager.getLogger(CustomVersion.class.getName());\r\n\t\t\r\n\t\tlogfile.info(\"Program starting at \" + \"\");\r\n\t\tlogfile.trace(\"add a trace to log output\");\r\n\t\t\r\n\t\tlogfile.log(AUDIT, \"first auditing message\");\r\n\t\tlogfile.log(AUDIT, \"second auditing message\");\r\n\t\t\r\n\t\tfor (int index = 1; index &lt;= 5; index++)\r\n\t\t{\r\n\t\t\ttry {\r\n\t\t\t\tint square = de.companyname.utility.CustomMathV.square(index);\r\n\t\t\t\tlogfile.info(index + \" x \" + index + \" = \" + square);\r\n\t\t\t}\r\n\t\t\tcatch (Exception ex)\r\n\t\t\t{\r\n\t\t\t\tlogfile.fatal(\" while processing value \" + index);\r\n\t\t\t\tlogfile.fatal(ex.getMessage());\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tlogfile.log(Level.getLevel(\"UNDEFINED\"), \"some xxxx message\"); \r\n\r\n\t\tlogfile.debug(\"add some additional debug to output\");\r\n\t\tlogfile.info(\"Program finishing at \" + \"\");\r\n\t}\r\n}<\/code><\/pre>\n<\/div>\n<p><b>CustomMathV.java<\/b><\/p>\n<div class=\"sbody-code\">\n<pre><code>package de.companyname.utility;\r\n\r\n\r\nimport org.apache.logging.log4j.Level;\r\nimport org.apache.logging.log4j.LogManager;\r\nimport org.apache.logging.log4j.Logger;\r\n\r\npublic class CustomMathV {\r\n\r\n\t\/*\r\n\t * will return the square of the value input.\r\n\t * this method will only be for the range of positive numbers.\r\n\t *\/\r\n\tstatic public int square (int value) throws Exception\r\n\t{\r\n\t\tLevel AUDIT = Level.forName(\"AUDIT\", 350);\r\n\t\tLevel SILLYDEBUG = Level.forName(\"SILLYDEBUG\", 9999); \r\n\t\tLogger logfile = LogManager.getLogger(CustomMathV.class.getName());\r\n\t\t\r\n\t\tlogfile.log(SILLYDEBUG, \"entering square\");\r\n\t\tlogfile.debug(\"parameter is \" + value);\r\n\t\t\r\n\t\tlogfile.log(SILLYDEBUG,  \"mathmatics of squared using \" + value);\r\n\t\tint retval = 0;\r\n\t\t\r\n\t\tif (value &gt; 0)\r\n\t\t{\r\n\t\t\tretval = value * value;\r\n\t\t\tif (value &gt;= 4)\r\n\t\t\t\tlogfile.log(AUDIT,\"extremely large numbers being used in math - value = '\" + value + \"'\");\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tlogfile.debug(\"invalid value of \" + value + \" given\");\r\n\t\t}\r\n\t\t\r\n\t\tlogfile.log(SILLYDEBUG, \"exiting square\");\r\n\t\treturn retval;\r\n\t}\r\n}<\/code><\/pre>\n<\/div>\n<p><b>log4j2-custom.xml<\/b><\/p>\n<div class=\"sbody-code\">\n<pre><code>&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n&lt;Configuration xmlns=\"http:\/\/logging.apache.org\/log4j\/2.0\/config\"&gt;\r\n\r\n\t&lt;Properties&gt;\r\n\t\t&lt;Property name=\"LOG_NAME\"&gt;logfile.log&lt;\/Property&gt;\r\n\t\t&lt;Property name=\"FILEPATTERN\"&gt;%-5p | %d{yyyy-MM-dd HH:mm:ss} | [%t] %C (%F:%L) - %m%n&lt;\/Property&gt;\r\n\t\t&lt;Property name=\"PATTERN\"&gt;%d{yyyy-MM-dd HH:mm:ss} | %-5p | %C{2} (%F:%L) - %m%n&lt;\/Property&gt;\r\n\t&lt;\/Properties&gt;\r\n\r\n\t&lt;Appenders&gt;\r\n\t\t&lt;File name=\"FILE\" fileName=\"${LOG_NAME}\" append=\"true\"&gt;\r\n\t\t\t&lt;PatternLayout pattern=\"${FILEPATTERN}\"\/&gt;\r\n\t\t&lt;\/File&gt;\r\n\t\t&lt;Console name=\"STDOUT\" target=\"SYSTEM_OUT\"&gt;\r\n\t\t\t&lt;PatternLayout pattern=\"${PATTERN}\"\/&gt;\r\n\t\t&lt;\/Console&gt;\r\n\t&lt;\/Appenders&gt;\r\n\r\n\t&lt;Loggers&gt;\r\n\t\t&lt;!-- add this line to get debug output for this particular class --&gt;\r\n\t\t&lt;Logger name=\"dex.companyname.utility.Mymath\" level=\"debug\"\/&gt;\r\n\t\t&lt;Logger name=\"dex.companyname.utility.CustomMath\" level=\"debug\"\/&gt;\r\n\r\n\t\t&lt;Root level=\"info\"&gt;\r\n\t\t\t&lt;AppenderRef ref=\"STDOUT\" \/&gt;\r\n\t\t\t&lt;AppenderRef ref=\"FILE\" \/&gt;\r\n\t\t&lt;\/Root&gt;\r\n\t&lt;\/Loggers&gt;\r\n\r\n&lt;\/Configuration&gt;\r\n<\/code><\/pre>\n<\/div>\n<p><strong>runProgram.sh<\/strong><\/p>\n<div class=\"sbody-code\">\n<pre><code>#!\/bin\/bash\r\n\r\nVMARGS=\"-Dlog4j.configurationFile=resources\/log4j2-custom.xml \"\r\nCP=\/tmp\/version3\/lib\/log4j-api-2.6.2.jar:\/tmp\/version3\/lib\/log4j-core-2.6.2.jar:.\r\nPGM=de.companyname.utility.CustomVersion\r\n\r\nCMD=\"java -cp $CP $VMARGS $PGM\"\r\n\r\necho $CMD\r\n$CMD\r\n<\/code><\/pre>\n<\/div>\n<h3>References<br \/>\n<a href=\"http:\/\/logging.apache.org\/log4j\/2.x\/manual\/customloglevels.html\">http:\/\/logging.apache.org\/log4j\/2.x\/manual\/customloglevels.html<\/a><\/h3>\n<p>Note: The\u00a0example was compiled with java 1.8 and uses log4j-api-2.6.2.jar and log4j-core-2.6.2.jar<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Log4j is a very convenient tool to have in your programming arsenal. \u00a0This is true even if only the very basic functionality is being used. \u00a0 \u00a0The basic functionality of log4j2 was covered in my previous example. To be honest, &hellip; <a href=\"https:\/\/blog.paranoidprofessor.com\/index.php\/2016\/09\/04\/better-logging-than-printf-using-log4j-part-iii\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[20],"tags":[73,12,78],"_links":{"self":[{"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/posts\/1213"}],"collection":[{"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/comments?post=1213"}],"version-history":[{"count":3,"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/posts\/1213\/revisions"}],"predecessor-version":[{"id":1216,"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/posts\/1213\/revisions\/1216"}],"wp:attachment":[{"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/media?parent=1213"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/categories?post=1213"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/tags?post=1213"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}