雖然JAVA日志包提供的功能已經很方便,但是假如我們有新的需求如:將日志文件保存到我們希望的位置並在日志文件名中添加日期且保存指定時間內的日志文件;按照自己希望的格式輸出日志內容。對於這些需求我們只要擴展java.util.logging.StreamHandler(Handler的子類),java.util.logging.Formatter創建自定義的處理器及格式化器即可以實現。下面是個例子,它分別創建了Handler及Formatter的子類,以便實現將日志文件保存到我們需要的位置,及在日志文件名中保存日志的生成日期,允許你設置需要保存幾天內的日志文件,允許設置每個日志文件的大小。
(1)自定義文件處理器
package com.bsk.log; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStream; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedList; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.logging.StreamHandler; public class CustomFileStreamHandler extends StreamHandler{ //輸出流 private MeteredStream msOut; //日志玩家的寫入的字節數, limit 為0 表示沒有限制 private int limit = 50000; //是否添加的玩家末尾 private boolean append; //保存存在的日志文件 private LinkedList<File> files; //希望寫入的日志文件 private File file; //希望寫入的日志路徑 private String fileUrl; //希望寫入的當天日志路徑 private String fileUrlDay; //保存幾天之內的日志文件 private int dateInter = 1; //索引文件,用於記錄當前日志記錄信息,請不要認為的修改 private File indexFile; /** * 初始化自定義文件流處理器 * @param fileUrl 文件路徑, 可以是個目錄或希望的日志名稱,如果是個目錄則日志為“未命名” * 指定日志名稱時不需要包括日期,程序會自動生成日志文件的生成日期及相應的編號 * @param limit 每個日志希望寫入的最大字節數,如果日志達到最大字節數則當天日期的一個新的編 號的日志文件將被創建,最新的日志記錄在最大編號的文件中 * @param dateInter 事務保存的日期間隔 * @param append 是否將日志寫入已存在的日志文件中 * @throws java.lang.Exception */ public CustomFileStreamHandler(String fileUrl, int limit, int dateInter, boolean append) throws Exception { super(); this.fileUrl = fileUrl; if (dateInter <= 0) { throw new IllegalArgumentException("時間間隔必須大於0"); } if (limit < 0) { throw new IllegalArgumentException("寫入日志文件的最大字節數必須大於0"); } this.limit = limit; this.dateInter = dateInter; this.append = append; openWriteFiles(); } public CustomFileStreamHandler(String fileUrl, boolean append) throws Exception { super(); this.fileUrl = fileUrl; this.append = append; openWriteFiles(); } /** * 獲得將要寫入的文件 */ private synchronized void openWriteFiles() throws Exception { if (fileUrl == null) { throw new IllegalArgumentException("文件路徑不能為null"); } //files = getWritedLog(); //checkLogFile(); getWriteFile(); if (append) { //openFile(files.getLast(), append); openFile(file, append); } else { getLastFile(); } } private void getWriteFile()throws Exception{ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String trace = sdf.format(new Date().getTime()); fileUrlDay = fileUrl+"_"+trace+".log"; file = new File(fileUrlDay); if (!file.exists()) { file.createNewFile(); } } /** * 打開需要寫入的文件 * @param file 需要打開的文件 * @param append 是否將內容添加到文件末尾 */ private void openFile(File file, boolean append) throws Exception { //System.out.println("***opend = true " + file.toString()); int len = 0; if (append) { len = (int) file.length(); } FileOutputStream fout = new FileOutputStream(file.toString(), append); BufferedOutputStream bout = new BufferedOutputStream(fout); msOut = new MeteredStream(bout, len); setOutputStream(msOut); } /** * 將離現在最近的文件作為寫入文件的文件 * 例如 D:logmylog_30_2008-02-19.log * mylog表示自定義的日志文件名,2008-02-19表示日志文件的生成日期,30 表示此日期的第30個日志文件 */ private void getLastFile() { try { super.close(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String trace = sdf.format(new Date().getTime()); int maxLogCount = 0; //獲得當前最大的日志文件編號 //System.out.println("********共有文件**********"); for (File file : files) { //System.out.println(file.toString()); String fileName = file.toString(); //獲得相同同日期日志文件的最大編號 if (fileName.indexOf(trace) != -1) { int last = fileName.lastIndexOf('_'); int beforeLast = fileName.lastIndexOf('_', last - 1); maxLogCount = Integer.valueOf(fileName.substring(beforeLast + 1, last)); } } //System.out.println("********"); //System.out.println("maxLogCount == " + maxLogCount); File file = new File(fileUrl); String logIndex = (maxLogCount + 1) + "_" + trace; if (file.isDirectory()) { //是個目錄 files.add(new File(fileUrl + File.separator + "未命名_" + logIndex + ".log")); } else { files.add(new File(fileUrl + "_" + logIndex + ".log")); } writeLogIndex(logIndex, true); openFile(files.getLast(), false); } catch (Exception ex) { Logger.getLogger(CustomFileStreamHandler.class.getName()).log(Level.SEVERE, null, ex); } } /** * 讀取已經記錄的日志的時間信息 */ private LinkedList<File> getWritedLog() { LinkedList<File> fileList = new LinkedList<File>(); BufferedReader br = null; try { File file = new File(fileUrl); if (fileUrl.endsWith("/") || fileUrl.endsWith("/")) { //是個目錄 if (!file.exists()) { file.mkdirs(); } } if (file.isDirectory()) { //只有指定file存在且是個目錄 indexFile = new File(file.toString() + File.separator + "logindex"); } else { indexFile = new File(file.getParent() + File.separator + "logindex"); } if (!indexFile.exists()) { indexFile.createNewFile(); } FileReader fr = null; fr = new FileReader(indexFile); br = new BufferedReader(fr); String line = null; while ((line = br.readLine()) != null) { if (line.trim().length() != 0) { if (file.isDirectory()) { //是個目錄 fileList.add(new File(fileUrl + File.separator + "未命名" + "_" + line + ".log")); } else { fileList.add(new File(fileUrl + "_" + line + ".log")); } System.out.println("line == " + line); } } return fileList; } catch (Exception ex) { Logger.getLogger(CustomFileStreamHandler.class.getName()).log(Level.SEVERE, null, ex); } finally { try { br.close(); } catch (IOException ex) { Logger.getLogger(CustomFileStreamHandler.class.getName()).log(Level.SEVERE, null, ex); } } return null; } /** * 寫入日志索引 * @param logIndex 日志所以 * @param isAppend 是否添加到索引文件中 */ private void writeLogIndex(String logIndex, boolean isAppend) { File file = new File(fileUrl); BufferedWriter bw = null; try { FileWriter fw = null; if (file.isDirectory()) { //是個目錄 //是個目錄 fw = new FileWriter(new File(file.toString() + File.separator + "logindex"), isAppend); } else { fw = new FileWriter(new File(file.getParent() + File.separator + "logindex"), isAppend); } bw = new BufferedWriter(fw); bw.newLine(); bw.write(logIndex, 0, logIndex.length()); bw.flush(); } catch (Exception ex) { Logger.getLogger(CustomFileStreamHandler.class.getName()).log(Level.SEVERE, null, ex); } finally { try { bw.close(); } catch (IOException ex) { Logger.getLogger(CustomFileStreamHandler.class.getName()).log(Level.SEVERE, null, ex); } } } /** * 檢查當前日志時間 * 刪除過期日志,並檢查日志索引中是否包含了現在日期 */ private void checkLogFile() { try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String trace = sdf.format(new Date().getTime()); boolean isIncludeNow = false; LinkedList<File> overdueLog = new LinkedList<File>(); //過期的日志文件 long nowDate = sdf.parse(trace).getTime(); for (File file : files) { if (file.toString().indexOf(trace) != -1) { isIncludeNow = true; } if ((nowDate - sdf.parse(file.toString(), new ParsePosition(file.toString().lastIndexOf('_') + 1)).getTime()) / (86400 * 1000) > 5) { overdueLog.add(file); //System.err.println("將被刪除的日志為 " + file); } } //刪除過期日志記錄, 並重寫日志索引文件 if (overdueLog.size() != 0) { files.removeAll(overdueLog); indexFile.delete(); indexFile.createNewFile(); String fileStr = null; for (File file : files) { fileStr = file.toString(); writeLogIndex(fileStr.substring(fileStr.lastIndexOf('_') - 1, fileStr.lastIndexOf('.')), true); } //刪除過期文件 for (File file : overdueLog) { file.delete(); } } //如果沒有包含當天的日期同時日志需要添加到文件末尾,則添加當天日期的日志文件 if (!isIncludeNow && append) { File file = new File(fileUrl); String logIndex = 1 + "_" + trace; if (file.isDirectory()) { //是個目錄 files.add(new File(fileUrl + File.separator + "未命名_" + logIndex + ".log")); } else { files.add(new File(fileUrl + "_" + logIndex + ".log")); } writeLogIndex(logIndex, true); } } catch (Exception ex) { Logger.getLogger(CustomFileStreamHandler.class.getName()).log(Level.SEVERE, null, ex); } } /** * 發布日志信息 */ @Override public synchronized void publish(LogRecord record) { super.publish(record); super.flush(); if (limit > 0 && msOut.written >= limit) { getLastFile(); } } /** * 抄自FileHandler的實現,用於跟蹤寫入文件的字節數 * 這樣以便提高效率 */ private class MeteredStream extends OutputStream { private OutputStream out; //記錄當前寫入字節數 private int written; MeteredStream(OutputStream out, int written) { this.out = out; this.written = written; } public void write(int b) throws IOException { out.write(b); written++; } @Override public void write(byte buff[]) throws IOException { out.write(buff); written += buff.length; } @Override public void write(byte buff[], int off, int len) throws IOException { out.write(buff, off, len); written += len; } @Override public void flush() throws IOException { out.flush(); } @Override public void close() throws IOException { out.close(); } } }
(2)自定義日志輸出格式化器
package com.bsk.log; import java.io.PrintWriter; import java.io.StringWriter; import java.security.AccessController; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.Formatter; import java.util.logging.LogRecord; public class CustomFormatter extends Formatter { //當前是第幾條記錄 private int logCount; //時間 private Date dat = new Date(); //參數 private Object[] args = new Object[1]; //消息格式化器 private MessageFormat formatter; //時間參數 private String format = "日期 {0,date} 時間{0,time}"; //行分格符 private String lineSeparator = AccessController.doPrivileged(new sun.security.action.GetPropertyAction("line.separator")); /** * @param 日志記錄器 * @return 返回格式化好的日志內容 */ @Override public String format(LogRecord record) { StringBuffer sb = new StringBuffer(); dat.setTime(record.getMillis()); args[0] = dat; StringBuffer text = new StringBuffer(); if (formatter == null) { formatter = new MessageFormat(format); } formatter.format(args, text, null); //sb.append("【第 " + (logCount++) + " 條記錄】" + lineSeparator); //sb.append(text); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String trace = sdf.format(new Date().getTime()); sb.append("[--|--][DEBUG] [" +trace).append("]"); sb.append(" "); /*if (record.getSourceClassName() != null) { sb.append("源文件名 " + record.getSourceClassName()); } else { sb.append("日志名 " + record.getLoggerName()); } if (record.getSourceMethodName() != null) { sb.append(" "); sb.append("方法名 " + record.getSourceMethodName()); }*/ sb.append(" "); String message = formatMessage(record); sb.append(record.getLevel().getLocalizedName()); sb.append(": "); sb.append(message); sb.append(lineSeparator); if (record.getThrown() != null) { try { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); record.getThrown().printStackTrace(pw); pw.close(); sb.append(sw.toString()); } catch (Exception ex) { } } return sb.toString(); } }
(3) 測試代碼
package com.bsk.doctor.log; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; public class HuanXinLog { private static Logger fileLogger; static { fileLogger = Logger.getLogger("com.bsk.doctor.util"); fileLogger.setLevel(Level.INFO); Handler[] hs = fileLogger.getHandlers(); for (Handler h : hs) { h.close(); fileLogger.removeHandler(h); } try { //文件 日志文件名為mylog 日志最大寫入為4000個字節 保存5天內的日志文件 如果文件沒有達到規定大小則將日志文件添加到已有文件 CustomFileStreamHandler fh = new CustomFileStreamHandler("e:/log/huanxinlog", 0, 1000, true); fh.setEncoding("UTF-8"); fh.setFormatter(new CustomFormatter()); fileLogger.setUseParentHandlers(false); fileLogger.addHandler(fh); } catch (Exception ex) { ex.printStackTrace(); } } /** * Creates a new instance of MyLog */ private HuanXinLog() { } /** * 返回一個文件記錄實例 */ public static synchronized Logger getFileLogger() { return fileLogger; } public static void writeFileLogger(String info){ getFileLogger().log(Level.INFO, info); //getFileLogger().logp(Level.INFO, sourceClass, sourceMethod, info); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { fileLogger.logp(Level.INFO, "ewrwerwer", "werwerw", "記錄log?"); } /*String name = "insert into doc_huanxin_chatting_message (content,messageTimestamp,sender,addressee,timeStr,fromPhone,toPhone,questionId)"; String sqlTemp = name.substring(0,50); System.out.println(sqlTemp);*/ } }
