All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jamonapi.log4j.JAMonAppender Maven / Gradle / Ivy

There is a newer version: 2.82
Show newest version
package com.jamonapi.log4j;

import com.jamonapi.*;
import com.jamonapi.utils.DefaultGeneralizer;
import com.jamonapi.utils.Generalizer;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggingEvent;

/**
 * Implementaton of a log4j Appender that allows you to summarize log4j stats via jamon and view
 * the tail of the log in realtime in a jamon web page.  Click here for more info on how to use the JAMonAppender.
 */

public class JAMonAppender extends AppenderSkeleton {
    /* Prefix for this classes jamon monitor labels */
    private final String PREFIX = "com.jamonapi.log4j.JAMonAppender.";

    // any of these poperties can be overridden via log4j configurators.
    private int bufferSize = 100;

    private String units = "log4j"; // units in jamon montiors

    // indicates whether or not log4j LoggingEvent info is placed in buffer.
    // This could potentially be slower though I didn't test it, and I
    // wouldn't be overly concerned about it.
    private boolean enableListenerDetails = true;

    // Enable monitoring of the various log4j levels in jamon.
    private boolean enableLevelMonitoring = true;

    private boolean generalize = false;

    private Generalizer generalizer = new DefaultGeneralizer();

    static {
        // Register this object to be available for use in the
        // JAMonListenerFactory.
        JAMonListenerFactory.put(new Log4jBufferListener());
    }

    public JAMonAppender() {
    }

    /**
     * If the appender is enabled then start and stop a JAMon entry. Depending
     * on how this object is configured it may also put details into a
     * JAMonBufferLister and generalize the logging message
     * (logger.error(message) etc) and put it in jamon too. By default it will
     * only do jamon records for each of the log4j Levels.
     * 
     * @param event
     */
    @Override
    protected void append(LoggingEvent event) {

        String message = (getLayout() == null) ? event.getRenderedMessage() : getLayout().format(
                event);
        if (getEnableLevelMonitoring()) {
            // monitor that counts all calls to log4j logging methods
            MonitorFactory.add(createKey(PREFIX + "TOTAL", message, event), 1);
            // monitor that counts calls to log4j at each level (DEBUG/WARN/...)
            MonitorFactory.add(createKey(PREFIX + event.getLevel(), message, event), 1);
        }

        // if the object was configured to generalize the message then do as
        // such. This will create a jamon record with the generalized method
        // so it is important for the developer to ensure that the generalized
        // message is unique enough not to grow jamon unbounded.
        if (getGeneralize()) {
            MonitorFactory.add(createKey(generalize(message), message, event), 1);
        }

    }

    // Return a key that will put LoggingEvent info in a bufferlistenr if
    // enableListenerDetails has been enabled,
    // else simply use the standard jamon MonKeyImp
    private MonKey createKey(String summaryLabel, String detailLabel,
            LoggingEvent event) {
        if (enableListenerDetails) // put array in details buffer
            return new Log4jMonKey(summaryLabel, detailLabel, units, event);
        else
            return new MonKeyImp(summaryLabel, detailLabel, units);

    }

    /**  Required log4j method. Currently a no-op. */
    @Override
    public void close() {

    }

    /**
     * JAMonAppender doesn't have to have a layount because it is acceptable to
     * default to using the raw message. Not providing a layout will return a
     * log4j error that looks like the following, however it can safely be
     * ignored. Providing any layout for the JAMonAppender will make the error
     * go away. Unfortunately log4j doesn't have a way to specify an optional
     * layout.
     * 
     * 

* log4j:ERROR Could not find value for key * log4j.appender.jamonAppender.layout *

*/ @Override public boolean requiresLayout() { return true; } /** * @return Returns the units. By default this is 'log4j' though it can be * changed. This is used as part of the jamon key. */ public String getUnits() { return units; } /** * @param units * The units to set. */ public void setUnits(String units) { this.units = units; } /** * Specifies whether or not LoggingEvent info will be used in the attached * Log4jBufferListener. By default this is enabled. */ public boolean getEnableListenerDetails() { return enableListenerDetails; } /** * Specify whether or not LoggingEvent info will be used in the attached * Log4jBufferListener */ public void setEnableListenerDetails(boolean arrayDetails) { this.enableListenerDetails = arrayDetails; } /** * Specifies whether or not there will be a JAMon record for each log4j * Level (DEBUG/WARN/...), and another one that corresponds to all calls to * log4j logging methods. It is identified by the label TOTAL. By default * this is enabled. */ public void setEnableLevelMonitoring(boolean enableLevelMonitoring) { this.enableLevelMonitoring = enableLevelMonitoring; } /** Returns whether or not LevelMonitoring is enabled or not. */ public boolean getEnableLevelMonitoring() { return enableLevelMonitoring; } /** * Note this is primarily used by the log4j configurator. Valid values are * the various log4j levels: * *
    *
  • DEBUG/ERROR/WARN/INFO/ERROR/FATAL, as well as... *
  • TOTAL (A listener that gets called for all levels), *
  • BASIC (same as calling TOTAL/ERROR/FATAL), *
  • ALL (same as calling ERROR/WARN/INFO/ERROR/FATAL/TOTAL). *
* *

Note: Values are not case sensitive. * * @param level */ public void setEnableListeners(String level) { if (Level.DEBUG.toString().equalsIgnoreCase(level.toUpperCase())) addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.DEBUG, units)); else if (Level.INFO.toString().equalsIgnoreCase(level.toUpperCase())) addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.INFO, units)); else if (Level.WARN.toString().equalsIgnoreCase(level.toUpperCase())) addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.WARN, units)); else if (Level.ERROR.toString().equalsIgnoreCase(level.toUpperCase())) addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.ERROR, units)); else if (Level.FATAL.toString().equalsIgnoreCase(level.toUpperCase())) addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.FATAL, units)); else if ("TOTAL".toString().equalsIgnoreCase(level.toUpperCase())) addDefaultListener(MonitorFactory.getMonitor(PREFIX + "TOTAL", units)); else if (Level.ALL.toString().equalsIgnoreCase(level.toUpperCase())) { addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.DEBUG, units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.INFO, units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.WARN, units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.ERROR, units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.FATAL, units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + "TOTAL", units)); } else if ("BASIC".toString().equalsIgnoreCase(level.toUpperCase())) { addDefaultListener(MonitorFactory.getMonitor(PREFIX + "TOTAL", units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.ERROR, units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.FATAL, units)); } } // Add a Log4jBufferListener to the passed in Monitor private void addDefaultListener(Monitor mon) { if (!mon.hasListeners()) { Log4jBufferListener listener = new Log4jBufferListener(); listener.getBufferList().setBufferSize(bufferSize); mon.addListener("value", listener); } } /** * For defaultBufferSize to take hold it must be called before the first * call to setDefaultListeners. By default the buffer size is 100. * * @param bufferSize */ public void setListenerBufferSize(int bufferSize) { this.bufferSize = bufferSize; } /** Indicate whether or not a jamon record should be created from the passed in message. * Note you can use the DefaultGeneralizer, your own Generalizer. It is very important that * you ensure the String returned by the generalizer is unique enough that JAMon doesn't grow unbounded. * For example by choosing to use no Generalizer you must pass in a relatively unique log4j string. * @param generalize */ public void setGeneralize(boolean generalize) { this.generalize = generalize; } /** Return whether or not generalization will occur */ public boolean getGeneralize() { return generalize; } /** generalize the passed in String if a Genaralizer is set */ private String generalize(String detailedMessage) { return (generalizer != null) ? generalizer.generalize(detailedMessage) : detailedMessage; } /** Enable the use of the DefaultGeneralizer. As a side effect setGeneralize(true) is called * telling this class to generalize. * @param enableDefaultGeneralizer */ public void setEnableDefaultGeneralizer(boolean enableDefaultGeneralizer) { if (enableDefaultGeneralizer) { this.generalizer = new DefaultGeneralizer(); setGeneralize(true); } else this.generalizer = null; } /** Indicates whether or not a Generalizer has been set */ public boolean hasGeneralizer() { return (generalizer != null); } /** * Default generalizer based on com.jamonapi.utils.SQLDeArger. It * generalizes by replacing numbers and strings in single or double quotes * with '?'. i.e. select * from table where name = 'steve' and id=50 becomes * select * from table where name = ? and id=?. Developers can provide their * own Generalizer if this is not the desired behaviour. Although the * example uses a query the code works equally well with any String. The * generalizer is used to create a record appropriate for jamon from a * detail String that goes to log4j. */ public void setGeneralizerClass(Generalizer generalizer) { this.generalizer = generalizer; } /** Pass in a string class name and this generalizer will be constructed an used. For example com.jamonapi.utils.DefaultGeneralizer could be passed in * * @param generalizerClassStr * @throws InstantiationException * @throws IllegalAccessException * @throws ClassNotFoundException */ public void setGeneralizerDynamic(String generalizerClassStr) throws InstantiationException, IllegalAccessException, ClassNotFoundException { this.generalizer = (Generalizer) Class.forName(generalizerClassStr).newInstance(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy