`
genius0182
  • 浏览: 37583 次
  • 性别: Icon_minigender_1
  • 来自: 沈阳
社区版块
存档分类
最新评论

对JDK log日志的扩展

阅读更多

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

    }
}

 

以上就是实现的全部代码

0
0
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    采用jdk实现的日志框架

    虽然JDK的内置日志框架提供了基础的日志功能,但其灵活性和可扩展性相较于Log4j或Logback等第三方框架略显不足。因此,开发者经常选择这些第三方库,以获得更丰富的特性,如更精细的配置选项、更高效的性能和更好的...

    log日志组件总结

    本文将深入探讨Java中的log日志组件,包括JDK内置的日志(jdkLog)、Apache的Commons Logging(commonLog)以及流行的log4j。了解这些组件的特点和用法,将提升你对Java日志处理的理解。 首先,我们来看看JDK内置的...

    日志版本-log4j2版本jar

    总的来说,Log4j2作为日志框架的新一代产品,提供了许多增强的功能和优化,能够更好地满足开发者对日志管理的需求。对于大型企业和复杂系统,正确配置和使用Log4j2可以极大地提升系统的可维护性和稳定性。

    log4j实用配置扩展

    log4j的主要优势在于其灵活性和可扩展性,允许用户通过配置文件自定义日志记录的方式,无需修改应用程序代码。 #### 二、日志记录的目的 在应用程序中记录日志主要有以下三个目的: 1. **监视代码中变量的变化...

    log4j日志写入redis扩展

    log4j日志写入redis扩展 一. 环境log4j 1.2.17 + jedis 2.9.0 + 1.2.31 + jdk8二. log4j.properties相关配置具体配置信息在resource下的配置文件log4j.properties:log4j.appender.D=org.apache.log4j....

    日志框架总结JUL、Log4j、Log4j2、Logback以及门面技术

    ### 日志框架总结:JUL、Log4j、Log4j2、Logback及门面技术 ...而对于复杂的企业级应用,则可能需要考虑使用更加强大和灵活的日志框架如Logback或Log4j2,并结合日志门面技术SLF4j来进一步增强系统的可维护性和扩展性。

    JDK Logger 简介

    虽然JDK Logger在简单场景下已经足够使用,但在大型项目中,可能需要更强大和灵活的日志框架,如Log4j或Logback。这些框架提供了更多功能,如异步日志处理、更高级的过滤规则和更丰富的插件体系。 ### 总结 JDK ...

    jdk1.8+kafka3.2 linux版本

    在Linux环境下搭建Kafka,你需要修改`config/server.properties`文件,配置包括broker.id(每个Kafka节点的唯一标识)、zookeeper.connect(指向Zookeeper集群的连接字符串)、log.dirs(Kafka日志数据存储路径)等...

    log4j2.zip

    Log4j 2相较于Log4j 1.x在性能、可扩展性和灵活性方面有了显著提升,引入了新的API和组件,优化了日志处理机制。 一、Log4j 2概述 Log4j 2的核心设计理念是提供高性能、低延迟的日志记录,并且支持异步日志记录,这...

    Log4j工程官方源码

    - **Filter**: 过滤器允许对日志事件进行条件筛选,根据预定义的规则决定哪些日志事件被处理。 - **Level**: 日志级别用于定义日志信息的严重性,通常包括DEBUG、INFO、WARN、ERROR和FATAL。 2. **Log4j的功能** ...

    Commons-logging + Log4j 使用

    这是因为`commons-logging`的接口使得代码对日志库的依赖变得透明,而`log4j`的强大功能则提供了实际的日志记录服务。这种组合并不会增加额外的学习和维护成本,反而简化了配置和使用。`commons-logging`会自动检测...

    log4j配置.doc

    在log4j推出时,其优异性能曾引起Sun公司注意,甚至考虑将其纳入JDK1.4中替代原有的日志工具,但因JDK1.4接近完成而未能实现。尽管如此,log4j凭借其优势在Java开发中占据了主导地位,成为首选的日志记录解决方案。 ...

    log-sys, 基于 spring 云 & Docker的分布式日志系统.zip

    总的来说,log-sys是一个结合了现代微服务架构理念和技术的分布式日志系统,它利用Spring Cloud的智能化服务治理和Docker的容器化优势,提供了高效、可扩展的日志解决方案。对于需要处理大规模日志数据的企业和...

    apache-log4j-2.7

    对于大型分布式系统而言,Log4j 2.7的可扩展性和灵活性使其成为理想的日志解决方案。 解压`apache-log4j-2.7-bin`压缩包后,通常会包含`log4j-api-2.7.jar`(API库)、`log4j-core-2.7.jar`(核心实现库)、文档、...

    Jdk1.8搭建SSh框架所需Jar包

    8. **其他依赖库**:如log4j.jar用于日志记录,commons-lang3.jar、commons-logging.jar等Apache Commons库,还有javassist.jar用于动态字节码操作。 在实际开发中,你可能还需要其他的jar包来支持额外的功能,如...

    jetty-jdk7使用版本

    同时,通过配置logback或log4j等日志框架,可以实现详细的日志记录,方便问题排查。 9. 安全性:Jetty支持基本的身份验证、SSL/TLS加密以及角色权限控制,确保Web应用的安全性。 10. 扩展性:Jetty的设计允许通过...

    log4J 的应用

    4. **JDK自带日志**:如果上述条件均不满足,则检查JDK版本是否支持日志记录功能(通常从JDK 1.4开始提供),如果是,则使用JDK自带的日志实现。 5. **SimpleLog**:最后,如果上述所有选项都不可用,则使用Commons-...

Global site tag (gtag.js) - Google Analytics