最近因为需要写了一个日志模块,主要功能是实现日志文件滚动(不限日志文件个数的滚动),还有就是记录日志信息。因为我功能有限,所以我没有选择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();
}
}
}
以上就是实现的全部代码