日志规范(个人总结)

日志作用

打印调试:即可以用日志来记录变量或者某一段逻辑。记录程序运行的流程,即程序运行了哪些代码,方便排查逻辑问题。
问题定位:程序出异常或者出故障时快速的定位问题,方便后期解决问题。因为线上生产环境无法 debug,在测试环境去模拟一套生产环境,费时费力。所以依靠日志记录的信息定位问题,这点非常重要。还可以记录流量,后期可以通过 ELK(包括 EFK 进行流量统计)。
用户行为日志:记录用户的操作行为,用于大数据分析,比如监控、风控、推荐等等。这种日志,一般是给其他团队分析使用,而且可能是多个团队,因此一般会有一定的格式要求,开发者应该按照这个格式来记录,便于其他团队的使用。当然,要记录哪些行为、操作,一般也是约定好的,因此,开发者主要是执行的角色。
根因分析(甩锅必备):即在关键地方记录日志。方便在和各个终端定位问题时,别人说时你的程序问题,你可以理直气壮的拿出你的日志说,看,我这里运行了,状态也是对的。这样,对方就会乖乖去定位他的代码,而不是互相推脱。

什么时候记录日志

系统初始化:系统或者服务的启动参数。核心模块或者组件初始化过程中往往依赖一些关键配置,根据参数不同会提供不一样的服务。务必在这里记录 INFO 日志,打印出参数以及启动完成态服务表述。
编程语言提示异常:如今各类主流的编程语言都包括异常机制,业务相关的流行框架有完整的异常模块。这类捕获的异常是系统告知开发人员需要加以关注的,是质量非常高的报错。应当适当记录日志,根据实际结合业务的情况使用 WARN 或者 ERROR 级别。
业务流程预期不符:除开平台以及编程语言异常之外,项目代码中结果与期望不符时也是日志场景之一,简单来说所有流程分支都可以加入考虑。取决于开发人员判断能否容忍情形发生。常见的合适场景包括外部参数不正确,数据处理问题导致返回码不在合理范围内等等。
系统核心角色,组件关键动作:系统中核心角色触发的业务动作是需要多加关注的,是衡量系统正常运行的重要指标,建议记录 INFO 级别日志,比如电商系统用户从登录到下单的整个流程;微服务各服务节点交互;核心数据表增删改;核心组件运行等等,如果日志频度高或者打印量特别大,可以提炼关键点 INFO 记录,其余酌情考虑 DEBUG 级别。
第三方服务远程调用:微服务架构体系中有一个重要的点就是第三方永远不可信,对于第三方服务远程调用建议打印请求和响应的参数,方便在和各个终端定位问题,不会因为第三方服务日志的缺失变得手足无措。

日志级别

主要使用如下的四个级别:

DEBUG:DEUBG 级别的主要输出调试性质的内容,该级别日志主要用于在开发、测试阶段输出。该级别的日志应尽可能地详尽,开发人员可以将各类详细信息记录到 DEBUG 里,起到调试的作用,包括参数信息,调试细节信息,返回值信息等等,便于在开发、测试阶段出现问题或者异常时,对其进行分析。
INFO:INFO 级别的主要记录系统关键信息,旨在保留系统正常工作期间关键运行指标,开发人员可以将初始化系统配置、业务状态变化信息,或者用户业务流程中的核心处理记录到INFO日志中,方便日常运维工作以及错误回溯时上下文场景复现。建议在项目完成后,在测试环境将日志级别调成 INFO,然后通过 INFO 级别的信息看看是否能了解这个应用的运用情况,如果出现问题后是否这些日志能否提供有用的排查问题的信息。
WARN:WARN 级别的主要输出警告性质的内容,这些内容是可以预知且是有规划的,比如,某个方法入参为空或者该参数的值不满足运行该方法的条件时。在 WARN 级别的时应输出较为详尽的信息,以便于事后对日志进行分析。
ERROR:ERROR 级别主要针对于一些不可预知的信息,诸如:错误、异常等,比如,在 catch 块中抓获的网络通信、数据库连接等异常,若异常对系统的整个流程影响不大,可以使用 WARN 级别日志输出。在输出 ERROR 级别的日志时,尽量多地输出方法入参数、方法执行过程中产生的对象等数据,在带有错误、异常对象的数据时,需要将该对象一并输出。

日志设置模板

<configuration><property name="LOG_PATH"
          value="${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}"/>


<springProperty scope="context" name="APP_NAME"
                source="spring.application.name" defaultValue="spring-boot-fusion"/>
<!-- 全局统一 pattern -->
<property name="LOG_PATTERN"
          value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}|${PID:- }|%level|${LOG_LEVEL_PATTERN:-%5p}|%t|%-40.40logger{39}: %msg%n"/>
<!-- 输出模式 file,滚动记录文件,先将日志文件指定到文件,当符合某个条件时,将日志记录到其他文件 -->
<appender name="fileInfo" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。-->
    <file>${LOG_PATH}/${APP_NAME}-info.log</file>
    <!--滚动策略  基于时间的分包策略 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <!-- yyyy-MM-dd 时间策略则为一天一个文件 -->
        <FileNamePattern>${LOG_PATH}/${APP_NAME}-info.%d{yyyy-MM-dd-HH}.%i.log</FileNamePattern>
        <!--日志文件保留小时数-->
        <MaxHistory>48</MaxHistory>
        <maxFileSize>1GB</maxFileSize>
        <totalSizeCap>20GB</totalSizeCap>
    </rollingPolicy>
    <!--  layout 负责把事件转换成字符串,格式化的日志信息的输出 -->
    <layout class="ch.qos.logback.classic.PatternLayout">
        <pattern>${LOG_PATTERN}</pattern>
    </layout>
    <!--级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志-->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <!--设置过滤级别-->
        <level>INFO</level>
        <!--用于配置符合过滤条件的操作-->
        <onMatch>ACCEPT</onMatch>
        <!--用于配置不符合过滤条件的操作-->
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>
</configuration>
日志记录的时机:

总结几个需要写日志的点:

  • 编程语言提示异常:如今各类主流的编程语言都包括异常机制,业务相关的流行框架有完整的异常模块。这类捕获的异常是系统告知开发人员需要加以关注的,是质量非常高的报错。应当适当记录日志,根据实际结合业务的情况使用warn或者error级别。
  • 业务流程预期不符:除开平台以及编程语言异常之外,项目代码中结果与期望不符时也是日志场景之一,简单来说所有流程分支都可以加入考虑。取决于开发人员判断能否容忍情形发生。常见的合适场景包括外部参数不正确,数据处理问题导致返回码不在合理范围内等等。
  • 系统核心角色,组件关键动作:系统中核心角色触发的业务动作是需要多加关注的,是衡量系统正常运行的重要指标,建议记录INFO级别日志,比如电商系统用户从登录到下单的整个流程;微服务各服务节点交互;核心数据表增删改;核心组件运行等等,如果日志频度高或者打印量特别大,可以提炼关键点INFO记录,其余酌情考虑DEBUG级别。
  • 系统初始化:系统或者服务的启动参数。核心模块或者组件初始化过程中往往依赖一些关键配置,根据参数不同会提供不一样的服务。务必在这里记录INFO日志,打印出参数以及启动完成态服务表述。
日志变量定义:

可以使用private static final Logger log = LoggerFactory.getLogger({SimpleClassName}.getClass());方法来创建log对象来使用

还可以通过使用Lombok中的注解@Slf4j,在类的头部上加上此注解,使用起来更加简洁一点,在重构代码尤其是修改类名的时候无需改动原有代码。

日志内容:

我们在写日志的时候,需要注意输出适当的内容。首先,尽量使用业务相关的描述。我们的程序是实现某种业务的,那么就好能描述清楚这个时候走到了业务过程的哪一步。
其次,避免在日志中输出一些敏感信息,例如用户名和密码。以及,要保持编码的一致。日志打印的信息需要有效性,不要随便打印例如

log.info("查询开始");
...
log.info("查询结束");
日志格式:

常见的日志格式中对于每一条日志应该含有的信息包括日期、时间、日志级别、代码位置、日志内容、错误码等信息。使用参数化形式 {} 占位,[] 进行参数隔离,这样的好处是可读性更高,而且只有真正准备打印的时候才会处理参数。例如:

log.info("具体要表达的意思 : [{占位符拼接字段用的}], 表达式二 : [{}], 表达式三 : [{}],.....,时间为 : [{}]", 表达式一的对象或者参数, 表达式二的对象或者参数, 表达式三对象或者参数....., 时间为当前时间精确到毫秒),最后拼接时间。
    
打印出来例子
14:35:41.683 (时间)[http-nio-18081-exec-4] INFO(错误级别)  c.r.a.t.c.ApiTerminalRecycleRecordController (代码位置)- [recoveryDataStatistics(那个方法),64(代码行数)] - 启动中的id为 : [00002586653], 参数为 : [Ticket(UserId=00002586653, UserName=15874948674, UserType=null, OrganId=null, OrganName=null, OpenId=om5HE5HdnnM8Bmd9ZY4mNhyofV50, SystemName=null, Scope=null, ExtInfo=null, language=null)], 时间为 : [20230828143541]

    
    
log.error格式
try {
    // ...
} catch (Exception e) {
    // 输出全部的内容
   	log.error("XX 发生异常 : [{}], 描述信息 : [{}] : [{}], 时间为", e, e.getMessage, 当前时间);
}
打印出来例子
14:35:41.682 (时间)[http-nio-18081-exec-4] ERROR (错误级别)c.r.a.t.c.ApiTerminalRecycleRecordController (代码位置)- [recoveryDataStatistics(那个方法),63(代码行数)] - 启动中的id为[00002586653]
    
log.error("XX 发生异常 : [{}], 描述信息 : [{}] : [{}], 时间为", e, e.getMessage, 当前时间);
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值