前一篇博文介绍了 JDK logging基础知识
博文中也提到LogManager,本章主要阐述怎么完全定制化LogManager来实现应用程序完全自定制的logger,其实对于大多数开发者来说,很少有需要定制LogManager的时候,只有是需要单独开发一个产品,需要完全独立的logger机制时才有可能需要定制LogManager,比如:
1,希望自由定制log的输出路径
2,希望完全定制log的format
3,希望日志中的国际化信息采用自己定义的一套机制等
当然,对于大型的中间件而言,自定义LogManager则是非常有必要的。
引言
对tomcat熟悉的读者,有可能会注意到tomcat的启动脚本catalina.bat中也使用定制的LogManager,如下:
1 2 |
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的一大目的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
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的第二大目的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
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 = "server"; static final String LOG_FILENAME_SUFFIX = ".log"; 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( "Serious Error Couldn't open Log File" + e); } } super .publish(record); flush(); } public String createFileName() { String instDir = ""; instDir = System.getProperty("com.bes.instanceRoot"); if (instDir == null || "".equals(instDir)){ instDir = "."; } return instDir + "/" + 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 && !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做了缓存以提高效率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
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("line.separator"); private static final SimpleDateFormat dateFormatter = new SimpleDateFormat( "yyyy-MM-dd 'T' HH:mm:ss.SSSZ"); 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 = "The log message is null ."; } if (logMessage.indexOf("{ 0 }") >= 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 "Log error occurred on msg: " + record.getMessage() + ": " + 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对日志进行格式化。
以上就是通过自定制LogManager实现程序完全自定义的logger的详细内容,更多关于自定制LogManager实现自定义logger的资料请关注其它相关文章!
原文链接:https://blog.csdn.net/qingkangxu/article/details/84225566
查看更多关于通过自定制LogManager实现程序完全自定义的logger的详细内容...