- 浏览: 24897 次
- 性别:
- 来自: 深圳
最新评论
-
sp42:
还是比较复杂 虽然功能比较多可以看看 tomcat 的http ...
为java.util.logging自定义文件处理器及日志输出格式 -
cai521chen:
不错很是相信 学习了!谢谢分享!
jbpm相关 -
zengqun89:
楼主写得太好了,学习了
为java.util.logging自定义文件处理器及日志输出格式 -
niwtsew:
喜欢楼主的inferCaller()的实现。jdk自带的inf ...
为java.util.logging自定义文件处理器及日志输出格式
最近闲来无事,刚好拾起了来公司的第一个项目,发现里面的logging功能有点弱,竟然不支持按日期文件名输出,更惨的是由于引入了些apache的公用了包,一个项目里竟然同时出现logging,log4j,commons-logging,这年头引用下开源包还要用不同的loger.记得log4j好像支持输出的文件按日期来分类的,jdk自带的logging好像还没有,于是google了一把,发现JAVA中自定义日志输出格式及自定义文件处理器的实现 ,功能还不错,有些bug及不合理的地方,于是再修改下,凑合用用.
直接代码:
FileStreamHandler实现
package com.xunlei.demo.util; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.TreeSet; import java.util.logging.Filter; import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.logging.StreamHandler; /** * 自定义日志文件处理器---日期及文件大小存储,并自动清除过期日志文件 * * @author ZengDong * @since 2008-9-11下午11:16:39 */ public class XLFileStreamHandler extends StreamHandler { /** * 抄自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 close() throws IOException { out.close(); } public void flush() throws IOException { out.flush(); } 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 write(int b) throws IOException { out.write(b); written++; } } private class XLLogFile extends File { private static final long serialVersionUID = 952141123094287978L; private Date date; private String dateString; private int sid; public int getSid() { return this.sid; } public void setSid(int sid) { this.sid = sid; } public Date getDate() { return this.date; } public void setDate(Date date) { this.date = date; } public String getDateString() { return this.dateString; } public void setDateString(String dateString) { this.dateString = dateString; } public int compareTo(File another) { XLLogFile ano = (XLLogFile) another; int dateComResult = date.compareTo(ano.getDate()); if (dateComResult == 0) { return sid - ano.getSid(); } return dateComResult; } SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); public XLLogFile(String pathname) { super(pathname); try { int dot = pathname.lastIndexOf('.'); int split = pathname.lastIndexOf(splitDateIndexChar); int underline = pathname.lastIndexOf('_'); dateString = pathname.substring(underline + 1, split); String numStr = pathname.substring(split + 1, dot); date = sdf.parse(dateString); sid = Integer.valueOf(numStr); } catch (Exception e) { System.err.println("log对应文件夹中包含了不符合XLLOG格式的文件!!"); e.printStackTrace(); } } } private static final String undifine = "xlcallcenter"; // 是否将日志写入已存在的日志文件中 private boolean append; // 保存几天之内的日志文件 // 时间间隔小于等于0时表明不删除历史记录 private int dateinterval = 5; // 保存存在的日志文件 private Map<String, TreeSet<XLLogFile>> files; // 每个日志希望写入的最大字节数,如果日志达到最大字节数则当天日期的一个新的编号的日志文件将被创建,最新的日志记录在最大编号的文件中 // 文件大小为小于等于0时表明不限制日志文件大小 private int limit = 1048576 * 5; // 输出流 private MeteredStream msOut; // 文件路径, 可以是个目录或希望的日志名称,如果是个目录则日志为"callcenter_zd" // 指定日志名称时不需要包括日期,程序会自动生成日志文件的生成日期及相应的编号 private String pattern = "./log/xunleidemo"; private char splitDateIndexChar = '#'; public XLFileStreamHandler() throws Exception { configure(); openWriteFiles(); } /** * 初始化自定义文件流处理器 * * @param fileUrl * 文件路径, 可以是个目录或希望的日志名称,如果是个目录则日志为"callcenter_zd" * 指定日志名称时不需要包括日期,程序会自动生成日志文件的生成日期及相应的编号 * @param limit * 每个日志希望写入的最大字节数,如果日志达到最大字节数则当天日期的一个新的编 * 号的日志文件将被创建,最新的日志记录在最大编号的文件中 * @param dateinterval * 保存几天之内的日志文件 * @param append * 是否将日志写入已存在的日志文件中 * @throws java.lang.Exception */ public XLFileStreamHandler(String fileUrl, int limit, int dateinterval, boolean append) throws Exception { super(); this.pattern = fileUrl; this.limit = limit; this.dateinterval = dateinterval; this.append = append; openWriteFiles(); } /** * 检查当前日志时间,删除过期日志 */ private void deleteExpiredLog() { try { // 今天作为基准 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String today = sdf.format(new Date().getTime()); // 删除过期日志 for (String keyDate : files.keySet()) { if ((sdf.parse(today).getTime() - sdf.parse(keyDate).getTime()) / (86400 * 1000) > dateinterval) { TreeSet<XLLogFile> traceDateFiles = files.get(keyDate); for (File deletingFile : traceDateFiles) { // if(deletingFile.exists()) { deletingFile.delete(); // } } files.remove(today); } } } catch (Exception ex) { Logger.getLogger(XLFileStreamHandler.class.getName()).log( Level.SEVERE, null, ex); } } // Private method to configure a FileHandler from LogManager // properties and/or default values as specified in the class // javadoc. private void configure() { LogManager manager = LogManager.getLogManager(); String cname = getClass().getName(); // 获得pattern pattern = manager.getProperty(cname + ".pattern"); if (pattern == null) { pattern = "./log/xunleidemo"; } // 获得limit String limitVal = manager.getProperty(cname + ".limit"); if (limitVal == null) { limit = 1048576 * 5; } else { try { limit = Integer.parseInt(limitVal.trim()); // if (limit < 0) { // limit = 1048576 * 5; // } } catch (Exception ex) { limit = 1048576 * 5; } } // 获得formatter String formatVal = manager.getProperty(cname + ".formatter"); if (formatVal == null) { setFormatter(new XLLogFormatter()); } else { try { Class clz = ClassLoader.getSystemClassLoader().loadClass( formatVal); setFormatter((Formatter) clz.newInstance()); } catch (Exception ex) { // We got one of a variety of exceptions in creating the // class or creating an instance. // Drop through. } } // 获得append String appendVal = manager.getProperty(cname + ".append"); if (appendVal == null) { append = false; } else { if (appendVal.equalsIgnoreCase("true") || appendVal.equals("1")) { append = true; } else if (appendVal.equalsIgnoreCase("false") || appendVal.equals("0")) { append = false; } } // 获得level String levelVal = manager.getProperty(cname + ".level"); if (levelVal == null) { setLevel(Level.ALL); } else { try { setLevel(Level.parse(levelVal.trim())); } catch (Exception ex) { setLevel(Level.ALL); } } // 获得dateinterval String dateintervalVal = manager.getProperty(cname + ".dateinterval"); if (dateintervalVal == null) { dateinterval = 5; } else { try { dateinterval = Integer.parseInt(dateintervalVal.trim()); if (dateinterval <= 0) { dateinterval = 5; } } catch (Exception ex) { dateinterval = 5; } } // 获得filter String filterVal = manager.getProperty(cname + ".filter"); if (filterVal == null) { setFilter(null); } else { try { Class clz = ClassLoader.getSystemClassLoader().loadClass( filterVal); setFilter((Filter) clz.newInstance()); } catch (Exception ex) { // We got one of a variety of exceptions in creating the // class or creating an instance. // Drop through. } } // 获得encoding String encodingVal = manager.getProperty(cname + ".encoding"); if (encodingVal == null) { try { setEncoding(null); } catch (Exception ex2) { // doing a setEncoding with null should always work. // assert false; } } else { try { setEncoding(encodingVal); } catch (Exception ex) { try { setEncoding(null); } catch (Exception ex2) { // doing a setEncoding with null should always work. // assert false; } } } } /** * 将离现在最近的文件作为写入文件的文件 例如 xunleidemo_2008-02-19#30.log * xunleidemo表示自定义的日志文件名,2008-02-19表示日志文件的生成日期,30 表示此日期的第30个日志文件 */ private void openLastFile(boolean append) { try { super.close(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String today = sdf.format(new Date().getTime()); // 如果没有包含当天的日期,则添加当天日期的日志文件 boolean isFirstLogToday = false; if (!files.containsKey(today)) { String logIndex = today + splitDateIndexChar + 1; TreeSet<XLLogFile> todayFiles = new TreeSet<XLLogFile>(); todayFiles.add(getNewFile(logIndex)); files.put(today, todayFiles); isFirstLogToday = true; } // 获得今天最大的日志文件编号 XLLogFile todayLastFile = files.get(today).last(); int maxLogCount = todayLastFile.getSid(); String logIndex = today + splitDateIndexChar + (maxLogCount + (isFirstLogToday ? 0 : (append ? 0 : 1))); XLLogFile wantWriteFile = getNewFile(logIndex); files.get(today).add(wantWriteFile); openFile(wantWriteFile, append); } catch (Exception ex) { Logger.getLogger(XLFileStreamHandler.class.getName()).log( Level.SEVERE, null, ex); } } /** * 根据logIndex要建立File * * @param logIndex * 包含今天日期及编号,如2008-09-11#1 * @return File */ private XLLogFile getNewFile(String logIndex) { File file = new File(pattern); XLLogFile wantWriteFile = null; StringBuilder filePath = new StringBuilder(pattern); if (file.isDirectory()) { filePath.append(File.separator); filePath.append(undifine); } filePath.append('_'); filePath.append(logIndex); filePath.append(".log"); wantWriteFile = new XLLogFile(filePath.toString()); return wantWriteFile; } /** * 读取已经记录的日志的时间信息 */ private Map<String, TreeSet<XLLogFile>> getRecodedLog() { Map<String, TreeSet<XLLogFile>> filesMap = new HashMap<String, TreeSet<XLLogFile>>(); try { // 建立相关目录 File file = new File(pattern); File fileDir = null; if (pattern.endsWith("/") || pattern.endsWith("\\")) { // 是个目录 fileDir = file; if (!file.exists()) { file.mkdirs(); } } else { // 带了前缀 File parentFile = new File(file.getParent()); fileDir = parentFile; // 父目录不存在则新建目录 if (!parentFile.exists()) { parentFile.mkdirs(); } } // 加入到filesMap中 for (File contentFile : fileDir.listFiles()) { if (contentFile.isFile()) { XLLogFile newXLLogFile = new XLLogFile(contentFile .getAbsolutePath()); TreeSet<XLLogFile> fileListToDate = filesMap .get(newXLLogFile.getDateString()); if (fileListToDate == null) { fileListToDate = new TreeSet<XLLogFile>(); } fileListToDate.add(newXLLogFile); filesMap.put(newXLLogFile.getDateString(), fileListToDate); } } files = filesMap; return filesMap; } catch (Exception ex) { Logger.getLogger(XLFileStreamHandler.class.getName()).log( Level.SEVERE, null, ex); } return null; } /** * 打开需要写入的文件 * * @param file * 需要打开的文件 * @param append * 是否将内容添加到文件末尾 */ private void openFile(File file, boolean append) throws Exception { 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); } /** * 获得将要写入的文件 */ private synchronized void openWriteFiles() throws Exception { if (!getLevel().equals(Level.OFF)) { getRecodedLog(); deleteExpiredLog(); openLastFile(append); } } /** * 发布日志信息 */ public synchronized void publish(LogRecord record) { super.publish(record); super.flush(); if (getLevel().equals(Level.OFF)) { return; } if (limit > 0 && msOut.written >= limit) { openLastFile(false); } } public static void main(String[] args) { Logger fileLogger = Logger.getLogger(XLFileStreamHandler.class .getName()); fileLogger.setLevel(Level.INFO); Handler[] hs = fileLogger.getHandlers(); for (Handler h : hs) { h.close(); fileLogger.removeHandler(h); } try { // 文件 日志文件名为mylog 日志最大写入为4000个字节 保存5天内的日志文件 // 如果文件没有达到规定大小则将日志文件添加到已有文件 XLFileStreamHandler fh = new XLFileStreamHandler("d:\\mylog\\test", 200, 10, false); fh.setLevel(Level.OFF); fh.setEncoding("UTF-8"); fh.setFormatter(new XLLogFormatter()); fileLogger.setUseParentHandlers(false); fileLogger.addHandler(fh); } catch (Exception ex) { ex.printStackTrace(); } for (int i = 0; i < 30; i++) { fileLogger.log(Level.INFO, "我被记录了吗?"); } } }
附带用到的
日志格式
package com.xunlei.demo.util; import java.io.PrintWriter; import java.io.StringWriter; import java.text.DecimalFormat; import java.util.Calendar; import java.util.logging.LogRecord; /** * Print a brief summary of the LogRecord in a human readable. The summary will * typically be on a single line (unless it's too long :) ... what I meant to * say is that we don't add any line breaks). * * @author ZengDong */ public class XLLogFormatter extends java.util.logging.Formatter { private static String lineSeparator = System.getProperty("line.separator"); static long startTime = System.currentTimeMillis(); private static DecimalFormat threeDigFmt = new DecimalFormat("000"); private static DecimalFormat twoDigFmt = new DecimalFormat("00"); /** * Format the given LogRecord. * * @param record * the log record to be formatted. * @return a formatted log record */ // private final static String format = "{0,date} {0,time}"; // private MessageFormat formatter; // Date dat = new Date(); // private Object args[] = new Object[1]; public synchronized String format(LogRecord record) { StringBuilder sb = new StringBuilder(); // 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(text); // current time // Calendar cal = Calendar.getInstance(); // int millis = cal.get(Calendar.MILLISECOND); // sb.append('.').append(threeDigFmt.format(millis)).append(' '); // current time Calendar cal = Calendar.getInstance(); int hour = cal.get(Calendar.HOUR_OF_DAY); int minutes = cal.get(Calendar.MINUTE); int seconds = cal.get(Calendar.SECOND); int millis = cal.get(Calendar.MILLISECOND); sb.append(twoDigFmt.format(hour)).append(':'); sb.append(twoDigFmt.format(minutes)).append(':'); sb.append(twoDigFmt.format(seconds)).append('.'); sb.append(threeDigFmt.format(millis)).append(' '); // log level sb.append(record.getLevel().getLocalizedName()); sb.append(": "); // caller method int lineNumber = inferCaller(record); String loggerName = record.getLoggerName(); if (loggerName == null) loggerName = record.getSourceClassName(); if (loggerName.startsWith("com.xunlei.callcenter.")) { sb.append(loggerName.substring("com.xunlei.callcenter.".length())); } else sb.append(record.getLoggerName()); if (record.getSourceMethodName() != null) { sb.append('.'); sb.append(record.getSourceMethodName()); // include the line number if we have it. if (lineNumber != -1) sb.append("().").append(Integer.toString(lineNumber)); else sb.append("()"); } sb.append(' '); sb.append(record.getMessage()); 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(); } /** * Try to extract the name of the class and method that called the current * log statement. * * @param record * the logrecord where class and method name should be stored. * * @return the line number that the call was made from in the caller. */ private int inferCaller(LogRecord record) { // Get the stack trace. StackTraceElement stack[] = (new Throwable()).getStackTrace(); // the line number that the caller made the call from int lineNumber = -1; // First, search back to a method in the XLCallCenter Logger class. int ix = 0; while (ix < stack.length) { StackTraceElement frame = stack[ix]; String cname = frame.getClassName(); if (cname.equals("com.xunlei.demo.util.Logger")) { break; } ix++; } // Now search for the first frame before the XLCallCenter Logger class. while (ix < stack.length) { StackTraceElement frame = stack[ix]; lineNumber = stack[ix].getLineNumber(); String cname = frame.getClassName(); if (!cname.equals("com.xunlei.demo.util.Logger")) { // We've found the relevant frame. record.setSourceClassName(cname); record.setSourceMethodName(frame.getMethodName()); break; } ix++; } return lineNumber; } }
评论
3 楼
sp42
2017-06-13
还是比较复杂 虽然功能比较多
可以看看 tomcat 的
https://github.com/apache/tomcat/blob/TOMCAT_7_0_42/java/org/apache/juli/FileHandler.java
可以看看 tomcat 的
https://github.com/apache/tomcat/blob/TOMCAT_7_0_42/java/org/apache/juli/FileHandler.java
2 楼
zengqun89
2011-03-24
楼主写得太好了,学习了
1 楼
niwtsew
2009-12-16
喜欢楼主的inferCaller()的实现。jdk自带的inferCaller()只能找到java.util.logging.**这一层,太破了。
相关推荐
在Swing应用中,我们同样可以使用Java内置的`java.util.logging`包来输出错误日志。这篇文档主要讨论了如何在Java Swing应用中使用`Logger`来生成和管理错误日志。 `Logger`是Java的日志记录工具,它提供了多种级别...
`Logger`是Java标准库`java.util.logging`包提供的日志工具,它提供了多种级别的日志记录,如`SEVERE`、`WARNING`、`INFO`、`CONFIG`、`FINE`、`FINER`和`FINEST`。本篇文章将深入探讨Java中的`Logger`封装,以及...
例如,添加`java.util.logging.FileHandler.pattern`和`java.util.logging.FileHandler.limit`等属性,设置日志文件的命名规则(例如包含日期)以及每个文件的最大大小。 重起Tomcat后,新的配置才会生效。这意味着...
`logging.properties`文件用于配置Java原生日志工具的行为,包括日志输出的目的地、格式以及过滤规则等。默认情况下,Java安装目录下的`jre/lib/logging.properties`文件会被用来初始化日志系统。 ##### 4.1 读取...
2. **使用Java Util Logging (JUL)**:这是Java内置的日志框架,可以设置系统属性(如`java.util.logging.LogManager.config`指向配置文件路径)或直接调用`Logger`类的方法来控制日志级别。例如,你可以使用`java....
比如,`java.util.logging`的配置文件是`logging.properties`,其中定义了日志级别、处理器和输出格式等。开发者可以通过指定`java.util.logging.config.file`属性来使用自定义的配置文件。其他框架如Log4j和Logback...
6. **格式化日志**:可以创建`SimpleFormatter`或自定义`Formatter`来控制日志输出的格式。例如: ```java SimpleFormatter formatter = new SimpleFormatter(); fileHandler.setFormatter(formatter); ``` 7. ...
`MySimpleFormatter.java`可能定义了一个简单的日志格式化器,继承自`java.util.logging.Formatter`,并覆盖`format(LogRecord)`方法,以自定义日志输出的样式。默认的`SimpleFormatter`会输出一条简洁的消息,但...
总结来说,JavaUtil日志提供了一套完整的日志管理方案,包括创建日志记录器、设置处理器以及定制日志格式,使得开发者能够根据需求灵活地调整日志的输出方式和内容。在实际开发中,合理使用这些功能可以帮助调试、...
Juli,全称为Java Util Logging,是Tomcat自定义的日志框架,它取代了早期版本中的Apache Commons Logging。Juli提供了更高效、更可控的日志处理机制,允许开发者针对不同的日志需求进行精细化配置。与Java内置的...
4. **格式化日志**:JDK提供了`java.util.logging.Formatter`类,用于控制日志消息的输出格式。我们可以创建自定义的Formatter,以满足特定的格式需求,比如添加时间戳、线程ID、类名等信息。 5. **使用第三方日志...
在Java中实现syslog日志发送,我们可以使用开源库如`logback`或`java.util.logging`。以`logback`为例,我们需要配置`logback.xml`,添加一个syslog appender: ```xml <appender name="SYSLOG" class="ch.qos....
1. **Logger**: Ibatis提供了一个日志接口`org.apache.ibatis.logging.Log`,而`ibatis.util.LogFactory`则负责根据用户配置的实现(如Log4j、SLF4J或Java内置的日志系统)创建对应的日志实例,用于记录SQL执行和...
它的配置文件通常为logback.xml,同样支持多级别的日志输出和自定义布局。 3. **Java.util.logging**: Java内置的日志框架,简洁且易于集成。尽管功能相对较弱,但在小型项目或不希望引入额外依赖的场合,它是不错...
例如,`java.util.logging.ConsoleHandler`负责将日志输出到控制台,而`java.util.logging.FileHandler`则将日志写入文件。 5. **日志级别配置** - 通过调整日志级别的设置,可以控制哪些级别的信息被记录。例如,...
- **自定义处理器**:可以继承`java.util.logging.Handler`类,实现自己的日志处理逻辑。 5. **过滤器(Filter)** - **控制日志输出**:通过实现`java.util.logging.Filter`接口,可以过滤特定的日志事件,只让...
这个XML文件可能包含了日志记录级别设置、日志处理器(如控制台、文件或网络)的选择、过滤器的定义以及日志输出格式等。例如,你可以指定哪些类或包的日志应该被记录,以及这些日志应如何处理和存储。 在JDK的`...
在Java Web应用开发中,Tomcat作为常用的Servlet容器,其默认的日志系统是通过`catalina.out`文件记录所有标准输出和错误输出。当应用运行一段时间后,`catalina.out`文件可能会变得非常大,占用大量磁盘空间,这...
3. 日志配置:Java日志系统可以通过配置文件(如logging.properties)来定制日志的行为,如日志输出的格式、级别、处理器等。 二、常见Java日志框架 1. Log4j:Apache的Log4j是Java中最广泛使用的日志框架,提供了...
Spring Boot默认集成了多个流行的日志框架,如Logback、Log4j2和Java Util Logging(JUL)。其中,Logback是Spring Boot的默认日志系统,因为它在性能和灵活性方面表现优秀。如果想切换到其他日志框架,可以通过排除...