您好,登錄后才能下訂單哦!
本篇內容主要講解“怎么自定制LogManager實現程序完全自定義的logger”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么自定制LogManager實現程序完全自定義的logger”吧!
對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
首先要實現一個繼承自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; } }
如下:
該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 = "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; } }
之前已經提到過,使用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("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”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。