完整配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true">
<!-- 统一日志文件名 = 服务名 -->
<springProperty scope="context" name="spring.application.name" source="spring.application.name"/>
<!-- 从配置文件中获取日志文件位置 -->
<springProperty scope="context" name="logging.path" source="logging.path"/>
<!-- 从配置文件中获取root日志级别 -->
<springProperty scope="context" name="logging.level.root" source="logging.level.root"/>
<!-- 从配置文件中获取应用日志级别 -->
<springProperty scope="context" name="logging.level.app" source="logging.level.com.pingxx"/>
<!-- 从配置文件中获取历史日志存放天数 -->
<springProperty scope="context" name="logging.max.history" source="logging.logback.rollingpolicy.max-history"/>
<!-- 本地开发环境 控台日志输出 -->
<springProfile name="local">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level --- [%X{tid}][%20.20thread] %logger{36} : %msg%n</Pattern>
</layout>
</encoder>
</appender>
<logger name="com.pingxx" level="${logging.level.app}" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
<root level="${logging.level.root}">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<!-- dev | sit | uat | prod 生产环境日志规范 https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.logging-->
<springProfile name="dev | sit | uat | prod">
<appender name="LOGSTASH" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logging.path}/${spring.application.name}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${logging.path}/${spring.application.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>500MB</maxFileSize>
<maxHistory>${logging.max.history}</maxHistory>
https://blog.csdn.net/youngerson/article/details/103497917
<cleanHistoryOnStart>true</cleanHistoryOnStart>、
<totalSizeCap>30GB</totalSizeCap>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeContext>false</includeContext>
<includeCallerData>true</includeCallerData>
<customFields>{"module":"${spring.application.name}","hostname":"${HOSTNAME}"}</customFields>
<provider class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.logstash.TraceIdJsonProvider"/>
</encoder>
</appender>
<!-- 日志异步输出 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>1024</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="LOGSTASH"/>
</appender>
<!-- additivity="false"避免重复打印日志,浪费磁盘空间 -->
<logger name="com.pingxx" level="${logging.level.app}" additivity="false">
<appender-ref ref="ASYNC"/>
</logger>
<root level="${logging.level.root}">
<appender-ref ref="ASYNC"/>
</root>
</springProfile>
</configuration>
logging:
level:
root: info
com.pingxx: info
org.springframework.integration.redis.util.RedisLockRegistry: error
path: /tmp/${spring.application.name}
logback:
rollingpolicy:
max-history: 3
日志文件规范:
logback标签说明
标签,必填标签,用来指定最基础的日志输出级别 标签,添加 append 标签,通过使用该标签指定日志的收集策略 - name 属性指定 appender 命名
- class 属性指定输出策略,通常有两种,控制台输出和文件输出,文件输出就是将日志进行一个持久化。ConsoleAppender 将日志输出到控制台
标签,通过使用该标签指定过滤策略 标签指定过滤的类型 标签,使用该标签下的 标签指定日志输出格式 标签指定收集策略,比如基于时间进行收集 标签指定生成日志保存地址 通过这样配置已经实现了分类分天手机日志的目标了 有四个属性: - scope:声明该变量的作用域;
- name:声明该变量的名称;
- source:声明该变量绑定的环境变量中值的信息;
- defaultValue:声明该变量的默认值,即在环境变量中未找到信息时的值
的name属性可以指定环境,多个环境可以使用 “|” 分割
多环境日志配置:
<!-- 本地开发环境 控台日志输出 -->
<springProfile name="local">
...
</springProfile>
<!-- dev, sit, uat, prod生产环境日志规范 -->
<springProfile name="dev | sit | uat | prod">
...
</springProfile>
日志滚动切分策略:
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 历史日志全路径 -->
<fileNamePattern>${logging.path}/${spring.application.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 单日志文件大小 -->
<maxFileSize>500MB</maxFileSize>
<!-- 日志保存周期/日 -->
<maxHistory>${logging.max.history}</maxHistory>
<!-- 日志文件总体的最大值,达到之后就不再产生日志 -->
<totalSizeCap>30GB</totalSizeCap>
</rollingPolicy>
SkyWalking的全局唯一标识 trace-id 链路追踪:
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeContext>false</includeContext>
<includeCallerData>true</includeCallerData>
<customFields>{"module":"${spring.application.name}","hostname":"${HOSTNAME}"}</customFields>
<!-- add TID(traceId) field -->
<provider class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.logstash.TraceIdJsonProvider"/>
</encoder>
异步打印日志:
参考:https://logback.qos.ch/manual/appenders.html#AsyncAppender
<!-- 日志异步输出 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="LOGSTASH"/>
</appender>
现有业务日志现状
- 除mch-application、dashboard-backend模块,其余模块日志输出均依赖common模块配置
idgen、webhook 、不依赖common模块,独立配置日志
日志打印规范:
日志级别:
logback有5种级别,分别是TRACE < DEBUG < INFO < WARN < ERROR,定义于ch.qos.logback.classic.Level类中。
- 日志级别:
- trace(跟踪):输出最细致的运行轨迹,可能包含涉及的数据内容。
- debug(调试):用于线下调试的日志信息,用于分析应用执行逻辑,root logger默认级别是DEBUG。生产环境禁止输出debug日志。
- info(信息):应用执行过程中的详细信息,一般通过该信息可以看到每个请求的主要执行过程,生产环境非必要关键日志信息应不应输出,防止磁盘占用过载。
- Warn:(警告):警告信息表示,程序进入了一个特殊的状态,在该状态下程序可以继续运行,但是不建议让程序进入该状态,因为该状态可能导致结果出现问题。通常情况下warn级别的异常都是可预见性业务异常。
- 如“用户请求参数校验错误”,避免用户投诉时,无所适从。
- 非必要情况下,不要在此场景打出error级别,避免频繁报警。
- error (错误):非预期中的错误,此种错误可能导致部分系统异常但不会影响核心业务和系统正常运行。
- 如RPC异常、http异常、数据缺失、系统逻辑出错,等未知异常 或 可预知但不应在业务逻辑中出现的错误。
应用中的日志应遵循分类可检索原则,有利于归类查找。
- 关键业务埋点日志,如:接收/响应外部http请求,rpc/http调用、mq消息发送/接收……日志均封装在comon模块,在请求拦截器或滤器中统一封装。该部分已经包含服务关键日志:
@timestamp
@version
message
logger_name
thread_name
level
level_value
request_id
root_mch_id
module
hostname
package com.pingxx.common.consts;
public final class LogConsts {
//request_id
public static final String REQUEST_ID = "request_id";
//租户视角 结构/银行/平台/代理商……
public static final String TENANT_VIEW_POINT = "tenant_view_point";
//租户ID
public static final String TENANT_ID = "tenant_id";
//机构id
public static final String CUP_ORG_ID = "cup_org_id";
//总行id
public static final String ROOT_BANK_ID = "root_bank_id";
//分行id
public static final String BANK_ID = "bank_id";
//平台id
public static final String ROOT_MCH_ID = "root_mch_id";
//代理商id
public static final String AGENT_MCH_ID = "agent_mch_id";
//关键业务节点标识,见LogTypeEnum
public static final String LOG_TYPE = "log_type";
//外部请求url
public static final String REQUEST_URI = "request_uri";
//GET/POST...记录外部请求类型
public static final String REQUEST_METHOD = "request_method";
//
public static final String REQUEST_QUERY_STRING = "request_query_string";
//请求体
public static final String REQUEST_BODY = "request_body";
//请求头
public static final String REQUEST_HEADERS = "request_headers";
//完整请求头
public static final String REQUEST_HEADERS_ALL = "request_headers_all";
//响应报文
public static final String RESPONSE_BODY = "response_body";
//响应头
public static final String RESPONSE_HEADERS = "response_headers";
//完整响应头
public static final String RESPONSE_HEADERS_ALL = "response_headers_all";
//请求时间
public static final String REQUEST_TIME = "request_time";
//响应状态码
public static final String STATUS_CODE = "status_code";
//响应错误码
public static final String ERROR_CODE = "error_code";
//异常抛出类
public static final String EXCEPTION_CLASS = "exception_class";
//耗费时间
public static final String COST_MS = "cost_ms";
//mq消费端标识
public static final String IS_CONSUMER_SIDE = "is_consumer_side";
//调用链ID
public static final String CALL_ID_CHAIN = "call_id_chain";
//开始时间
public static final String START_TIME = "start_time";
//成功标识
public static final String IS_SUCCEEDED = "is_succeeded";
//dubbo调用interface
public static final String INTERFACE = "interface";
//dubbo调用方法名
public static final String METHOD_NAME = "method_name";
//dubbo调用返回结果
public static final String RESULT = "result";
//dubbo/kafka调用参数
public static final String ARGS = "args";
//请求上下文信息
public static final String ATTACHMENT_REQUEST_INFO = "attachment_request_info";
//灰度
public static final String ATTACHMENT_DUBBO_TAG = "attachment_dubbo_tag";
//银行名
public static final String BANK_NAME = "bank_name";
/**
* sky walking 的traceId
*/
public static final String TRACE_ID = "trace_id";
}
有除了关键节点日志之外,其它自定义业务日志,通过 org.slf4j.Marker 来传递,示例:
log.info(append("log_type", "dubbo_method_invoked"), "some message");
日志记录应包含业务节点标识: ```java
public enum LogTypeEnum implements IFieldEnum
- **对trace/debug/info级别的日志输出,强制使用占位符的方式,杜绝+链接参数打印。<br />**说明:logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
如果日志级别是warn,上述日志不会打印,但是会执行字符串拼接操作,如果symbol是对象,会执行toString()方法,浪费了系统资源,执行了上述操作,最终日志却没有打印。<br />反例:
log.debug(“Processing trade with id: “ + id + “ and symbol: “ + symbol);
正例:
log.debug(“Processing trade with id: {} and symbol : {} “, id, symbol); ```
- 异常信息日志应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字throws往上抛出。