Sunday, 13 November 2011

Runtime log4j configuration

Recently I got struck with the question what log level the rootLogger in log4j was set to.
Normally a quick check of the log4j's property file would give you the answer, but in this case there was a third party application that had multiple ways of defining the log level both at startup and then runtime.

After a quick search I found this blog post: http://nelz.net/2008/04/08/log4j-runtime-configuration/
This will not only show you the runtime value of the loggers but also enable you to change this without require a restart of the application.

In order to rename log4jAdmin.jsp, grab it from the blog and perform the following steps:
Find: "/log4jAdmin.jsp
Replace with: request.getContextPath() + request.getServletPath() + "
Find: log4jAdmin.jsp
Replace with: <%=request.getContextPath() + request.getServletPath() %>

Screenshot:

The JSP with the customisations:
  1. <%@ page language="java" contentType="text/html;charset=UTF-8" %>
  2. <%@ page import="org.apache.log4j.Level" %>
  3. <%@ page import="org.apache.log4j.LogManager" %>
  4. <%@ page import="org.apache.log4j.Logger" %>
  5. <%@ page import="java.util.HashMap" %>
  6. <%@ page import="java.util.Enumeration" %>
  7. <%@ page import="java.util.Set" %>
  8. <%@ page import="java.util.Arrays" %>
  9. <% long beginPageLoadTime = System.currentTimeMillis();%>
  10.  
  11. <html>
  12. <head>
  13.     <title>Log4J Administration</title>
  14.     <style type="text/css">
  15.         <!--
  16.         #content {
  17.             margin: 0px;
  18.             padding: 0px;
  19.             text-align: center;
  20.             background-color: #ccc;
  21.             border: 1px solid #000;
  22.             width: 100%;
  23.  
  24.         }
  25.  
  26.         body {
  27.             position: relative;
  28.             margin: 10px;
  29.             padding: 0px;
  30.             color: #333;
  31.         }
  32.  
  33.         h1 {
  34.             margin-top: 20px;
  35.             font: 1.5em Verdana, Arial, Helvetica sans-serif;
  36.         }
  37.  
  38.         h2 {
  39.             margin-top: 10px;
  40.             font: 0.75em Verdana, Arial, Helvetica sans-serif;
  41.             text-align: left;
  42.         }
  43.  
  44.         a, a:link, a:visited, a:active {
  45.             color: red;
  46.             text-decoration: none;
  47.             text-transform: uppercase;
  48.         }
  49.  
  50.         table {
  51.             width: 100%;
  52.             background-color: #000;
  53.             padding: 3px;
  54.             border: 0px;
  55.         }
  56.  
  57.         th {
  58.             font-size: 0.75em;
  59.             background-color: #ccc;
  60.             color: #000;
  61.             padding-left: 5px;
  62.             text-align: center;
  63.             border: 1px solid #ccc;
  64.             white-space: nowrap;
  65.         }
  66.  
  67.         td {
  68.             font-size: 0.75em;
  69.             background-color: #fff;
  70.             white-space: nowrap;
  71.         }
  72.  
  73.         td.center {
  74.             font-size: 0.75em;
  75.             background-color: #fff;
  76.             text-align: center;
  77.             white-space: nowrap;
  78.         }
  79.  
  80.         .filterForm {
  81.             font-size: 0.9em;
  82.             background-color: #000;
  83.             color: #fff;
  84.             padding-left: 5px;
  85.             text-align: left;
  86.             border: 1px solid #000;
  87.             white-space: nowrap;
  88.         }
  89.  
  90.         .filterText {
  91.             font-size: 0.75em;
  92.             background-color: #fff;
  93.             color: #000;
  94.             text-align: left;
  95.             border: 1px solid #ccc;
  96.             white-space: nowrap;
  97.         }
  98.  
  99.         .filterButton {
  100.             font-size: 0.75em;
  101.             background-color: #000;
  102.             color: #fff;
  103.             padding-left: 5px;
  104.             padding-right: 5px;
  105.             text-align: center;
  106.             border: 1px solid #ccc;
  107.             width: 100px;
  108.             white-space: nowrap;
  109.         }
  110.  
  111.         -->
  112.     </style>
  113. </head>
  114. <body onLoad="javascript:document.logFilterForm.logNameFilter.focus();">
  115.  
  116. <%
  117.     String containsFilter = "Contains";
  118.     String beginsWithFilter = "Begins With";
  119.     String[] logLevels = {"debug", "info", "warn", "error", "fatal", "off"};
  120.     String targetOperation = (String) request.getParameter("operation");
  121.     String targetLogger = (String) request.getParameter("logger");
  122.     String targetLogLevel = (String) request.getParameter("newLogLevel");
  123.     String logNameFilter = (String) request.getParameter("logNameFilter");
  124.     String logNameFilterType = (String) request.getParameter("logNameFilterType");
  125. %>
  126. <div id="content">
  127. <h1>Log4J Administration</h1>
  128.  
  129. <div class="filterForm">
  130.  
  131.     <form action="<%=request.getContextPath() + request.getServletPath() %>" name="logFilterForm">
  132.        Filter Loggers:&nbsp;&nbsp;
  133.         <input name="logNameFilter" type="text" size="50" value="<%=(logNameFilter == null ? "":logNameFilter)%>"
  134.                class="filterText"/>
  135.         <input name="logNameFilterType" type="submit" value="<%=beginsWithFilter%>" class="filterButton"/>&nbsp;
  136.         <input name="logNameFilterType" type="submit" value="<%=containsFilter%>" class="filterButton"/>&nbsp;
  137.         <input name="logNameClear" type="button" value="Clear" class="filterButton"
  138.                onmousedown='javascript:document.logFilterForm.logNameFilter.value="";'/>
  139.         <input name="logNameReset" type="reset" value="Reset" class="filterButton"/>
  140.         <param name="operation" value="changeLogLevel"/>
  141.     </form>
  142. </div>
  143.  
  144. <table cellspacing="1">
  145.     <tr>
  146.         <th width="25%">Logger</th>
  147.         <th width="25%">Parent Logger</th>
  148.         <th width="15%">Effective Level</th>
  149.         <th width="35%">Change Log Level To</th>
  150.     </tr>
  151.  
  152.     <%
  153.         Enumeration loggers = LogManager.getCurrentLoggers();
  154.         HashMap loggersMap = new HashMap(128);
  155.         Logger rootLogger = LogManager.getRootLogger();
  156.  
  157.         if (!loggersMap.containsKey(rootLogger.getName())) {
  158.             loggersMap.put(rootLogger.getName(), rootLogger);
  159.         }
  160.  
  161.         while (loggers.hasMoreElements()) {
  162.             Logger logger = (Logger) loggers.nextElement();
  163.             if (logNameFilter == null || logNameFilter.trim().length() == 0) {
  164.                 loggersMap.put(logger.getName(), logger);
  165.             } else if (containsFilter.equals(logNameFilterType)) {
  166.                 if (logger.getName().toUpperCase().indexOf(logNameFilter.toUpperCase()) >= 0) {
  167.                     loggersMap.put(logger.getName(), logger);
  168.                 }
  169.             } else {
  170. // Either was no filter in IF, contains filter in ELSE IF, or begins with in ELSE
  171.                 if (logger.getName().startsWith(logNameFilter)) {
  172.  
  173.                     loggersMap.put(logger.getName(), logger);
  174.                 }
  175.             }
  176.         }
  177.         Set loggerKeys = loggersMap.keySet();
  178.         String[] keys = new String[loggerKeys.size()];
  179.         keys = (String[]) loggerKeys.toArray(keys);
  180.         Arrays.sort(keys, String.CASE_INSENSITIVE_ORDER);
  181.         for (int i = 0; i < keys.length; i++) {
  182.             Logger logger = (Logger) loggersMap.get(keys[i]);
  183.  
  184. // MUST CHANGE THE LOG LEVEL ON LOGGER BEFORE GENERATING THE LINKS AND THE
  185. // CURRENT LOG LEVEL OR DISABLED LINK WON'T MATCH THE NEWLY CHANGED VALUES
  186.             if ("changeLogLevel".equals(targetOperation) && targetLogger.equals(logger.getName())) {
  187.                 Logger selectedLogger = (Logger) loggersMap.get(targetLogger);
  188.                 selectedLogger.setLevel(Level.toLevel(targetLogLevel));
  189.             }
  190.  
  191.             String loggerName = null;
  192.             String loggerEffectiveLevel = null;
  193.             String loggerParent = null;
  194.             if (logger != null) {
  195.                 loggerName = logger.getName();
  196.                 loggerEffectiveLevel = String.valueOf(logger.getEffectiveLevel());
  197.                 loggerParent = (logger.getParent() == null ? null : logger.getParent().getName());
  198.             }
  199.     %>
  200.     <tr>
  201.         <td><%=loggerName%></td>
  202.         <td><%=loggerParent%></td>
  203.         <td><%=loggerEffectiveLevel%></td>
  204.         <td class="center">
  205.             <%
  206.                 for (int cnt = 0; cnt < logLevels.length; cnt++) {
  207.  
  208.                     String url = request.getContextPath() + request.getServletPath() +
  209.                         "?operation=changeLogLevel&logger=" + loggerName + "&newLogLevel=" + logLevels[cnt] +
  210.                         "&logNameFilter=" + (logNameFilter != null ? logNameFilter : "") +
  211.                         "&logNameFilterType=" + (logNameFilterType != null ? logNameFilterType : "");
  212.  
  213.                     if (logger.getLevel() == Level.toLevel(logLevels[cnt]) ||
  214.                         logger.getEffectiveLevel() == Level.toLevel(logLevels[cnt])) {
  215.  
  216.             %>
  217.             [<%=logLevels[cnt].toUpperCase()%>]
  218.             <%
  219.             } else {
  220.             %>
  221.             <a href='<%=url%>'>[<%=logLevels[cnt]%>]</a>&nbsp;
  222.             <%
  223.                     }
  224.                 }
  225.             %>
  226.         </td>
  227.     </tr>
  228.     <%
  229.         }
  230.     %>
  231. </table>
  232. <h2>
  233.     Revision: 1.0<br/>
  234.     Page Load Time (Millis): <%=(System.currentTimeMillis() - beginPageLoadTime)%>
  235.  
  236. </h2>
  237. </div>
  238. </body>
  239. </html>