如何在java日志记录属性文件中配置我自己的格式化程序

时间:2010-04-26 00:25:50

标签: java logging java.util.logging

对于我的java项目,我使用的是java logging api。我想使用属性文件记录所有内容。

在使用这个文件(log.properties)之前,我在java代码中配置了我的onwn格式化程序。 (见下文)

现在我想在属性文件中配置我自己的fomatter,而不是java代码。 有人知道怎么做吗?

Formatter formatter = new Formatter() {

                @Override
                public String format(LogRecord arg0) {
                    StringBuilder b = new StringBuilder();
                    b.append(new Date());
                    b.append(" ");
                    b.append(arg0.getSourceClassName());
                    b.append(" ");
                    b.append(arg0.getSourceMethodName());
                    b.append(" ");
                    b.append(arg0.getLevel());
                    b.append(" ");
                    b.append(arg0.getMessage());
                    b.append(System.getProperty("line.separator"));
                    return b.toString();
                }

            };

java代码中的fomatter

  

..... .....

     

java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter   java.util.logging.FileHandler.level =警告

     

**的java.util.logging。??? =如何在属性文件中使用以下信息配置我自己的格式化程序:data,clasename,methodename,level .etc。****

de log.proprties中的

formatter

2 个答案:

答案 0 :(得分:0)

将所需的属性放在属性文件中。

在格式化程序调用中

LogManager theManager = LogManager.getLogManager();
String value = theManager.getProperty("propName");
// do whatever with value

答案 1 :(得分:0)

编写自己的格式化程序,接受格式字符串。

在logging.properties文件中配置格式化程序。例如:

java.util.logging.ConsoleHandler.formatter = my.util.logging.CustomFormatter
my.util.logging.CustomFormatter.format = %t %L: %E %m
my.util.logging.CustomFormatter.date.format = ddMMMyyyy HH:mm:ss
# logger.log(Level.INFO, "INFO message.");
# produces the following log message:
# 18Aug2013 13:04:19 INFO: (LoggingLevelDemo.java:34) INFO message.

如果写这样的格式化程序看起来令人生畏,那么可以从以下开始:

package my.util.logging;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.Format;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.logging.Formatter;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
 * A {@link Formatter} that may be customized in a {@code logging.properties} file. The syntax of the property
 * {@code my.util.logging.CustomFormatter.format} specifies the output. A newline will be appended to the string and
 * the following special characters will be expanded (case sensitive):-
 * <ul>
 * <li>{@code %m} - message</li>
 * <li>{@code %L} - log level</li>
 * <li>{@code %n} - name of the logger</li>
 * <li>{@code %t} - a timestamp (in ISO-8601 "yyyy-MM-dd HH:mm:ss.SSS" format)</li>
 * <li>{@code %M} - source method name (if available, otherwise "?")</li>
 * <li>{@code %c} - source class name (if available, otherwise "?")</li>
 * <li>{@code %C} - source simple class name (if available, otherwise "?")</li>
 * <li>{@code %T} - thread ID</li>
 * <li>{@code %E} - (Filename.java:linenumber) Slow to generate Eclipse format</li>
 * </ul>
 * The default format is {@value #DEFAULT_FORMAT}. Curly brace characters are not allowed.
 *
 * Based on http://javablog.co.uk/2008/07/12/logging-with-javautillogging/ 
 * %E Eclipse format was added with flag to avoid Stack trace generation if Eclipse format not used.
 *
 * @author Samuel Halliday
 * @author Ryan Ripken
 */
public class CustomFormatter extends Formatter
{

    // milliseconds can be nice for rough performance numbers
    public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS Z";
    private static final DateFormat defaultDateFormat = new SimpleDateFormat(DATE_FORMAT);
    // private static final String USACE_DATE_FORMAT = "yyyyMMdd HHmmss Z";
    public static final String DEFAULT_FORMAT = "%t %L: %E %m";
    protected static final StackTraceElement nullElement = new StackTraceElement("?", "?", "?", -1);

    private final MessageFormat messageFormat;
    private final boolean[] needsArg;
    private final DateFormat dateFormat;

    public CustomFormatter()
    {
        super();

        LogManager logManager = LogManager.getLogManager();
        String classname = getClass().getName();

        // load the format from logging.properties
        String dateFormatKey = classname + ".date.format";

        String strDateFormat = logManager.getProperty(dateFormatKey);

        if (strDateFormat != null) {
            dateFormat = new SimpleDateFormat(strDateFormat);
        } else {
            dateFormat = defaultDateFormat;
        }
        String dateFormatTimeZoneKey = classname + ".date.timezone";
        String strDateFormatTimeZone = logManager
            .getProperty(dateFormatTimeZoneKey);
        if (strDateFormatTimeZone != null) {
            dateFormat.setTimeZone(TimeZone.getTimeZone(strDateFormatTimeZone));
        }

        String propName = classname + ".format";
        String format = logManager.getProperty(propName);
        if (format == null || format.trim().length() == 0) {
            format = DEFAULT_FORMAT;
        }
        if (format.contains("{") || format.contains("}")) {
            throw new IllegalArgumentException("curly braces not allowed");
        }

        // convert it into the MessageFormat form
        format = format.replace("%L", "{0}")
            .replace("%m", "{1}")
            .replace("%M", "{2}")
            .replace("%t", "{3}")
            .replace("%c", "{4}")
            .replace("%T", "{5}")
            .replace("%n", "{6}")
            .replace("%C", "{7}")
            .replace("%E", "{8}") +
            "\n";

        messageFormat = new MessageFormat(format);
        Format[] formatsByArgumentIndex = messageFormat.getFormatsByArgumentIndex();
        needsArg = new boolean[formatsByArgumentIndex.length];
        for (int i = 0; i < formatsByArgumentIndex.length; i++) {
            needsArg[i] = format.contains("{" + i + "}");
        }
    }

    @Override
    public String format(LogRecord record)
    {
        String[] arguments = new String[9];

        // This is a StringBuffer instead of StringBuilder so that
        // it can be re-used for the messageFormat.format calls.
        // It is, hopefully, slightly oversized so that it won't 
        // have to be enlarged...
        StringBuffer sb = new StringBuffer(256);

        if (needsArg[0]) {
            // %L
            arguments[0] = record.getLevel().toString();
        }

        if (needsArg[1]) {
            // %m
            sb = formatMessage(record, sb);
            sb = getThrowableMessage(record, sb);  // maybe this should have been its own flag?
            arguments[1] = sb.toString();
        }

        if (needsArg[2]) {
            // %M
            arguments[2] = record.getSourceMethodName();
        } else {
            arguments[2] = "?";
        }

        if (needsArg[3]) {
            // %t
            sb.delete(0, sb.length()); // re-use

            Date date = new Date(record.getMillis());
            FieldPosition fieldPos = new FieldPosition(0);
            synchronized (dateFormat) {
                sb = dateFormat.format(date, sb, fieldPos);
            }

            arguments[3] = sb.toString();
        }

        if (needsArg[4] || needsArg[7]) {
            // %c
            arguments[4] = record.getSourceClassName();
        } else {
            arguments[4] = "?";
        }

        if (needsArg[5]) {
            // %T
            arguments[5] = Integer.toString(record.getThreadID());
        }

        if (needsArg[6]) {
            // %n
            arguments[6] = record.getLoggerName();
        }

        if (needsArg[7]) {
            // %C
            int start = arguments[4].lastIndexOf('.') + 1;
            if (start > 0 && start < arguments[4].length()) {
                arguments[7] = arguments[4].substring(start);
            } else {
                arguments[7] = arguments[4];
            }
        }

        if (needsArg[8]) {
            // %E Expensive Eclipse Format
            sb.delete(0, sb.length()); // reuse buffer
            getEclipseFormat(sb); // gets a stackTrace to generate.
            arguments[8] = sb.toString();
        } else {
            arguments[8] = "(?:?)";
        }

        sb.delete(0, sb.length()); //reuse buffer

        FieldPosition fieldPos = new FieldPosition(0);

        synchronized (messageFormat) {
            // messageFormat.format only calls into 
            // messageFormat.format(arguments, new StringBuffer(), new FieldPosition(0)).toString();
            // we already have a StringBuffer laying around.  We should reuse it.
            //return messageFormat.format(arguments);
            sb = messageFormat.format(arguments, sb, fieldPos);
        }
        return sb.toString();
    }

    /**
     * Localize and format the message string from a log record.
     * <p>
     * The message string is first localized to a format string using the record's ResourceBundle. (If there is no
     * ResourceBundle, or if the message key is not found, then the key is used as the format string.) The format String
     * uses java.text style formatting.
     * <ul>
     * <li>If there are no parameters, no formatter is used.
     * <li>Otherwise, if the string contains "{0" then java.text.MessageFormat is used to format the string.
     * <li>Otherwise no formatting is performed.
     * </ul>
     * <p>
     *
     * @param record the log record containing the raw message
     * @param result the StringBuffer where the message text is to be appended
     * @return StringBuffer where the message text was appended
     */
    public synchronized StringBuffer formatMessage(LogRecord record, StringBuffer result)
    {
        // This is the default formatMessage implementation from 
        // java.util.logging.Formatter except that it has been
        // modified to operate on the passed in StringBuffer.

        String format = record.getMessage();
        java.util.ResourceBundle catalog = record.getResourceBundle();
        if (catalog != null) {
            try {
                format = catalog.getString(record.getMessage());
            } catch (java.util.MissingResourceException ex) {
                // Drop through.  Use record message as format
                format = record.getMessage();
            }
        }
        // Do the formatting.
        try {
            Object parameters[] = record.getParameters();
            if (parameters == null || parameters.length == 0) {
                // No parameters.  Just return format string.
                result.append(format);
            } else if (format.indexOf("{0") >= 0 ||
                format.indexOf("{1") >= 0 ||
                format.indexOf("{2") >= 0 ||
                format.indexOf("{3") >= 0) {
                // Is is a java.text style format?
                // Ideally we could match with
                // Pattern.compile("\\{\\d").matcher(format).find())
                // However the cost is 14% higher, so we cheaply check for
                // 1 of the first 4 parameters
                MessageFormat temp = new MessageFormat(format);
                temp.format(parameters, result, new FieldPosition(0));  // this appends to sb       
            } else {
                result.append(format);
            }
        } catch (Exception ex) {
            // Formatting failed: use localized format string.
            result.append(format);
        }
        return result;
    }

    protected StringBuffer getThrowableMessage(LogRecord record, StringBuffer sb)
    {
        Throwable thrown = record.getThrown();
        if (thrown != null) {
            PrintWriter pw = null;
            try {
                StringWriter sw = new StringWriter();
                pw = new PrintWriter(sw);
                record.getThrown().printStackTrace(pw);
                pw.flush();
                sb.append('\n');
                sb.append(sw.toString());
            } catch (Exception ex) {
                System.out.println("CustomFormatter:Caught exception trying to build log message.");
                ex.printStackTrace();
            } finally {
                if (pw != null) {
                    pw.close();
                }
            }
        }
        return sb;
    }

    /**
     * Returns caller location information in eclipse format eg (Filename.java:23) WARNING Generating caller location
     * information is extremely slow. It's use should be avoided unless execution speed is not an issue.
     *
     * @param sb
     *
     * @return the eclipse format
     */
    public StringBuffer getEclipseFormat(StringBuffer sb)
    {
        final boolean useCurrentThread = false;
        // getStackTrace can be expensive
        StackTraceElement[] stackTrace;

        if (useCurrentThread) {
            stackTrace = Thread.currentThread().getStackTrace();
        } else {
            // Its possible this is faster.
            // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302
            stackTrace = new Throwable().getStackTrace();
        }

        StackTraceElement element = findCallingElement(stackTrace);
        appendEclipseFormat(element, sb);
        return sb;
    }

    public static StringBuffer appendEclipseFormat(final StackTraceElement element, final StringBuffer sb)
    {
        if (element == null || nullElement == element) {
            sb.append("(?:?)");
        } else {
            sb.append('(');

            String filename = element.getFileName();
            if (filename == null || filename.isEmpty()) {
                sb.append("?");
            } else {
                sb.append(filename);
            }

            sb.append(':');

            int lineNumber = element.getLineNumber();
            if (lineNumber <= 0) {
                sb.append('?');
            } else {
                sb.append(lineNumber);
            }

            sb.append(')');
        }

        return sb;
    }

    public static StackTraceElement findCallingElement(StackTraceElement[] stackTrace)
    {
        StackTraceElement retval = nullElement;

        int lastIdx = firstIndexNot(stackTrace, Logger.class.getName(), 7);

        if (lastIdx >= 0 && lastIdx < stackTrace.length) {
            retval = stackTrace[lastIdx];
        }

        return retval;
    }

    private static int firstIndexNot(StackTraceElement[] stackTrace,
        String classname, int fromIndex)
    {
        int idx = -1;

        if (classname != null) {
            if (stackTrace != null && fromIndex >= 0 && stackTrace.length > fromIndex) {
                for (int i = fromIndex; i < stackTrace.length; i++) {
                    if (!classname.equals(stackTrace[i].getClassName())) {
                        idx = i;
                        break;
                    }
                }
            }
        }

        return idx;
    }

}