`
Donald_Draper
  • 浏览: 980989 次
社区版块
存档分类
最新评论

Log4j日志输出详解

阅读更多
Log4j初始化详解:http://donald-draper.iteye.com/blog/2332385
Log4j日志输出详解 :http://donald-draper.iteye.com/blog/2332395
slf4j + Log4j的使用:http://donald-draper.iteye.com/blog/2332407
上一篇我们说了log4j的初始化,Logger的实例为NOPLogger,所有Appender,委托给
rootLogger管理,今天我们来看一下,日志的打印输出。
日志输出源头为下一句
log.info("========test daily level info=========");

我们来看一下,这一句都做了些什么?
public final class NOPLogger extends Logger
public class Logger extends Category

而NOPLogger,Logger,没有info方法,来看Category
//Category
public class Category
    implements AppenderAttachable
{
  AppenderAttachableImpl aai;
  static 
    {
        FQCN = (org.apache.log4j.Category.class).getName();
    }
    //输出日志
    public void info(Object message)
    {
        //查看info日志,是否开启
        if(repository.isDisabled(20000))
            return;
	 //如果INFO,大于等于有效的日志级别,则输出日志
        if(Level.INFO.isGreaterOrEqual(getEffectiveLevel()))
            forcedLog(FQCN, Level.INFO, message, null);
    }
    //根据日志级别,输出日志
    protected void forcedLog(String fqcn, Priority level, Object message, Throwable t)
    {
        //将LoggingEvent,委托给AppenderS处理,
        callAppenders(new LoggingEvent(fqcn, this, level, message, t));
    }
    public void callAppenders(LoggingEvent event)
    {
        int writes;
        Category c;
        writes = 0;
        c = this;
_L3:
label0:
        {
            if(c == null)
                break; /* Loop/switch isn't completed */
            synchronized(c)
            {
                if(c.aai != null)
		    //Appenders处理日志输出事件
                    writes += c.aai.appendLoopOnAppenders(event);
                if(c.additive)
                    break label0;
            }
            break; /* Loop/switch isn't completed */
        }
        category;
        JVM INSTR monitorexit ;
          goto _L1
        exception;
        throw exception;
_L1:
        c = c.parent;
        if(true) goto _L3; else goto _L2
_L2:
        if(writes == 0)
            repository.emitNoAppenderWarning(this);
        return;
    }
}

//AppenderAttachableImpl
public class AppenderAttachableImpl
    implements AppenderAttachable
{
protected Vector appenderList;
 //遍历rootLooger的Appenders,每一个Appenders分别处理log输出事件
 public int appendLoopOnAppenders(LoggingEvent event)
    {
        int size = 0;
        if(appenderList != null)
        {
            size = appenderList.size();
            for(int i = 0; i < size; i++)
            {
                Appender appender = (Appender)appenderList.elementAt(i);
                appender.doAppend(event);
            }

        }
        return size;
    }
}

这里我们来看一下DailyRollingFileAppender
//DailyRollingFileAppender
public class DailyRollingFileAppender extends FileAppender
{
    static final int TOP_OF_TROUBLE = -1;
    static final int TOP_OF_MINUTE = 0;
    static final int TOP_OF_HOUR = 1;
    static final int HALF_DAY = 2;
    static final int TOP_OF_DAY = 3;
    static final int TOP_OF_WEEK = 4;
    static final int TOP_OF_MONTH = 5;
    private String datePattern;
    private String scheduledFilename;
    private long nextCheck;
    Date now;
    SimpleDateFormat sdf;
    RollingCalendar rc;
    int checkPeriod;
    static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
    public DailyRollingFileAppender(Layout layout, String filename, String datePattern)
        throws IOException
    {
        super(layout, filename, true);
        this.datePattern = "'.'yyyy-MM-dd";
        nextCheck = System.currentTimeMillis() - 1L;
        now = new Date();
        rc = new RollingCalendar();
        checkPeriod = -1;
        this.datePattern = datePattern;
	//构造是调用激活配置方法
        activateOptions();
    }
}

//AppenderSkeleton
public abstract class AppenderSkeleton
    implements Appender, OptionHandler
{
   //处理日志事件
public synchronized void doAppend(LoggingEvent event)
    {
        append(event);
    }
    //待子类拓展
   protected abstract void append(LoggingEvent loggingevent);
}

//AppenderSkeleton
public class WriterAppender extends AppenderSkeleton
{
   //处理日志事件
   public void append(LoggingEvent event)
    {
        if(!checkEntryConditions())
        {
            return;
        } else
        {
            subAppend(event);
            return;
        }
    }
    protected void subAppend(LoggingEvent event)
    {
        qw.write(layout.format(event));
        if(layout.ignoresThrowable())
        {
            String s[] = event.getThrowableStrRep();
            if(s != null)
            {
                int len = s.length;
                for(int i = 0; i < len; i++)
                {
		    //输出日志,关键是QuietWriter
                    qw.write(s[i]);
                    qw.write(Layout.LINE_SEP);
                }

            }
        }
        if(shouldFlush(event))
            qw.flush();
    }
    protected boolean immediateFlush;
    protected String encoding;
    protected QuietWriter qw;
}

下面看一下QuietWriter是什么?如何来的?
看DailyRollingFileAppender的构造方法中,调用了一个方法激活配置activateOptions
public DailyRollingFileAppender(Layout layout, String filename, String datePattern)
        throws IOException
    {
        super(layout, filename, true);
        this.datePattern = "'.'yyyy-MM-dd";
        nextCheck = System.currentTimeMillis() - 1L;
        now = new Date();
        rc = new RollingCalendar();
        checkPeriod = -1;
        this.datePattern = datePattern;
	//激活配置
        activateOptions();
    }
     public void activateOptions()
    {
        super.activateOptions();
        if(datePattern != null && fileName != null)
        {
            now.setTime(System.currentTimeMillis());
            sdf = new SimpleDateFormat(datePattern);
            int type = computeCheckPeriod();
            printPeriodicity(type);
            rc.setType(type);
            File file = new File(fileName);
            scheduledFilename = fileName + sdf.format(new Date(file.lastModified()));
        } else
        {
            LogLog.error("Either File or DatePattern options are not set for appender [" + name + "].");
        }
    }

查看FileAppender
public class FileAppender extends WriterAppender
{
public void activateOptions()
    {
        if(fileName != null)
        {
            try
            {
                setFile(fileName, fileAppend, bufferedIO, bufferSize);
            }
    }
    public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
        throws IOException
    {
        LogLog.debug("setFile called: " + fileName + ", " + append);
        if(bufferedIO)
            setImmediateFlush(false);
        reset();
        FileOutputStream ostream = null;
        try
        {
            ostream = new FileOutputStream(fileName, append);
        }
	//根据文件流,创建Writer
        Writer fw = createWriter(ostream);
        if(bufferedIO)
            fw = new BufferedWriter(fw, bufferSize);
	//设置QuietWriter的输出流
        setQWForFiles(fw);
        this.fileName = fileName;
        fileAppend = append;
        this.bufferedIO = bufferedIO;
        this.bufferSize = bufferSize;
        writeHeader();
        LogLog.debug("setFile ended");
    }
    //设置QuietWriter的输出流
    protected OutputStreamWriter createWriter(OutputStream os)
    {
        OutputStreamWriter retval = null;
        String enc = getEncoding();
        if(enc != null)
            try
            {
                retval = new OutputStreamWriter(os, enc);
            }
        if(retval == null)
            retval = new OutputStreamWriter(os);
        return retval;
    }
    //设置QuietWriter的输出流
     protected void setQWForFiles(Writer writer)
    {
        qw = new QuietWriter(writer, errorHandler);
    }
}

再看一下ConsoleAppender
//ConsoleAppender。
ublic class ConsoleAppender extends WriterAppender
{
    private static class SystemOutStream extends OutputStream
    {

        public void close()
        {
        }

        public void flush()
        {
            System.out.flush();
        }

        public void write(byte b[])
            throws IOException
        {
            System.out.write(b);
        }

        public void write(byte b[], int off, int len)
            throws IOException
        {
            System.out.write(b, off, len);
        }

        public void write(int b)
            throws IOException
        {
            System.out.write(b);
        }

        public SystemOutStream()
        {
        }
    }
     public ConsoleAppender(Layout layout, String target)
    {
        this.target = "System.out";
        follow = false;
        setLayout(layout);
        setTarget(target);
        activateOptions();
    }
    public void activateOptions()
    {
        if(follow)
        {
            if(target.equals("System.err"))
                setWriter(createWriter(new SystemErrStream()));
            else
	        //设置输出流
                setWriter(createWriter(new SystemOutStream()));
        } else
        if(target.equals("System.err"))
            setWriter(createWriter(System.err));
        else
            setWriter(createWriter(System.out));
        super.activateOptions();
    }
}

总结:
从上面的分析我们可以看出,log日志的输出,是遍历rootLogger的Appender来处理日志输出事件,Appender,首先确定日志级别是否大于RootLogger的日志级别,大于,则处理日志,而日志的输出委托给QuietWriter,而QuietWriter来源于DailyRollingFileAppender,
ConsoleAppender。
0
1
分享到:
评论

相关推荐

    SSM整合中的Log4j日志的配置详情

    Log4j 的核心组件包括日志记录器(Logger)、日志输出目的地(Appender)和日志格式化器(Layout)。 二、配置 Log4j.properties 文件 在 SSM 整合项目中,配置 Log4j.properties 文件是必不可少的步骤。该文件...

    log4j日志配置以及配置文件详解

    这是log4j框架的配置文件,使用Java Properties格式,用于定义日志输出的行为。主要包括以下几个核心部分: 1. **Logger**: 定义日志级别,例如DEBUG、INFO、WARN、ERROR和FATAL。日志级别决定了哪些信息会被记录。...

    tomcat下的log4j日志配置

    ### Tomcat 下的 Log4j 日志配置详解 在日常的 Web 开发中,日志记录对于调试问题、监控系统状态以及后期维护来说至关重要。在使用 Apache Tomcat 作为服务器时,合理配置日志框架(如 Log4j)能够极大地提高开发...

    Log4j将System.out搞到log4j中输出四

    《Log4j将System.out重定向到Log4j输出详解》 在日志管理中,Log4j是一款广泛使用的开源日志框架,它允许开发者灵活地控制日志信息的输出方式和级别。当我们习惯于使用`System.out.println()`进行调试时,如何将...

    Log4j日志配置说明,Log4j日志配置说明

    ### Log4j日志配置详解 #### 一、概述 Log4j 是一个基于 Java 的开源日志记录框架,由 Apache 软件基金会维护。它允许开发人员根据等级记录日志信息,使得用户能够控制日志信息的记录级别及去向。本文将通过一份...

    log4j入门、详解

    为了提高性能,Log4j提供了日志输出的条件判断,只有当日志级别高于或等于当前设置的级别时,才会执行日志输出操作。此外,还可以通过异步日志记录和使用更高效的布局策略来进一步提升性能。 总结来说,Log4j是一个...

    log4j配置文件详解

    #### 二、Log4j日志处理相关配置 ##### Log4j类图与基本组件 Log4j的架构设计围绕三大核心组件展开:日志记录器(Logger)、日志信息输出目的地(Appender)与日志信息输出格式(Layout)。通过灵活配置这三部分,...

    Log4j分包输出日志

    实现分包日志输出的关键在于配置文件(通常是log4j.properties或log4j.xml)。在配置文件中,我们可以定义多个Logger,并通过设置它们的name属性为类的包名来关联不同的日志输出策略。以下是一个简单的配置示例: `...

    log4j中配置日志文件相对路径方法(PDF)

    ### Log4j中配置日志文件相对路径方法详解 #### 概述 在软件开发过程中,日志记录是一项重要的功能,它有助于开发者调试程序、监控应用程序的运行状态以及追踪问题。`Log4j`作为一款优秀的日志管理工具,被广泛应用...

    log4J日志.zip

    在“log4j日志.zip”压缩包中,包含的可能是Log4j的配置文件(如log4j.properties或log4j.xml)。这个文件定义了日志的级别、输出位置和格式。例如: ```properties # log4j.properties 示例 log4j.rootLogger=...

    log4j日志jar包.rar

    **日志库Log4j详解** 日志在软件开发中起着至关重要的作用,它能够帮助开发者追踪程序运行状态,定位错误,优化性能,并且在生产环境中为运维人员提供宝贵的诊断信息。Log4j,由Apache软件基金会开发,是Java编程...

    Log4j写入数据库详解

    接下来需要配置log4j.properties文件,以指定日志输出的具体设置。以下是一个示例配置: ```properties # 设置root logger的默认日志级别为INFO,并指定其输出目的地为stdout和db log4j.rootLogger=INFO, stdout, ...

    log4j技术详解

    通过使用Log4j,开发者可以轻松地控制日志信息的输出目的地、格式以及优先级。这种灵活性使得Log4j成为Java开发中非常受欢迎的日志框架之一。 - **日志信息的优先级**:日志级别用于区分日志的重要性,常见的优先级...

    Log4j日志管理系统简单使用说明

    ### Log4j日志管理系统简单使用说明 #### 一、Log4j简介 Log4j是一款流行的开源日志框架,由Apache软件基金会提供。它能够帮助开发者以灵活且高效的方式记录应用程序的日志信息。Log4j的核心组成部分包括:Loggers...

    log4j日志管理

    ### Log4j日志管理详解 #### 一、Log4j概述 Log4j是一款流行的开源日志框架,由Apache软件基金会提供。它允许开发者在应用程序中加入日志功能,以便于跟踪程序运行过程中的各种状态信息。Log4j的核心设计理念在于...

    Log4j 入门到详解[推荐].pdf

    ### Log4j从入门到详解知识点汇总 #### 1. Log4j简介 ...通过以上介绍,我们不仅了解了Log4j的基本使用方法,还深入了解了如何通过配置文件来精细控制日志输出的过程。这对于开发高质量的应用程序至关重要。

    log4j日志的基本使用

    ### Log4j日志框架基本使用详解 #### 一、Log4j概述 Log4j是Apache的一个开源项目,被广泛应用于Java应用中作为日志记录工具。它提供了一个简单而强大的日志记录机制,可以帮助开发者高效地追踪程序运行过程中的...

    SpringBoot框架配置log4j和log4j2的配置代码

    Log4j和Log4j2是两种广泛使用的Java日志框架,它们提供了灵活的日志配置和高性能的日志处理能力。本文将详细介绍如何在SpringBoot项目中配置Log4j和Log4j2。 ### SpringBoot与Log4j Log4j是Apache的一个开源项目,...

Global site tag (gtag.js) - Google Analytics