As Tomcat internally uses JUL to log , you can use the system property java.util.logging.config.file to specify the file path of the properties file. For the format of this properties file , you can refer to your JRE_HOME/lib/logging.properties (which is the default configuration file used by JUL)
However, JUL does not support the daily rotation . If you don't mind , you can use its java.util.logging.FileHandler to rotate the log files based on the log 's file size instead:
# Define the FileHandler
handlers= java.util.logging.FileHandler
# Configure the FileHandler
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 1024000
java.util.logging.FileHandler.count = 3
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.FileHandler.append=true
Then , each log file will has the limit size 1024000 bytes (1MB) , and maximum roll to 3 output log files . You can refer the Javadoc of java.util.logging.FileHandler for the details about the configuration.
You have to implemnt the File Handler if you want to support rotation on daily basis using JUL . I found a custom implemenation from this blog . I did not try it yet. You can refer it if you have any interest.
最近因为需要写了一个日志模块,主要功能是实现日志文件滚动(不限日志文件个数的滚动),还有就是记录日志信息。因为我功能有限,所以我没有选择log4j这样的日志扩展,而选择了jdk log扩展。
开发环境是netbean 6.8
下面是扩展JDK LOG的Handler的类 直接上代码了。
- import java.io.BufferedOutputStream;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.UnsupportedEncodingException;
- import java.io.Writer;
- import java.security.AccessController;
- import java.security.PrivilegedAction;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.logging.ErrorManager;
- import java.util.logging.Handler;
- import java.util.logging.Level;
- import java.util.logging.LogManager;
- import java.util.logging.LogRecord;
- import java.util.logging.SimpleFormatter;
- public class RollingFileHandler extends Handler {
- private MeteredStream meter;
- private boolean append;
- private int limit; // zero => no limit.
- private int count;
- private String pattern;
- private LogManager manager = LogManager.getLogManager();
- private boolean doneHeader;
- private Writer writerHandler;
- private OutputStream output;
- ErrorManager error;
- private static int index = 1;
- @Override
- public synchronized void flush() {
- if (writerHandler != null) {
- try {
- writerHandler.flush();
- } catch (Exception ex) {
- // We don't want to throw an exception here, but we
- // report the exception to any registered ErrorManager.
- reportError(null, ex, ErrorManager.FLUSH_FAILURE);
- }
- }
- }
- private synchronized void flushAndClose() throws SecurityException {
- manager.checkAccess();
- if (writerHandler != null) {
- try {
- if (!doneHeader) {
- writerHandler.write(getFormatter().getHead(this));
- doneHeader = true;
- }
- writerHandler.write(getFormatter().getTail(this));
- writerHandler.flush();
- writerHandler.close();
- } catch (Exception ex) {
- // We don't want to throw an exception here, but we
- // report the exception to any registered ErrorManager.
- reportError(null, ex, ErrorManager.CLOSE_FAILURE);
- }
- writerHandler = null;
- output = null;
- }
- }
- /**
- * Change the output stream.
- * <P>
- * If there is a current output stream then the <tt>Formatter</tt>'s
- * tail string is written and the stream is flushed and closed.
- * Then the output stream is replaced with the new output stream.
- *
- * @param out New output stream. May not be null.
- * @exception SecurityException if a security manager exists and if
- * the caller does not have <tt>LoggingPermission("control")</tt>.
- */
- protected synchronized void setOutputStream(OutputStream out) throws SecurityException {
- if (out == null) {
- throw new NullPointerException();
- }
- flushAndClose();
- output = out;
- doneHeader = false;
- String encoding = getEncoding();
- if (encoding == null) {
- writerHandler = new OutputStreamWriter(out);
- } else {
- try {
- writerHandler = new OutputStreamWriter(out, encoding);
- } catch (UnsupportedEncodingException ex) {
- // This shouldn't happen. The setEncoding method
- // should have validated that the encoding is OK.
- throw new Error("Unexpected exception " + ex);
- }
- }
- }
- /**
- * Set (or change) the character encoding used by this <tt>Handler</tt>.
- * <p>
- * The encoding should be set before any <tt>LogRecords</tt> are written
- * to the <tt>Handler</tt>.
- *
- * @param encoding The name of a supported character encoding.
- * May be null, to indicate the default platform encoding.
- * @exception SecurityException if a security manager exists and if
- * the caller does not have <tt>LoggingPermission("control")</tt>.
- * @exception UnsupportedEncodingException if the named encoding is
- * not supported.
- */
- public void setEncoding(String encoding)
- throws SecurityException, java.io.UnsupportedEncodingException {
- super.setEncoding(encoding);
- if (output == null) {
- return;
- }
- // Replace the current writer with a writer for the new encoding.
- flush();
- if (encoding == null) {
- writerHandler = new OutputStreamWriter(output);
- } else {
- writerHandler = new OutputStreamWriter(output, encoding);
- }
- }
- // A metered stream is a subclass of OutputStream that
- // (a) forwards all its output to a target stream
- // (b) keeps track of how many bytes have been written
- private class MeteredStream extends OutputStream {
- OutputStream out;
- int written;
- boolean doneHeader;
- Writer writer;
- MeteredStream(OutputStream out, int 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();
- }
- }
- private void open(File fname, boolean append) throws IOException {
- int len = 0;
- if (append) {
- len = (int) fname.length();
- }
- FileOutputStream fout = new FileOutputStream(fname.toString(), append);
- BufferedOutputStream bout = new BufferedOutputStream(fout);
- meter = new MeteredStream(bout, len);
- setOutputStream(meter);
- }
- // 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 = "%h/java%u.log";
- limit = 0;
- if (limit < 0) {
- limit = 0;
- }
- append = false;
- setLevel(Level.ALL);
- setFilter(null);
- setFormatter(new SimpleFormatter());
- try {
- setEncoding(null);
- } catch (Exception ex) {
- try {
- setEncoding(null);
- } catch (Exception ex2) {
- // doing a setEncoding with null should always work.
- // assert false;
- }
- }
- }
- /**
- * Construct a default <tt>FileHandler</tt>. This will be configured
- * entirely from <tt>LogManager</tt> properties (or their default values).
- * <p>
- * @exception IOException if there are IO problems opening the files.
- * @exception SecurityException if a security manager exists and if
- * the caller does not have <tt>LoggingPermission("control"))</tt>.
- * @exception NullPointerException if pattern property is an empty String.
- */
- public RollingFileHandler() throws IOException, SecurityException {
- manager.checkAccess();
- configure();
- openFiles();
- }
- /**
- * Initialize a <tt>FileHandler</tt> to write to the given filename.
- * <p>
- * The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
- * properties (or their default values) except that the given pattern
- * argument is used as the filename pattern, the file limit is
- * set to no limit, and the file count is set to one.
- * <p>
- * There is no limit on the amount of data that may be written,
- * so use this with care.
- *
- * @param pattern the name of the output file
- * @exception IOException if there are IO problems opening the files.
- * @exception SecurityException if a security manager exists and if
- * the caller does not have <tt>LoggingPermission("control")</tt>.
- * @exception IllegalArgumentException if pattern is an empty string
- */
- public RollingFileHandler(String pattern) throws IOException, SecurityException {
- if (pattern.length() < 1) {
- throw new IllegalArgumentException();
- }
- manager.checkAccess();
- configure();
- this.pattern = pattern;
- this.limit = 0;
- this.count = 1;
- openFiles();
- }
- /**
- * Initialize a <tt>FileHandler</tt> to write to the given filename,
- * with optional append.
- * <p>
- * The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
- * properties (or their default values) except that the given pattern
- * argument is used as the filename pattern, the file limit is
- * set to no limit, the file count is set to one, and the append
- * mode is set to the given <tt>append</tt> argument.
- * <p>
- * There is no limit on the amount of data that may be written,
- * so use this with care.
- *
- * @param pattern the name of the output file
- * @param append specifies append mode
- * @exception IOException if there are IO problems opening the files.
- * @exception SecurityException if a security manager exists and if
- * the caller does not have <tt>LoggingPermission("control")</tt>.
- * @exception IllegalArgumentException if pattern is an empty string
- */
- public RollingFileHandler(String pattern, boolean append) throws IOException, SecurityException {
- if (pattern.length() < 1) {
- throw new IllegalArgumentException();
- }
- manager.checkAccess();
- configure();
- this.pattern = pattern;
- this.limit = 0;
- this.count = 1;
- this.append = append;
- openFiles();
- }
- /**
- * Initialize a <tt>FileHandler</tt> to write to a set of files. When
- * (approximately) the given limit has been written to one file,
- * another file will be opened. The output will cycle through a set
- * of count files.
- * <p>
- * The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
- * properties (or their default values) except that the given pattern
- * argument is used as the filename pattern, the file limit is
- * set to the limit argument, and the file count is set to the
- * given count argument.
- * <p>
- * The count must be at least 1.
- *
- * @param pattern the pattern for naming the output file
- * @param limit the maximum number of bytes to write to any one file
- * @param count the number of files to use
- * @exception IOException if there are IO problems opening the files.
- * @exception SecurityException if a security manager exists and if
- * the caller does not have <tt>LoggingPermission("control")</tt>.
- * @exception IllegalArgumentException if limit < 0, or count < 1.
- * @exception IllegalArgumentException if pattern is an empty string
- */
- public RollingFileHandler(String pattern, int limit, int count)
- throws IOException, SecurityException {
- if (limit < 0 || count < 1 || pattern.length() < 1) {
- throw new IllegalArgumentException();
- }
- manager.checkAccess();
- configure();
- this.pattern = pattern;
- this.limit = limit;
- this.count = count;
- openFiles();
- }
- /**
- * Initialize a <tt>FileHandler</tt> to write to a set of files
- * with optional append. When (approximately) the given limit has
- * been written to one file, another file will be opened. The
- * output will cycle through a set of count files.
- * <p>
- * The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
- * properties (or their default values) except that the given pattern
- * argument is used as the filename pattern, the file limit is
- * set to the limit argument, and the file count is set to the
- * given count argument, and the append mode is set to the given
- * <tt>append</tt> argument.
- * <p>
- * The count must be at least 1.
- *
- * @param pattern the pattern for naming the output file
- * @param limit the maximum number of bytes to write to any one file
- * @param count the number of files to use
- * @param append specifies append mode
- * @exception IOException if there are IO problems opening the files.
- * @exception SecurityException if a security manager exists and if
- * the caller does not have <tt>LoggingPermission("control")</tt>.
- * @exception IllegalArgumentException if limit < 0, or count < 1.
- * @exception IllegalArgumentException if pattern is an empty string
- *
- */
- public RollingFileHandler(String pattern, int limit, int count, boolean append)
- throws IOException, SecurityException {
- if (limit < 0 || pattern.length() < 1) {
- throw new IllegalArgumentException();
- }
- manager.checkAccess();
- configure();
- this.pattern = pattern;
- this.limit = limit;
- this.count = count;
- this.append = append;
- openFiles();
- }
- // Private method to open the set of output files, based on the
- // configured instance variables.
- private void openFiles() throws IOException {
- if (append) {
- File f = new File(pattern);
- try {
- open(f, true);
- } catch (IOException ex) {
- reportError(null, ex, ErrorManager.OPEN_FAILURE);
- }
- } else {
- rotate();
- }
- }
- // Rotate the set of output files
- private synchronized void rotate() {
- File target;
- File file;
- Level oldLevel = getLevel();
- setLevel(Level.OFF);
- close();
- Date date = new Date();
- SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH_mm_ss");
- String dateFormat = format.format(date);
- boolean renameSucceeded = true;
- if (count > 0) {
- file = new File(pattern + '_' + dateFormat + '.' + count);
- if (file.exists()) {
- index++;
- target = new File(pattern + '_' + dateFormat + "." + index);
- renameSucceeded = file.renameTo(target);
- }
- for (int i = count - 1; i >= 1 && renameSucceeded; i--) {
- file = new File(pattern + "." + i);
- if (file.exists()) {
- target = new File(pattern + '.' + (i + 1));
- renameSucceeded = file.renameTo(target);
- }
- }
- if (renameSucceeded) {
- // Rename fileName to fileName.1
- index = 1;
- target = new File(pattern + '_' + dateFormat + "." + 1);
- file = new File(pattern);
- renameSucceeded = file.renameTo(target);
- //
- // if file rename failed, reopen file with append = true
- //
- if (!renameSucceeded) {
- File f = new File(pattern);
- append = true;
- try {
- open(f, append);
- } catch (IOException ex) {
- reportError(null, ex, ErrorManager.OPEN_FAILURE);
- }
- }
- }
- }
- if (renameSucceeded) {
- File f = new File(pattern);
- try {
- open(f, false);
- } catch (IOException ex) {
- reportError(null, ex, ErrorManager.OPEN_FAILURE);
- }
- }
- setLevel(oldLevel);
- }
- /**
- * Format and publish a <tt>LogRecord</tt>.
- *
- * @param record description of the log event. A null record is
- * silently ignored and is not published
- */
- public synchronized void publish(LogRecord record) {
- if (!isLoggable(record)) {
- return;
- }
- String msg;
- try {
- msg = getFormatter().format(record);
- } catch (Exception ex) {
- // We don't want to throw an exception here, but we
- // report the exception to any registered ErrorManager.
- reportError(null, ex, ErrorManager.FORMAT_FAILURE);
- return;
- }
- try {
- if (!doneHeader) {
- writerHandler.write(getFormatter().getHead(this));
- doneHeader = true;
- }
- if (writerHandler == null) {
- File f = new File(pattern);
- open(f, append);
- }
- writerHandler.write(msg);
- } catch (Exception ex) {
- // We don't want to throw an exception here, but we
- // report the exception to any registered ErrorManager.
- reportError(null, ex, ErrorManager.WRITE_FAILURE);
- }
- flush();
- if (limit > 0 && meter.written >= limit) {
- // We performed access checks in the "init" method to make sure
- // we are only initialized from trusted code. So we assume
- // it is OK to write the target files, even if we are
- // currently being called from untrusted code.
- // So it is safe to raise privilege here.
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
- rotate();
- return null;
- }
- });
- }
- }
- /**
- * Close all the files.
- *
- * @exception SecurityException if a security manager exists and if
- * the caller does not have <tt>LoggingPermission("control")</tt>.
- */
- public synchronized void close() throws SecurityException {
- flushAndClose();
- }
- private static class InitializationErrorManager extends ErrorManager {
- Exception lastException;
- public void error(String msg, Exception ex, int code) {
- lastException = ex;
- }
- }
- // Private native method to check if we are in a set UID program.
- private static native boolean isSetUID();
- }
然后是Util类的代码
- import java.io.IOException;
- import java.util.logging.Handler;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- public class Utils {
- private static Logger log;
- /**
- * @serial Class that issued logging call
- */
- protected static String sourceClassName;
- /**
- * @serial Method that issued logging call
- */
- protected static String sourceMethodName;
- private transient boolean needToInferCaller;
- static {
- inferCaller();
- Handler handler = configure("log.log",1000,1,true);
- try {
- log = Logger.getLogger(sourceClassName);
- log.addHandler(handler);
- log.setLevel(Level.ALL);
- } catch (Exception ex) {
- System.out.println("can't init the Logger, caused by: " + ex);
- }
- }
- public static void debug(String info) {
- inferCaller();
- log.logp(Level.SEVERE, sourceClassName, sourceMethodName, info);
- }
- public static void debug(String info,Throwable thrown){
- inferCaller();
- log.logp(Level.SEVERE, sourceClassName, sourceMethodName, info,thrown);
- }
- public static void info(String info) {
- inferCaller();
- log.logp(Level.INFO, sourceClassName, sourceMethodName, info);
- }
- public static void info(String info,Throwable thrown){
- inferCaller();
- log.logp(Level.INFO, sourceClassName, sourceMethodName, info,thrown);
- }
- public static void warning(String info) {
- inferCaller();
- log.logp(Level.WARNING, sourceClassName, sourceMethodName, info);
- }
- public static void warning(String info,Throwable thrown){
- inferCaller();
- log.logp(Level.WARNING, sourceClassName, sourceMethodName, info,thrown);
- }
- public static void error(String info) {
- inferCaller();
- log.logp(Level.SEVERE, sourceClassName, sourceMethodName, info);
- }
- public static void error(String info,Throwable thrown){
- inferCaller();
- log.logp(Level.SEVERE, sourceClassName, sourceMethodName, info,thrown);
- }
- private static void init() {
- inferCaller();
- log = Logger.getLogger(Utils.sourceClassName);
- }
- // Private method to infer the caller's class and method names
- private static void inferCaller() {
- // needToInferCaller = false;
- // Get the stack trace.
- StackTraceElement stack[] = (new Throwable()).getStackTrace();
- // First, search back to a method in the Logger class.
- int ix = 0;
- while (ix < stack.length) {
- StackTraceElement frame = stack[ix];
- String cname = frame.getClassName();
- if (cname.equals("com.lhsm.logger.Utils")) {
- stack[ix] = stack[2];
- break;
- }
- ix++;
- }
- // Now search for the first frame before the "Logger" class.
- while (ix < stack.length) {
- StackTraceElement frame = stack[ix];
- String cname = frame.getClassName();
- if (!cname.equals("com.lhsm.logger.Utils")) {
- // We've found the relevant frame.
- sourceClassName = cname;
- sourceMethodName = frame.getMethodName();
- return;
- }
- ix++;
- }
- // We haven't found a suitable frame, so just punt. This is
- // OK as we are only committed to making a "best effort" here.
- }
- /**
- *
- * @param pattern 为生成的输出文件名称指定一个模式。
- * @param limit 指定要写入到任意文件的近似最大量(以字节为单位)。如果该数为 0,则没有限制(默认为无限制)。.不能小于0
- * @param count (1为不限滚动),这里指定个数滚动文件没有实现
- * @param append 指定是否应该将 FileHandler 追加到任何现有文件上。
- * @return
- */
- public static Handler configure(String pattern,int limit,int count,boolean append) {
- Handler handler = null;
- if (limit < 0 || pattern.length() < 1) {
- throw new IllegalArgumentException();
- }
- try {
- handler = new RollingFileHandler(pattern, limit, count, append);
- } catch (IOException ex) {
- Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
- } catch (SecurityException ex) {
- Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
- }
- return handler;
- }
- /**
- * @return the sourceClassName
- */
- public String getSourceClassName() {
- if (needToInferCaller) {
- inferCaller();
- }
- return sourceClassName;
- }
- /**
- * @return the sourceMethodName
- */
- public String getSourceMethodName() {
- if (needToInferCaller) {
- inferCaller();
- }
- return sourceMethodName;
- }
- }
下面是一个测试Foo类
- public class Foo {
- private int i =0;
- public Foo(int i) {
- this.i = i;
- }
- public void doIt() {
- Utils.debug("Debug..."+i);
- Utils.info("Info..."+i);
- Utils.warning("Warn..."+i);
- Utils.error("Error..."+i);
- }
- public void doFooIt() {
- Utils.debug("Debug..."+i);
- Utils.info("Info..."+i);
- Utils.warning("Warn..."+i);
- Utils.error("Error..."+i);
- }
- }
- public class LogTest {
- public static void main(String[] args) {
- for (int i = 0; i < 10000; i++) {
- Foo foo = new Foo(i);
- foo.doIt();
- foo.doFooIt();
- }
- }
- }
浙公网安备 33010602011771号