十六、微服务进阶之Core模块(日志同步)

十六、微服务进阶之Core模块(日志同步)

功能介绍

日志是任何一个架构必备的功能, 目前 spring 主流的日志组件有 log4j2、slf4j、logback 等,当前版本的 springboot 默认使用 logback;
—— logback 和 log4j2 属于同一作者的产品,logback 性能上优于 log4j,完全兼容 log4j 的 api;logback 官网
—— slf4j 提供了优秀的日志接口,可以桥接各种其他日志框架,调用统一的日志 api 实现不同的日志输出

核心依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

注:当前依赖属于 spring-boot-starter 的一个模块,所以引入 spring-boot-starter 即可

需求说明

系统日志需要同时打印在控制台和文件中,文件日志分为普通日志和错误日志;开发人员手动打印日志需要同步至 elasticsearch;

需求目的

可以让开发人员更快速地定位 BUG、方便测试人员或者生产环境时查看日志信息;

需求实现

通过配置日志输出规则文件即可实现日志信息输出至文件,封装自己的日志工具实现日志异步输出至 rabbit,然后消费 rabbit 的消息将日志 同步至 elasticsearch;

配置文件
# 指定日志文件保存路径和名称 
logging.file.home=/usr/local/fatcat/logs logging.file.name=${logging.file.home}/${spring.application.name}/${spring.application.name}_info.log logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger- %msg%n 
# admin 读取日志的文件路径,和logback-spring.xml中路径保持一致 
management.endpoint.logfile.external-file=${logging.file.home}/${spring.application.name}/${spring.application.name}_info.log
XML配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 保留spring默认日志配置,默认配置中定义了日志的打印规则 spring-logback 日志路径 :org.springframework.boot.logging.logback CONSOLE_LOG_PATTERN:控制台日志打印规则 FILE_LOG_PATTERN:文件日志打印规则 -->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <!-- 根据配置文件引入自定义变量 配置文件加载顺序:logback.xml -> 本地配置 -> logback-spring.xml -> 远程配置 因为需要引入配置变量,所以当前文件命名为logback-spring,而非logback 初始化 logback-spring 时,还未获取到变量,需要设置默认值 -文件名默认为 fc-apps 如果想取消默认值,可以将变量配置在本地配置中 -->
    <springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="fc-apps"/>
    <springProperty scope="context" name="LOG_HOME" source="logging.file.home" defaultValue="/usr/local/fatcat/logs"/>
    <!--自定义常量方式:<property name="" value=""/> -->
    <!-- 控制台日志打印定义 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>
    <!-- 文件日志打印定义 -->
    <!-- 按照每天生成日志文件 -->
    <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 当天有效日志的文件名 -->
        <file>${LOG_HOME}/${APP_NAME}/${APP_NAME}_info.log</file>
        <!-- 滚动策略,旧日志文件的文件名 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 日志文件输出的文件名 -->
            <!-- %i 当文件大小到达阈值时,变为文件索引值 -->
            <FileNamePattern>${LOG_HOME}/${APP_NAME}/history/${APP_NAME}_info_%d{yyyy-MM-dd}.%i.log</FileNamePattern>
            <!-- 日志文件保留天数 -->
            <MaxHistory>30</MaxHistory>
            <!--日志文件最大的大小-->
            <MaxFileSize>10MB</MaxFileSize>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>utf-8</charset>
        </encoder>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 当天有效日志的文件名 -->
        <file>${LOG_HOME}/${APP_NAME}/${APP_NAME}_error.log</file>
        <!-- 滚动策略,旧日志文件的文件名 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 日志文件输出的文件名 -->
            <FileNamePattern>${LOG_HOME}/${APP_NAME}/history/${APP_NAME}_error_%d{yyyy-MM-dd}.%i.log</FileNamePattern>
            <!-- 日志文件保留天数 -->
            <MaxHistory>7</MaxHistory>
            <!--日志文件最大的大小-->
            <MaxFileSize>10MB</MaxFileSize>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>utf-8</charset>
        </encoder>
        <!-- 此日志文件只记录 ERROR 级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!-- SQL 打印 -->
    <!-- logger用来指定具体的包下的日志级别,additivity=false表示不向上级传递(传递可能会打印多次相同日志)-->
    <!-- <logger name="org.apache.ibatis" level="DEBUG" additivity="false"/>-->
    <!-- <logger name="java.sql.Connection" level="DEBUG" additivity="false"/>-->
    <!-- <logger name="java.sql.Statement" level="DEBUG" additivity="false"/>-->
    <!-- <logger name="java.sql.PreparedStatement" level="DEBUG" additivity="false"/>-->
    <!-- 全局配置,所有环境(develop 、test 、master)均生效-->
    <!-- 日志输出级别 低于当前级别的日志不输出 等同于配置 logging.root.level -->
    <!-- 级别等级 ALL < TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="INFO"/>
        <appender-ref ref="ERROR"/>
    </root>
    <!-- 自己工程的日志降低日志等级,方便 debug -->
    <logger name="com.fatcat" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="INFO"/>
        <appender-ref ref="ERROR"/>
    </logger>
    <!-- 单独为不同环境配置日志的打印方式 需要在项目中指定每个服务的运行环境名称,即属性 spring.profiles.active = develop | test | master -->
    <!-- <springProfile name="develop">-->
    <!-- <root level="INFO">-->
    <!-- <appender-ref ref="STDOUT"/>-->
    <!-- </root>-->
    <!-- </springProfile>-->
    <!-- <springProfile name="test">-->
    <!-- <root level="INFO">-->
    <!-- <appender-ref ref="STDOUT"/>-->
    <!-- <appender-ref ref="INFO"/>-->
    <!-- <appender-ref ref="ERROR"/>-->
    <!-- </root>-->
    <!-- </springProfile>-->
    <!-- <springProfile name="master">-->
    <!-- <root level="INFO">-->
    <!-- <appender-ref ref="INFO"/>-->
    <!-- <appender-ref ref="ERROR"/>-->
    <!-- </root>-->
    <!-- </springProfile>-->
</configuration>
结论

由于此同步操作在线上环境会堵塞日志的输出,严重影响接口的响应时间,所以建议采用现有的解决方案(例如:Logstash)。