zoukankan      html  css  js  c++  java
  • JAVA日志系统

    JAVA日志系统

    @(博客文章)[java|大数据]

    java有大量的框架用于日志输出,常见的包含slf4j, log4j, logback, logging等.

    一、slf4j

    slf4j仅仅是一个门面(facet),它不包含详细的实现,而是将一些log4j,java.logging等实现包装成统一的接口。
    

      commons-logging和slf4j都是日志的接口。供用户使用,而没有提供实现!

      log4j,logback等等才是日志的真正实现。

      当我们调用接口时,接口的工厂会自己主动寻找恰当的实现,返回一个实现的实例给我服务。这些过程都是透明化的,用户不须要进行不论什么操作!

      这里有个小故事。当年Apache说服 log4j以及其他的日志来依照commons-logging的标准编写,可是因为commons-logging的类载入有点问题,实现起来也不友 好,因此log4j的作者就创作了slf4j。也因此而与commons-logging两分天下。至于究竟使用哪个。由用户来决定吧。

      这样。slf4j出现了,它通过简单的实现就能找到符合自己接口的实现类,假设不是满足自己标准的日志。能够通过一些中间实现比方上面的slf4j-log4j12.jar来进行适配。

    好。言归正传,怎样使用slf4j?

    (一)经常使用演示样例

    1、在pom.xml中加入下面内容

    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.0</version>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.0</version>
    </dependency>
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    </dependency>
    

    2、创建log4j.properties文件,详细配置见http://www.cnblogs.com/lujinhong2/p/4637219.html

    注意文件必须在config文件夹下(对于maven项目,log4j.properties位于src/main/resources下面)

    ### set log levels ###
    log4j.rootLogger = debug ,  stdout ,  D ,  E
    
    ### 输出到控制台 ###
    log4j.appender.stdout = org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.Target = System.out
    log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern =  %d{ABSOLUTE} %5p %c{1}:%L - %m%n
    
    ### 输出到日志文件 ###
    log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.D.File = logs/log.log
    log4j.appender.D.Append = true
    ## 输出DEBUG级别以上的日志
    log4j.appender.D.Threshold = DEBUG 
    log4j.appender.D.layout = org.apache.log4j.PatternLayout
    log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
    
    ### 保存异常信息到单独文件 ###
    log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
     ## 异常日志文件名称
    log4j.appender.E.File = ./error.log
    log4j.appender.E.Append = true
    ## 仅仅输出ERROR级别以上的日志!!!
    log4j.appender.E.Threshold = ERROR 
    log4j.appender.E.layout = org.apache.log4j.PatternLayout
    log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %l:%c:%t:%r ] - [ %p ]  %m%n
    

    3、在java文件里调用

    package org.lujinhong.javademo.slf4jdemo;
    
    import java.io.File;
    import java.io.IOException;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * @author jinhong-lu
     * @date 2015年7月9日 下午4:34:49
     * @Description:
     */
    public class Slf4jDemo {
        private static final Logger LOG = LoggerFactory.getLogger(Slf4jDemo.class);
    
    
        public static void main(String[] args) {
    
            // 使用此变量会生成文件成功
            String fileName = "1.txt";
            // 使用此变量会生成文件失败
            // String fileName = "/tt/1.txt";
            try {
                new File(fileName).createNewFile();
                LOG.info("create file " + fileName + "!");
            } catch (IOException e) {
                e.printStackTrace();
                LOG.error("create file " + fileName + " fail!!!!" + e.getMessage());
            }
    
        }
    
    }
    

    (二)其他演示样例

    首先,项目中必须要包含slf4j-api.jar,此外,还应该包含slf4j为详细实现所提供的适配器(如slf4j-log4j12.jar)。以及那个详细实现的jar包(如log4j-1.**.jar)。

    我们以下面代码为例:

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    public class Slf4jDemo {
    private static final Logger LOG = LoggerFactory.getLogger(Slf4jDemo.class);

    public static void main(String[] args) {
        LOG.error("Error Message!");
        LOG.warn("Warn Message!");
        LOG.info("Info Message!");
        LOG.debug("Debug Message!");
        LOG.trace("Trace Message!");
    }
    

    }
    因为所使用的详细实现不同,日志输出也有不同的结果。

    这也反应了通过使用slf4j。使得能够方便的替换日志系统。

    1、Slf4j-simple

    slf4j自带的一个简单实现。可用于小项目中,但无法配置日志级别等。

    官方文档中的描写叙述为:Binding for Simple implementation, which outputs all events to System.err. Only messages of level INFO and higher are printed. This binding may be useful in the context of small applications.

    在项目的build_path中加入slf4j-1.6.6.jar与slf4j-simple-1.6.6.jar。

    输出结果例如以下:

    2 [main] ERROR Slf4jDemo - Error Message!
    2 [main] WARN Slf4jDemo - Warn Message!
    2 [main] INFO Slf4jDemo - Info Message!
    

    2、slf4j-jdk

    使用jkd自带的日志系统,在项目的build_path中加入slf4j-1.6.6.jar与slf4j-jdk14-1.6.6.jar。

    输出结果例如以下:

    二月 16, 2015 11:09:36 下午 Slf4jDemo main
    严重: Error Message!
    二月 16, 2015 11:09:36 下午 Slf4jDemo main
    警告: Warn Message!
    二月 16, 2015 11:09:36 下午 Slf4jDemo main
    信息: Info Message!
    

    3、slf4j-log4j
    log4j是眼下用得最多的日志系统。它更适用于大型项目。
    在项目的build_path中加入slf4j-1.6.6.jar与slf4j-log4j-1.6.6.jar,以及log4j的详细实现,如log4j-1.2.16.jar。

    输出结果例如以下:

    log4j:WARN No appenders could be found for logger (Slf4jDemo).
    log4j:WARN Please initialize the log4j system properly.
    log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
    

    能够通过配置文件配置输出日志的级别。

    (三)一些注意事项

    1、注意build_path中不能有多个日志实现,否则会导致slf4j不知道该使用哪个实现,从而出现下面错误

    SLF4J: Class path contains multiple SLF4J bindings.
    SLF4J: Found binding in [jar:file:/Users/liaoliuqing/99_Project/1_myCodes/5_JavaEEDemo/lib/slf4j-log4j12-1.6.6.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    SLF4J: Found binding in [jar:file:/Users/liaoliuqing/99_Project/1_myCodes/5_JavaEEDemo/lib/slf4j-jdk14-1.6.6.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
    SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
    log4j:WARN No appenders could be found for logger (Slf4jDemo).
    log4j:WARN Please initialize the log4j system properly.
    log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
    

    3、有个非常大的坑:

    因为非常多项目都会使用log4j作为日志框架,而在一个项目的引用包中的log4j配置文件有可能将自定义的log4j.properties文件覆盖。

    假设发现自己的log4j配置文件不生效,则使用:

    java -Dlog4j.debug 主类

    来检查眼下载入了哪个配置文件,然后将其删除就可以。

    另外,即使使用了

    PropertyConfigurator.configure(“log4j2.properties”);
    指定配置文件,也有可能被第三方jar包使用相同语句覆盖了。

    二、log4j

    log4j有2个版本号。1.x和2.x,2.x较为完好,可是眼下大家都习惯了用1.x,并且非常多开源项目都使用了1.x。所以一般使用1.x就能够了。

    关于2.x的使用,请參考http://www.cnblogs.com/lujinhong2/p/4637295.html

    1.x要注意版本号的问题,一般使用1.2.17配合slfj的1.7.12版本号就可以。仅仅要不相差太多都没问题。

    (一)使用java向rsyslog发送日志

    基本用法

    1、开通rsyslog远程UDP訪问

    vi /etc/rsyslog.conf

    将下面两段前面的#号去掉

    #$ModLoad imudp  
    #$UDPServerRun 514  
    

    假设原来没有话则加上这2行

    2,建立存放log的文件地址
    vi /etc/rsyslog.conf
    加入下面两段

    local2.info       /var/log/login_info.log  
    local2.debug       /var/log/login_debug.log  
    

    3,开通防火墙
    将UDP端口514对外开放出来

    4,重新启动rsyslog
    service rsyslog restart

    5,log4j的配置
    将日志同一时候输出到syslog和console

    log4j.rootLogger =ALL,systemOut,SYSLOG
    log4j.appender.systemOut = org.apache.log4j.ConsoleAppender 
    log4j.appender.systemOut.layout = org.apache.log4j.PatternLayout 
    log4j.appender.systemOut.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n 
    log4j.appender.systemOut.Threshold = DEBUG 
    log4j.appender.systemOut.ImmediateFlush = TRUE 
    log4j.appender.systemOut.Target = System.out 
    log4j.appender.SYSLOG=org.apache.log4j.net.SyslogAppender  
    log4j.appender.SYSLOG.syslogHost=192.168.172.114
    log4j.appender.syslog.Threshold=DEBUG  
    log4j.appender.SYSLOG.layout=org.apache.log4j.PatternLayout  
    log4j.appender.SYSLOG.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n  
    log4j.appender.SYSLOG.Header=true
    log4j.appender.SYSLOG.Facility=local2   
    

    6、写java代码

    package com.lujinhong.demo.log4j;
    
    import org.apache.log4j.Logger;
    
    public class Log4jDemo {
        private static Logger LOG  =  Logger.getLogger(Log4jDemo. class );
    
      public static void main(String[] args) {
        //System.setProperty("LOGDIR","/Users/liaoliuqing/Downloads/");
        LOG.info("lujinhong !!INFO MESSAGE!!");
        LOG.error("ERROR MESSAGE!!!!");
        LOG.debug("DEBUG MESSAGE!");
        LOG.warn("WARN MESSAGE!!");
    
        new MyJavaClass().adder(2, 3);
      }
    }
    

    7,执行应用程序,能够在/var/log下看到不同级别的日志信息。如login_info.log和login_debug.log

    不使用配置文件

    有时候,log4j的配置文件会互相覆盖,真的非常烦,因此能够将配置写到代码中去
    1、定义一个单例类,将相关配置写入:

    package com.lujinhong.demo.log4j;
    
    import org.apache.log4j.Level;
    import org.apache.log4j.Logger;
    import org.apache.log4j.PatternLayout;
    import org.apache.log4j.net.SyslogAppender;
    
    
    public class MySysLogger {
        //日志级别
        private static Level level = Level.INFO;
    
    
        private static Logger LOG = null;
    
        public static synchronized Logger getInstance() {
            if (LOG == null) {
                new MySysLogger();
            }
            return LOG;
        }
    
        private MySysLogger() {
    
            LOG = Logger.getRootLogger();
    
            LOG.setLevel(level);
            SyslogAppender appender = new SyslogAppender();
            appender.setName("ma30");
    
            appender.setSyslogHost("192.168.172.98");
            appender.setLayout(new PatternLayout(
                    "%r [%t] %-5p %c%x: - %m%n"));
            appender.setHeader(true);
            appender.setFacility("local7");
            //appender.setWriter(new PrintWriter(System.out));
            //假设是文件是RollingFileAppender:setWriter(new PrintWriter(new File("F:/test/_debug.log")));
            LOG.addAppender(appender);
        }
    }
    

    2、在须要日志输出的类中加入下面语句

    private static Logger LOG = MySysLogger.getInstance();
    

    然后就能够使用LOG变量进行日志输出了

    吐槽几句。log4j的坑啊….

    (1)CLASSPATH中不能有多个log4j的版本号本,否则有有奇形怪状的NoSuchMethod, NoSuchFiled。 NoClassDefineFound等异常。明明是太多了。还告诉你没有

    (2)与slf4j的搭建。必须版本号一致,如slf4j-1.7.2相应log4j-1.2.17

    (3)配置文件啊,假设你引用的第三方包有log4j.properties。而又没有提供给你编辑,那恭喜你,慢慢调吧。把log4j的配置写入代码吧。不要用配置文件了

    (4) 假设你打算仅仅使用log4j,而不使用slf4j作包装,切记classpath中仅仅能有log4j,不能有log4j-over-slf4j-1.7.12.jar这样的包,不然会出现各种各样的错误,如:
    NoSuchMethod, NoSuchFiled。 NoClassDefineFound, IncompatibleClassChangeError: Implementing class

    三、logging

    直接一个演示样例

    (1)在你的代码中添加一个类,代码见后面。

    普通情况下仅仅须要改动输出日志的文件名称称就能够了。


    (2)在不论什么须要输出日志的类中加入一个变量,private static Logger LOG = MyLogger.getInstance();
    然后就能够使用LOG变量来做日志输出了。

    说明一下:
    1、storm从0.9.1后使用的日志架构是slf4j+logbakc,代替了原先的log4j。

    我们尝试使用log4j作日志框架,发现log4j.properties被storm某个jar包里面的配置覆盖了,导致自定义的log4j.properties怎么都不生效,然后尝试logback也有相似问题,定义的cluster.xml没有给出worker进程的日志配置。
    2、使用java.util.logging配置文件也能够,便在分布式系统中的配置载入比較复杂,并且java.util.logging非常坑。非常easy写错。比方每一行的最后都不能有空格等。

    package com.netease.sytopology.util;

    import java.io.IOException;
    import java.util.logging.ConsoleHandler;
    import java.util.logging.FileHandler;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import java.util.logging.SimpleFormatter;
    /**
    * @author lujinhong
    * @date 2015年7月25日 下午3:19:56
    * @Description:
    */
    public class MyLogger {
    //文件路径及文件名称
    private static String logFile = “/home/hadoop/storm/logs/userlog/ma30_filter.log”;
    //日志级别
    private static Level level = Level.INFO;
    //每一个日志文件的大小。单位为M。
    private static int logFileSize = 100;
    //保存日志文件的数据
    private static int logFileCount = 10;
    //logger的名称
    private static String logName = “com.lujinhong.demo”;
    private static Logger LOG = null;

    public static synchronized Logger getInstance() {
    if (LOG == null) {
    new MyLogger();
    }
    return LOG;
    }

    private MyLogger() {
    LOG = Logger.getLogger(logName);
    FileHandler fileHandler = null;
    try {
    fileHandler = new FileHandler(logFile,logFileSize*1024*1024,logFileCount,true);
    } catch (SecurityException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    fileHandler.setLevel(level);
    fileHandler.setFormatter(new SimpleFormatter());
    LOG.removeHandler(new ConsoleHandler());
    LOG.addHandler(fileHandler);
    }

    }

    四、使用slf4j+log4j2向rsyslog发送日志

    (一)rsyslog配置

    1、在/etc/rsyslog.d中新建storm.conf

    内容例如以下:

    $WorkDirectory /home/data/log/
    
    $MaxMessageSize 64k
    
    $ModLoad imudp
    $UDPServerRun 514
    
    $template stromtemplate, "/home/data/log/storm_%STRUCTURED-DATA:R,ERE,1:.*category="(.+)".*programname.*--end%_%$YEAR%%$MONTH%%$DAY%.log"
    
    $template stromformat, "[%TIMESTAMP:1:10:date-rfc3339% %TIMESTAMP:12:19:date-rfc3339%]  %syslogseverity-text:::uppercase% %STRUCTURED-DATA:R,ERE,2:.*category="(.+)".*programname="(.+)".*--end% %msg%
    "
    
    $EscapeControlCharactersOnReceive off
    $FileOwner hadoop
    $FileGroup hadoop
    $FileCreateMode 0644
    $DirCreateMode 0755
    $Umask 0022
    
    local7.*                        -?

    stromtemplate;stromformat local7.* ~

    2、改动/etc/rsyslog.conf

    改动下面3行:

    *.*;auth,authpriv.none;cron.none,local7.none -/var/log/syslog
    kern.*,local7.noe                      -/var/log/kern.log
    *.=info;*.=notice;*.=warn;
            auth,authpriv.none;
            cron,daemon.none;
            mail,news.none,local7.none      -/var/log/messages
    

    声明local7的日志不会发送到syslog, kern.log,message。否则日志量太大会撑爆。

    此时rsyslog就已经配置完毕,它会将发送至local7的日志解释后放到/home/data/log中。

    (二)java程序中输出日志

    1、加入依赖

    以maven为例。加入下面内容:

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.1</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>log4j-over-slf4j</artifactId>
        <version>1.6.6</version>
    </dependency>
    

    其他编译方式则加入相应的jar包。

    2、准备配置文件

    在resources文件夹下创建log4j2.xml,内容例如以下:

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="warn" name="test" packages="">
        <Appenders>
            <Syslog name="test" format="RFC5424" host="192.168.1.100" port="514"
                protocol="UDP" appName="storm" includeMDC="true" mdcId="mdc"
                facility="LOCAL7" enterpriseNumber="18060" newLine="true" 
                messageId="Audit" id="App">
                <LoggerFields>
                    <KeyValuePair key="category" value="%c"/>             
                    <KeyValuePair key="programname" value="%C"/>
                </LoggerFields>
                </Syslog>           
        </Appenders>
        <Loggers>
            <Logger name="test" level="INFO">
            <AppenderRef ref="test"/>
         </Logger>
            <Root level="INFO">
                <AppenderRef ref="test"/>
            </Root>
        </Loggers>
    </Configuration>
    

    3、输出日志

    (1)先获取Logger对象

    public static Logger LOG = LoggerFactory.getLogger("mytest");
    

    当中參数建议为拓扑名称。

    (2)然后你就能够轻松的输出日志了

    logger.error("INFO ljh_test again!");
    logger.info("INFO ljh_test message info");
    logger.debug("INFO ljh_test message debug");
    logger.warn("INFO ljh_test message warn");
    

    完整代码例如以下:https://github.com/lujinhong/log4j2demo

    package com.lujinhong.demo.log4j2;
    
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class Log4j2RsyslogDemo {
    
        public static Logger logger = LoggerFactory.getLogger("mytest");
    
        public static void main(String[] args) {
    
            System.out.println("Hello World2!");
    
            logger.error("INFO ljh_test again!");
            logger.info("INFO ljh_test message info");
            logger.debug("INFO ljh_test message debug");
            logger.warn("INFO ljh_test message warn");
        }
    }
    
  • 相关阅读:
    寒假学习10
    寒假学习9
    寒假学习8
    寒假学期7
    寒假学习6
    寒假学习5
    寒假学习4
    Notification通知栏的使用
    Service的使用
    BroadcastReceive的使用
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/6994697.html
Copyright © 2011-2022 走看看