Dynamic/Runtime Log level configuration

There is often a need to modify/change the log level of your (J2EE) application or parts of your application, without a server restart. These may be done for various purposes, debugging being one very obvious need.

Here’s a simple way to achieve the same, wherein we once you have deployed the EAR (if your WAR is wrapped within it) or WAR, you can simply place a JSP file at the root folder of your WAR file, and viola!, you have a custom log level configuration console.

In a typical JBOSS installation this would be placed under :   <jboss home>\server\<profile>\tmp\deploy\<EAR – if wrapped within>\<WAR>

In a typical WAS installation this would be placed under : <WebSphere Installation Directory>\AppServer\profiles\<profile>\installedApps\<node name>\<EAR – if wrapped within>\<WAR>

Of course, you should have configured the log4j in your application properly.

Here is the configureLogging.jsp:


<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@ page import="org.apache.log4j.Level"%>
<%@ page import="org.apache.log4j.LogManager"%>
<%@ page import="org.apache.log4j.Logger"%>
<%@ page import="java.util.HashMap"%>
<%@ page import="java.util.Enumeration"%>
<%@ page import="java.util.Set"%>
<%@ page import="java.util.Arrays"%>
<html>
<head>
<title>Log Level Configuration</title>
<style type="text/css">
<!--
#content {
    margin: 0px;
    padding: 0px;
    text-align: center;
    background-color: green;
    border: 1px solid #000;
    width: 100%;
}

body {
    position: relative;
    margin: 10px;
    padding: 0px;
    color: #333;
}

h1 {
    margin-top: 20px;
    font: 1.5em Verdana, Arial, Helvetica sans-serif;
}

h2 {
    margin-top: 10px;
    font: 0.75em Verdana, Arial, Helvetica sans-serif;
    text-align: left;
}

a,a:link,a:visited,a:active {
    color: blue;
    text-decoration: none;
    text-transform: uppercase;
}

table {
    width: 100%;
    background-color: #000;
    padding: 3px;
    border: 0px;
}

th {
    font-size: 0.75em;
    background-color: #eee;
    color: #000;
    padding-left: 5px;
    text-align: center;
    border: 1px solid #eee;
    white-space: nowrap;
}

td {
    font-size: 0.75em;
    background-color: #fff;
    white-space: nowrap;
}

td.center {
    font-size: 0.75em;
    background-color: #fff;
    text-align: center;
    white-space: nowrap;
}

.filterForm {
    font-size: 0.9em;
    background-color: #000;
    color: #fff;
    padding-left: 5px;
    text-align: left;
    border: 1px solid #000;
    white-space: nowrap;
}

.filterText {
    font-size: 0.75em;
    background-color: #ccc;
    color: #000;
    text-align: left;
    border: 1px solid #ccc;
    white-space: nowrap;
}

.filterButton {
    font-size: 0.75em;
    background-color: brown;
    color: white;
    padding-left: 5px;
    padding-right: 5px;
    text-align: center;
    border: 1px solid #ccc;
    width: 100px;
    white-space: nowrap;
}
–>
</style>
</head>
<body onLoad=”javascript:document.logFilterForm.logNameFilter.focus();”>

    <%
        String containsFilter = “Contains”;
        String beginsWithFilter = “Begins With”;

        String[] logLevels = { “debug”, “info”, “warn”, “error”, “fatal”,
                “off” };
        String targetOperation = (String) request.getParameter(“operation”);
        String targetLogger = (String) request.getParameter(“logger”);
        String targetLogLevel = (String) request
                .getParameter(“newLogLevel”);
        String logNameFilter = (String) request
                .getParameter(“logNameFilter”);
        String logNameFilterType = (String) request
                .getParameter(“logNameFilterType”);
    %>
    <div id=”content”>
        <h1>Log Level Configuration</h1>

        <div>

            <form action=”configureLogging.jsp” name=”logFilterForm”>
                Filter Loggers:&nbsp;&nbsp; <input name=”logNameFilter” type=”text”
                    size=”50″ value=”<%=(logNameFilter == null ? “” : logNameFilter)%>”
                    class=”filterText” /> <input name=”logNameFilterType” type=”submit”
                    value=”<%=beginsWithFilter%>” />&nbsp; <input
                    name=”logNameFilterType” type=”submit” value=”<%=containsFilter%>”
                    class=”filterButton” />&nbsp; <input name=”logNameClear”
                    type=”button” value=”Clear”
                    onmousedown=’javascript:document.logFilterForm.logNameFilter.value=””;’ />
                <input name=”logNameReset” type=”reset” value=”Reset”
                    class=”filterButton” />

                <param name=”operation” value=”changeLogLevel” />
            </form>
        </div>

        <table cellspacing=”1″>
            <tr>
                <th width=”25%”>Logger</th>

                <th width=”25%”>Parent Logger</th>
                <th width=”15%”>Current Level</th>

                <th width=”35%”>Change Log Level To</th>
            </tr>

            <%
                Enumeration loggers = LogManager.getCurrentLoggers();

                HashMap loggersMap = new HashMap(128);
                Logger rootLogger = LogManager.getRootLogger();

                if (!loggersMap.containsKey(rootLogger.getName())) {

                    loggersMap.put(rootLogger.getName(), rootLogger);
                }

                while (loggers.hasMoreElements()) {
                    Logger logger = (Logger) loggers.nextElement();

                    if (logNameFilter == null || logNameFilter.trim().length() == 0) {

                        loggersMap.put(logger.getName(), logger);
                    } else if (containsFilter.equals(logNameFilterType)) {

                        if (logger.getName().toUpperCase()
                                .indexOf(logNameFilter.toUpperCase()) >= 0) {

                            loggersMap.put(logger.getName(), logger);
                        }

                    } else {
                        // Either was no filter in IF, contains filter in ELSE IF, or begins with in ELSE
                        if (logger.getName().startsWith(logNameFilter)) {

                            loggersMap.put(logger.getName(), logger);
                        }

                    }
                }
                Set loggerKeys = loggersMap.keySet();

                String[] keys = new String[loggerKeys.size()];

                keys = (String[]) loggerKeys.toArray(keys);

                Arrays.sort(keys, String.CASE_INSENSITIVE_ORDER);
                for (int i = 0; i < keys.length; i++) {

                    Logger logger = (Logger) loggersMap.get(keys[i]);

                    // MUST CHANGE THE LOG LEVEL ON LOGGER BEFORE GENERATING THE LINKS AND THE
                    // CURRENT LOG LEVEL OR DISABLED LINK WON’T MATCH THE NEWLY CHANGED VALUES
                    if (“changeLogLevel”.equals(targetOperation)
                            && targetLogger.equals(logger.getName())) {

                        Logger selectedLogger = (Logger) loggersMap
                                .get(targetLogger);

                        selectedLogger.setLevel(Level.toLevel(targetLogLevel));
                    }

                    String loggerName = null;
                    String loggerEffectiveLevel = null;
                    String loggerParent = null;
                    if (logger != null) {
                        loggerName = logger.getName();
                        loggerEffectiveLevel = String.valueOf(logger
                                .getEffectiveLevel());
                        loggerParent = (logger.getParent() == null ? null : logger
                                .getParent().getName());

                    }
            %>
            <tr>
                <td><%=loggerName%></td>

                <td><%=loggerParent%></td>
                <td><%=loggerEffectiveLevel%></td>
                <td>
                    <%
                        for (int cnt = 0; cnt < logLevels.length; cnt++) {

                                String url = “configureLogging.jsp?operation=changeLogLevel&logger=”
                                        + loggerName
                                        + “&newLogLevel=”
                                        + logLevels[cnt]
                                        + “&logNameFilter=”
                                        + (logNameFilter != null ? logNameFilter : “”)
                                        + “&logNameFilterType=”
                                        + (logNameFilterType != null ? logNameFilterType
                                                : “”);

                                if (logger.getLevel() == Level.toLevel(logLevels[cnt])
                                        || logger.getEffectiveLevel() == Level
                                                .toLevel(logLevels[cnt])) {
                    %> [<%=logLevels[cnt].toUpperCase()%>] <%
                        } else {
                    %> <a href='<%=url%>’>[<%=logLevels[cnt]%>]</a>&nbsp; <%
     }
         }
 %>
                </td>
            </tr>

            <%
                }
            %>
        </table>
    </div>
</body>
</html>

Once you copy this JSP under root of your web app, you can access it by entering http://[host]:[port]/[context root]/configureLogging.jsp

You should see something like this on your screen: