`
qingkangxu
  • 浏览: 43542 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

如果定制LogManager

阅读更多

前一篇博文介绍了JDK logging基础知识 http://qingkangxu.iteye.com/blog/1503434

博文中也提到LogManager,本章主要阐述怎么完全定制化LogManager来实现应用程序完全自定制的logger,其实对于大多数开发者来说,很少有需要定制LogManager的时候,只有是需要单独开发一个产品,需要完全独立的logger机制时才有可能需要定制LogManager,比如:

  1,希望自由定制log的输出路径

  2,希望完全定制log的format

  3,希望日志中的国际化信息采用自己定义的一套机制等

当然,对于大型的中间件而言,自定义LogManager则是非常有必要的。

引言:

对tomcat熟悉的读者,有可能会注意到tomcat的启动脚本catalina.bat中也使用定制的LogManager,如下:


if not exist "%CATALINA_HOME%\bin\tomcat-juli.jar" goto noJuli

set JAVA_OPTS=%JAVA_OPTS% -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties"

当tomcat的bin路径下存在tomcat-juli.jar文件(也就是存在定制的LogManager)时,那么会强制在JVM系统属性中指定org.apache.juli.ClassLoaderLogManager作为整个JVM的LogManager,以此来完成一些特殊操作。



websphere的启动脚本startServer.bat中也定义了自己的LogManager,如下:

java.util.logging.manager=com.ibm.ws.bootstrap.WsLogManager


下边阐述怎么实现自定义的LogManager:

首先要实现一个继承自java.util.logging.LogManager的类:

    子类覆盖java.util.logging.LogManager的addLogger方法,在成功添加logger之后对logger做定制化操作,从代码中可以看出addLogger方法调用了子类的internalInitializeLogger方法,internalInitializeLogger方法中先清空logger的所有handler,然后再增加一个自定义的Handler

    需要说明一下:internalInitializeLogger方法中的操作(给logger增设我们自定义的handler)是我们自定义LogManager的一大目的。

package com.bes.logging;

import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

public class ServerLogManager extends LogManager {
	private static ServerFileHandler handlerSingleton;
	private static ServerLogManager thisInstance;
	private Object lockObj = new Object();

	public ServerLogManager() {
		super();
	}

	public static synchronized ServerLogManager getInstance() {
		if (thisInstance == null) {
			thisInstance = new ServerLogManager();
		}
		return thisInstance;
	}

	public boolean addLogger(Logger logger) {
		boolean result = super.addLogger(logger);

		 //initialize Logger
		if (logger.getResourceBundleName() == null) {
			try {
				Logger newLogger = Logger.getLogger(logger.getName(),
						getLoggerResourceBundleName(logger.getName()));

				assert (logger == newLogger);
			} catch (Throwable ex) {
				//ex.printStackTrace();
			}
		}
		synchronized (lockObj) {
			internalInitializeLogger(logger);
		}

		return result;
	}

	/**
	 * Internal Method to initialize a list of unitialized loggers.
	 */
	private void internalInitializeLogger(final Logger logger) {
		// Explicitly remove all handlers.
		Handler[] h = logger.getHandlers();
		for (int i = 0; i < h.length; i++) {
			logger.removeHandler(h[i]);
		}

		logger.addHandler(getServerFileHandler());
		logger.setUseParentHandlers(false);

		logger.setLevel(Level.FINEST);// only for test
	}

	private static synchronized Handler getServerFileHandler() {
		if (handlerSingleton == null) {
			try {
				handlerSingleton = ServerFileHandler.getInstance();
				handlerSingleton.setLevel(Level.ALL);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return handlerSingleton;
	}

	public String getLoggerResourceBundleName(String loggerName) {
		String result = loggerName + "." + "LogStrings";
		return result;
	}

}

 

 自定义的LogManager中使用到的ServerFileHandler如下:

    该ServerFileHandler是一个把logger日志输出到文件中的handler,可以通过com.bes.instanceRoot系统属性来指定日志文件跟路径;其次,ServerFileHandler也指定了自己的UniformLogFormatter;最后是需要覆盖父类的publish方法,覆盖的publish方法在做真正的日志输入之前会检查日志文件是否存在,然后就是创建一个和日志文件对应的输出流,把该输出流设置为ServerFileHandler的输出流以至日志输出的时候能输出到文件中。另外,WrapperStream仅仅是一个流包装类。

    这里也需要说一下:ServerFileHandler构造方法中的setFormatter(new UniformLogFormatter());操作是我们自定义LogManager的第二大目的。

package com.bes.logging;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.LogRecord;
import java.util.logging.StreamHandler;

public class ServerFileHandler extends StreamHandler {
  private WrapperStream wrappedStream;

  private String absoluteFileName = null;
  static final String LOG_FILENAME_PREFIX = &quot;server&quot;;
  static final String LOG_FILENAME_SUFFIX = &quot;.log&quot;;
  private String logFileName = LOG_FILENAME_PREFIX + LOG_FILENAME_SUFFIX;

  public static final ServerFileHandler thisInstance = new ServerFileHandler();

  public static synchronized ServerFileHandler getInstance() {
    return thisInstance;
  }

  protected ServerFileHandler() {
    try {
      setFormatter(new UniformLogFormatter());
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public synchronized void publish(LogRecord record) {
    if (wrappedStream == null) {
      try {
        absoluteFileName = createFileName();
        openFile(absoluteFileName);
      } catch (Exception e) {
        throw new RuntimeException(
            &quot;Serious Error Couldn't open Log File&quot; + e);
      }
    }
    super.publish(record);
    flush();
  }

  public String createFileName() {
    String instDir = &quot;&quot;;
    instDir = System.getProperty(&quot;com.bes.instanceRoot&quot;);
    if(instDir == null || &quot;&quot;.equals(instDir)){
      instDir = &quot;.&quot;;
    }
    return instDir + &quot;/&quot; + getLogFileName();
  }

  /**
   * Creates the file and initialized WrapperStream and passes it on to
   * Superclass (java.util.logging.StreamHandler).
   */
  private void openFile(String fileName) throws IOException {
    File file = new File(fileName);
    if(!file.exists()){
      if(file.getParentFile() != null &amp;&amp; !file.getParentFile().exists()){
        file.getParentFile().mkdir();
      }
      file.createNewFile();
    }
    FileOutputStream fout = new FileOutputStream(fileName, true);
    BufferedOutputStream bout = new BufferedOutputStream(fout);
    wrappedStream = new WrapperStream(bout, file.length());
    setOutputStream(wrappedStream);
  }

  private class WrapperStream extends OutputStream {
    OutputStream out;
    long written;

    WrapperStream(OutputStream out, long written) {
      this.out = out;
      this.written = written;
    }

    public void write(int b) throws IOException {
      out.write(b);
      written++;
    }

    public void write(byte buff[]) throws IOException {
      out.write(buff);
      written += buff.length;
    }

    public void write(byte buff[], int off, int len) throws IOException {
      out.write(buff, off, len);
      written += len;
    }

    public void flush() throws IOException {
      out.flush();
    }

    public void close() throws IOException {
      out.close();
    }
  }

  protected String getLogFileName() {
    return logFileName;
  }
}

 

实现Formatter:

    之前已经提到过,使用logger日志输出的时候,handler会自动调用自己的formatter对日志做format,然后输出格式化之后的日志。自定义的Formatter只需要覆盖public String format(LogRecord record)便可。这个类本身很简单,就是日志输出时自动增加指定格式的时间,加上分隔符,对日志进行国际化处理等操作。 需要注意的是类中对ResourceBundle做了缓存以提高效率。

package com.bes.logging;

import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.ResourceBundle;
import java.util.logging.Formatter;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;

public class UniformLogFormatter extends Formatter {

  private Date date = new Date();
  private HashMap loggerResourceBundleTable;
  private LogManager logManager;
  private static final char FIELD_SEPARATOR = '|';
  private static final String CRLF = System.getProperty(&quot;line.separator&quot;);

  private static final SimpleDateFormat dateFormatter = new SimpleDateFormat(
      &quot;yyyy-MM-dd'T'HH:mm:ss.SSSZ&quot;);

  public UniformLogFormatter() {
    super();
    loggerResourceBundleTable = new HashMap();
    logManager = LogManager.getLogManager();
  }

  public String format(LogRecord record) {
    return uniformLogFormat(record);
  }

  private String uniformLogFormat(LogRecord record) {
    try {
      String logMessage = record.getMessage();
      int msgLength = 150; // typical length of log record
      if (logMessage != null)
        msgLength += logMessage.length();
      StringBuilder recordBuffer = new StringBuilder(msgLength);

      // add date to log
      date.setTime(record.getMillis());
      recordBuffer.append(dateFormatter.format(date)).append(
          FIELD_SEPARATOR);
      // add log level and logger name to log
      recordBuffer.append(record.getLevel()).append(FIELD_SEPARATOR);
      recordBuffer.append(record.getLoggerName()).append(FIELD_SEPARATOR);

      if (logMessage == null) {
        logMessage = &quot;The log message is null.&quot;;
      }
      if (logMessage.indexOf(&quot;{0}&quot;) &gt;= 0) {
        try {
          logMessage = java.text.MessageFormat.format(logMessage,
              record.getParameters());
        } catch (Exception e) {
          // e.printStackTrace();
        }
      } else {
        ResourceBundle rb = getResourceBundle(record.getLoggerName());
        if (rb != null) {
          try {
            logMessage = MessageFormat.format(
                rb.getString(logMessage),
                record.getParameters());
          } catch (java.util.MissingResourceException e) {
          }
        }
      }
      recordBuffer.append(logMessage);
      recordBuffer.append(CRLF);

      return recordBuffer.toString();

    } catch (Exception ex) {
      return &quot;Log error occurred on msg: &quot; + record.getMessage() + &quot;: &quot;
          + ex;
    }
  }

  private synchronized ResourceBundle getResourceBundle(String loggerName) {
    if (loggerName == null) {
      return null;
    }
    ResourceBundle rb = (ResourceBundle) loggerResourceBundleTable
        .get(loggerName);

    if (rb == null) {
      rb = logManager.getLogger(loggerName).getResourceBundle();
      loggerResourceBundleTable.put(loggerName, rb);
    }
    return rb;
  }
} 

 
完成了定制的LogManager之后,在启动JVM的命令中增加系统属性便可,java -Djava.util.logging.manager=com.bes.logging.ServerLogManager,加上这个系统属性之后通过java.util.logging.Logger类获取的logger都是经过定制的LogManager作为初始化的,日志输出的时候便会使用上面的ServerFileHandler#publish()方法进行日志输出,并使用UniformLogFormatter对日志进行格式化。

 

 

0
0
分享到:
评论

相关推荐

    aaaaaaaaa

    如果需要更详细的分析,通常会涉及到代码中的方法、变量和逻辑处理,以及如何通过配置文件或代码动态调整日志行为。在实际应用中,对`LogManager`的了解有助于优化日志系统,提升应用的可维护性和调试效率。

    fst-1.60.zip

    《深入理解LogManager:探索日志管理的JDK实现与集成》 在IT行业中,日志管理是系统监控、故障排查以及性能优化的关键环节。本文将深入探讨标题为"fst-1.60.zip"的压缩包内容,该包包含了LogManager的相关实现,...

    c#非常好的日志类,很不错值得下载

    根据给定的信息,本文将详细解释C#中的一个日志类——`LogManager`,...通过灵活的配置选项和多样化的记录方法,开发者可以根据实际需求定制化地管理日志信息。无论是Web应用还是Windows Forms应用,都能从中受益匪浅。

    log4qt qt5版本

    《基于Qt5的log4qt库改进版》 ...通过灵活配置,开发者可以定制出满足不同需求的日志系统,有效地进行问题排查和性能优化。在实际开发中,务必根据项目的具体需求,合理选择和配置log4qt,使其发挥最大的作用。

    log4net日志库帮助文件

    - 如果需要实现自定义的Logger,可以通过实现ILog接口来创建。log4net提供了一个接口和一个管理类的基础架构,使得扩展和定制变得相对简单。 6. 配置: - log4net的配置可以通过XML文件进行,这使得在不修改代码...

    log4net 日志操作 源码

    通过灵活配置,你可以定制适合项目的日志系统,同时源码分析有助于深入理解其工作原理,以便于在必要时进行扩展或优化。希望这篇关于log4net日志操作的源码解析能对你有所帮助,祝你在编程旅程中一帆风顺!

    写日志类及资料Log4Net

    例如,你可以设置一个FileAppender将日志写入文件,并使用PatternLayout来定制日志格式,如时间戳、线程ID、级别和日志消息。 Log4Net的API简单易用,主要包含三个关键类:`ILog`接口、`log4net.LogManager`静态类...

    Log4net的使用 asp.net中log4net的使用方法

    3. **自定义Appender**:如果你有特殊的需求,可以创建自定义的日志Appender,比如发送日志到邮件或者云服务。 总结,Log4net是一个功能强大且灵活的日志框架,它可以帮助开发者在ASP.NET应用中轻松地进行日志管理...

    jboss日志发邮件需要替换的jar包

    Log4j提供了丰富的日志记录功能,包括定制日志格式、多种输出目的地(如文件、控制台、数据库等)。如果需要通过电子邮件发送日志,可能需要将这个jar包升级到支持SMTP(简单邮件传输协议)的日志Appender,或者添加...

    Run Jboss as a service in another location

    JAVA_OPTS="-Djava.util.logging.manager=org.jboss.logmanager.LogManager ${JAVA_OPTS}" ``` 确保也配置了日志目录: ```bash JAVA_OPTS="-Djboss.server.log.dir=C:/Logs/JBoss ${JAVA_OPTS}" ``` 完成配置后...

    log4j2在SpringMVC工程中的运用

    实际应用中,还可以进一步探索Log4j2的高级特性,如异步日志记录、过滤器、定制日志事件处理器等,以满足复杂日志管理需求。 总结来说,Log4j2在SpringMVC工程中的运用,通过配置文件、依赖管理和代码集成,可以...

    android-logging-log4j-1.0.3

    例如,如果使用Gradle,可以在dependencies块中添加如下代码: ```groovy dependencies { implementation 'org.apache.logging.log4j:log4j-api:2.x.x' // 使用最新版本 implementation 'org.apache.logging.log4...

    log4net配置详解

    ... ... ... 1. **Logger(记录器)**:Logger是与...通过灵活的配置,开发者可以实现日志的定制化记录,从而更好地监控和诊断应用程序的运行状态。正确配置和使用log4net对于提升应用的可维护性和问题排查效率具有重要意义。

    .net中log4net的用法

    它的设计允许开发者通过简单的XML配置文件定制日志记录行为,如输出级别、目标(控制台、文件、数据库等)以及格式。Log4Net还支持多种输出模式,包括文本、XML或自定义格式,满足不同场景的需求。 配置Log4Net通常...

    log4j.xml配置实现.pdf

    如果文件路径以`.xml`结尾,那么使用`DOMConfigurator.configure()`来解析XML配置文件;否则,假设配置文件是`.properties`格式,使用`PropertyConfigurator.configure()`进行配置。在应用关闭时,`contextDestroyed...

    监听Android系统log

    通过`LogObserver`,我们可以实现更高级别的定制化日志处理,实时跟踪应用程序的运行状态,这对于问题排查和性能优化具有极大的帮助。不过要注意,过度使用log可能会增加应用的运行负担,因此在生产环境中应合理控制...

    java_logger知识

    开发者可以根据需求定制格式,比如添加时间戳、线程信息等。 以下是一个简单的使用示例: ```java import java.util.logging.*; public class SimpleLoggingTest { public static void main(String args[]) { /...

    NLog测试程序

    通过实践,你将能够更好地理解如何根据项目需求定制NLog的配置,以及如何在代码中有效地利用NLog进行日志记录。 总之,NLog是.NET开发中不可或缺的日志工具,它提供了强大的功能和灵活性,能够帮助开发者实现高效的...

    日志系统详细说明文档

    Log4net的设计理念是“简单易用,但功能强大”,它的灵活性使得日志级别可调整,输出格式可定制,日志目的地可多样化。 1.1. log4net的简要说明 Log4net的核心概念包括日志器(Logger)、存储库(Repository)和...

    日志文件记录代码

    - 日志信息的格式可以通过PatternLayout或XmlLayout进行定制,包含时间戳、日志级别、线程ID、类名、方法名等信息。 **日志文件的使用** 1. **初始化log4net** - 在应用程序启动时,需要通过`log4...

Global site tag (gtag.js) - Google Analytics