日志规范

简介

日志记录的目的

1).对程序运行情况进行记录和监控

2).在必要是可详细了解程序的内部运行状态

3).记录RPC调用请求响应,以便排除夸系统访问问题。

5).支持分布式日志收集,以便在分布式部署中能集中查看和分析日志

6).对系统行影响尽量小


java 日志框架选择

1)log4j

2)log4j2

3)logback

为了飞马眼的flume插件不用维护多份,统一使用log4j2进行日志打印

日志级别

1)DEBUG 调试,主要用于开发过程中日志,可以详细到走了哪些流程,流程中的变量是什么,日常生产环境不开启debug日志,当遇到无法断定的问题,info日志也无法查出问题,再开启定位问题

2)INFO 信息,记录主要业务流程信息,能在数据异常的情况下,判断代码走了哪些分支,主业务流程和不稳定流程都应该记录

3)WARN 警告,不会导致流程异常,可能存在潜在问题、不需要立即人为介入,可以支持每月盘点时再分析的问题。

4)ERROR 异常,预料之外的问题,程序没法走下去,或不应该走下去,都可以抛出异常日志

5)FATAL 致命,系统崩溃级别的异常,需要立即调查,例如数据库无法连接、zookeeper连接不上、程序启动失败等。

最佳实践

1.在一个对象中通常只使用一个Logger对象,Logger应该是private static final的,只有在少数需要在构造函数中传递logger的情况下才使用private final。

static final Logger logger = LoggerFactory.getLogger(Main.class);

2.应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架SLF4J中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Abc.class);

3.输出Exceptions的全部Throwable信息,因为logger.error(msg)和logger.error(msg,e.getMessage())这样的日志输出方法会丢失掉最重要的StackTrace信息。

void foo(){
    try{

    } catch(Exception e){
        logger.error(e.getMessage());//错误
        logger.error("bad things",e.getMessage());//错误
        logger.error("bad things",e);//正确
    }
}

4.对trace/debug/info级别的日志输出,尽量使用占位符的方式。

说明:logger.debug(“Processing trade with id: ” + id + ” symbol: ” + symbol); 如果日志级别是warn,上述日志不会打印,但是会执行字符串拼接操作,如果symbol是对象,会执行toString()方法,浪费了系统资源,执行了上述操作,最终日志却没有打印。

//正例:(占位符)
logger.debug("Processing trade with id: {} symbol : {} ", id, symbol);

5.不允许记录日志后又抛出异常,因为这样会多次记录日志,只允许记录一次日志。

void foo(){
    try{
        //do something...
    } catch(Exception e){
        logger.error("bad things",e);
        throw new BussException("bad things",e);
    }
}

6.异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么往上抛。

void foo(){
    try{
        //do something...
    } catch(Exception e){
        throw new BussException("各类参数或者对象toString" + "_" + e.getMessage(), e);
    }
}

7.可以使用warn日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从。注意日志输出的级别,error级别只记录系统逻辑出错、异常等重要的错误信息。如非必要,请不要在此场景打出error级别。

if(tradeNo == null){
    logger.warn("继续履约,交易单号为:null ");//正确
    logger.error("继续履约,交易单号为:null ");//错误
    throw new BusinessException(BreakStatusEnum.BREAK_PARAMETER_ERROR.getCode(),"交易单号错误");
}

8.不允许出现System print(包括System.out.println和System.error.println)语句。

void foo(){
    try{
        //do something...
    } catch(Exception e){
        System.out.println(e.getMessage()); //错误
        System.err.println(e.getMessage()); //错误``
        logger.error("bad things",e);//正确
    }
}

9.不允许出现printStackTrace。

void foo(){
    try{
        //do something...
    } catch(Exception e){
        e.printStackTrace(); //错误
        logger.error("bad things",e);//正确
    }
}

10.必须存在%eye,除开发环境,必须要包含<appender-ref ref="async_flume"/>

<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
    <appenders>
        <Console name="CONSOLE" target="SYSTEM_OUT">
            <PatternLayout pattern="%d %-5p %t %c.%M:%L %fmeye - %m%n" />
        </Console>

        <RollingFile name="FILE" fileName="${log.path}/inservice.log" append="true"
                     filePattern="${log.path}/config.log.%d{yyyy-MM-dd}">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %t %-5p [%c][%M:%L] %fmeye - %m%n" />
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
            </Policies>
        </RollingFile>

        <RollingFile name="ERROR_FILE" fileName="${log.path}/inservice_err.log" append="true"
                     filePattern="${log.path}/inservice_err.log.%d{yyyy-MM-dd}">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %t %-5p [%c][%M:%L] %fmeye - %m%n" />
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
            </Policies>
        </RollingFile>

        <FMEyeAsync name="async_flume" hosts="${flume.host}" depName="${flume.depName}"
                    application="${flume.application}" unsafeMode="true"
                    blocking="false" bufferSize="2048">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%l] [%rms] %fmeye - %m" />
        </FMEyeAsync>

    </appenders>

    <loggers>
        <root level="DEBUG">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="FILE"/>
            <appender-ref ref="ERROR_FILE"/>
            <appender-ref ref="async_flume"/>
        </root>
    </loggers>
</configuration>

11.远程过程调用(RPC)必须在日志中输出请求的request和response

void foo(){
    log.info("开始调用订单转权接口:{}", BeanJsonUtil.bean2Json(transWhouseIn));
    DataResponse<TransWhouseOut> executeResult = tradeOrderService.transWhouse(transWhouseIn);
    TransWhouseOut transWhouseOut = executeResult.getData();
    log.info("调用订单转权后返回的数据为:{}", BeanJsonUtil.bean2Json(executeResult));  
}

参考资料

[Log日志规范]http://www.cnblogs.com/kofxxf/p/3713472.html

[阿里java编码规范]http://git.dazong.com/TradeDept/DeptDoc/blob/master/%E6%A0%87%E5%87%86%E8%A7%84%E8%8C%83/%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/%E9%98%BF%E9%87%8Cjava%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83.pdf

[SLF4J]http://blog.youkuaiyun.com/ydpiaoyun/article/details/6717969

[SLF4J]https://www.slf4j.org/index.html

[java日志规范]https://www.linkedin.com/pulse/java%E6%97%A5%E5%BF%97%E8%A7%84%E8%8C%83-ding-lau

[log4j2]http://logging.apache.org/log4j/2.x/manual/api.html

鸣谢

本文摘录自飞马大宗贸易部*斌文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值