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 : \server\\tmp\deploy\\

In a typical WAS installation this would be placed under : \AppServer\profiles\\installedApps\\\

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

git revert to commit

irst, it’s always worth noting that git reset –hard is a potentially dangerous command, since it throws away all your uncommitted changes. For safety, you should always check that the output of git status is clean (that is, empty) before using it.

Initially you say the following:

So I know that Git tracks changes I make to my application, and it holds on to them until I commit the changes, but here’s where I’m hung up:

In case this reveals a mistaken assumption, I should say that this isn’t correct. Git only records the state of the files when you stage them (with git add) or when you create a commit. Once you’ve created a commit which has your project files in a particular state, they’re very safe, but until then Git’s not really “tracking changes” to your files. (for example, even if you do git add to stage a new version of the file, that overwrites the previously staged version of that file in the staging area.)

In your question you then go on to ask the following:

When I want to revert to a previous commit I use: git reset –hard HEAD And git returns: HEAD is now at 820f417 micro

How do I then revert the files on my hard drive back to that previous commit?

If you do git reset --hard then Git will:

Make your current branch (typically master) back to point at .
Then make the files in your working tree and the index (“staging area”) the same as the versions committed in .
HEAD points to your current branch (or current commit), so all that git reset --hard HEAD will do is to throw away any uncommitted changes you have.

So, suppose the good commit that you want to go back to is f414f31. (You can find that via git log or any history browser.) You then have a few different options depending on exactly what you want to do:

Change your current branch to point to the older commit instead. You could do that with git reset –hard f414f31. However, this is rewriting the history of your branch, so you should avoid it if you’ve shared this branch with anyone. Also, the commits you did after f414f31 will no longer be in the history of your master branch.
Create a new commit that represents exactly the same state of the project as f414f31, but just adds that on to the history, so you don’t lose any history. You can do that using the steps suggested in this answer – something like:
git reset --hard f414f31
git reset --soft HEAD@{1}
git commit -m "Reverting to the state of the project at f414f31"