系统日志设计与实现

logback和slf4j的关系


刚开始接触日志,公司一般都采用springboot中去整合slf4j和logback来去开发。提及它们的关系,发现我们更多是在代码层面上操作slf4j,对于logback我们一般都是去配置。logback是日志内部的实现,而slf4j对外提供操作的接口。

什么场景下使用日志

  • 系统日志:打印当前传入的参数和返回的结果
  • 操作日志:当前业务操作流水

logback介绍


logback一共三个模块:logback-core、logback-classic 和 logback-access。
logback-core 模块为其他两个模块奠定了基础。
logback-classic 原生实现了SLF4J API
logback-access 模块与 Tomcat 和 Jetty 等 Servlet 容器集成,以提供 HTTP 访问日志功能。

springboot默认引入logback日志


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

springboot启动的日志分析

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.3.RELEASE)

2022-04-19 13:59:18.086  INFO 4444 --- [           main] c.s.e.EurekaServerApplication            : No active profile set, falling back to default profiles: default
2022-04-19 13:59:18.792  WARN 4444 --- [           main] o.s.boot.actuate.endpoint.EndpointId     : Endpoint ID 'service-registry' contains invalid characters, please migrate to a valid format.
2022-04-19 13:59:18.903  INFO 4444 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=7006a679-7862-3a5a-adca-267c2acdc775

2022-04-19 13:59:18.086 日期时间
INFO 日志等级
4444 进程id
main 线程名称
c.s.e.EurekaServerApplication 日志所在类名称
No active profile set, falling back to default profiles: default 日志内容

引入自定义的logback配置文件

logging:
  config: classpath:logback.xml

logback自定义Layout


<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <appender name="outConsole" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>
                %30(%green(%date{yyyy-MM-dd HH:mm:ss.SSS})  %highlight(%-5level) [%thread]) %cyan(%logger{32}.%method) - %msg%n
            </pattern>
        </layout>
    </appender>
    <root level="INFO">
        <appender-ref ref="outConsole"/>
    </root>
</configuration>

logback输出到文件


<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <appender name="outInfoFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>DENY</onMatch>
            <onMismatch>ACCEPT</onMismatch>
        </filter>
        <encoder>
            <pattern>
                %30(%green(%date{yyyy-MM-dd HH:mm:ss.SSS})  %highlight(%-5level) [%thread]) %cyan(%logger{32}.%method) - %msg%n
            </pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>log/eureka-provider.%d.log</fileNamePattern>
        </rollingPolicy>
    </appender>
    <root level="INFO">
        <appender-ref ref="outInfoFile"/>
    </root>
</configuration>

rollingPolicy:滚动记录,就是按照指定时间分割记录

logback配置滚动记录设置


<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <appender name="outErrorFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <encoder>
            <pattern>
                %30(%green(%date{yyyy-MM-dd HH:mm:ss.SSS})  %highlight(%-5level) [%thread]) %cyan(%logger{32}.%method) - %msg%n
            </pattern>
        </encoder>

        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>log/error.%d.log</fileNamePattern>
            <!--控制保留的归档文件的最大数量,超出数量就删除旧文件,假设设置每个月滚动,
            且maxHistory是1,则只保存最近1个月的文件,删除之前的旧文件-->
            <MaxHistory>1</MaxHistory>
        </rollingPolicy>
    </appender>
    <root level="INFO">
        <appender-ref ref="outErrorFile"/>
    </root>
</configuration>

logback配置生产环境


<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 日志文件名 -->
    <property name="LOG_FILE" value="sys-log" />
    <!-- 日志文件路径 -->
    <property name="LOG_PATH" value="D://log//dev" />
    <!-- 控制台日志输出格式 -->
    <property name="LOG_PATTERN_CONSOLE" value="%30(%boldGreen(%d{yyyy-MM-dd HH:mm:ss.SSS})  %highlight(%-5level) %magenta([%thread])) %boldCyan(%logger{32}.%method) - %yellow(%msg%n)" />
    <!-- 文件日志输出格式,不要配置颜色样式,会造成乱码 -->
    <property name="LOG_PATTERN_FILE" value="%30(%d{yyyy-MM-dd HH:mm:ss.SSS}  %-5level [%thread]) %logger{32}.%method - %msg%n" />
    <!--- 设置控制台日志 -->
    <appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${LOG_PATTERN_CONSOLE}</pattern>
        </encoder>
    </appender>
    <!-- 设置日志文件 -->
    <appender name="fileLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${LOG_PATTERN_FILE}</pattern>
        </encoder>
        <!-- 总文件日志 -->
        <file>${LOG_PATH}/${LOG_FILE}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天的文件日志 -->
            <fileNamePattern>${LOG_PATH}/${LOG_FILE}-%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 日志文件保存30天,超过30天的自动删除 -->
            <maxHistory>30</maxHistory>
            <!--启动项目后清理历史日志-->
            <cleanHistoryOnStart>true</cleanHistoryOnStart> 
            <!-- 日志总大小 -->
            <totalSizeCap>20GB</totalSizeCap>
            <!-- maxFileSize:这是活动文件的大小,默认值是10MB -->    
            <maxFileSize>20MB</maxFileSize>
        </rollingPolicy>
    </appender>

    <!-- 设置日志级别,及需要记录日志的类 -->
    <root level="INFO">
        <appender-ref ref="consoleLog" />
        <appender-ref ref="fileLog" />
    </root>
</configuration>

配置多源日志


思路:利用springboot提供的多种环境配置的切换来实现对不同日志的配置。

## application.yml
spring:
  application:
    name: authority-service
  profiles:
    active: dev

## application-dev.yml
logging:
  config: classpath:logback-dev.xml

实现操作日志


自定义注解:

/**
 * 日志操作注解
 */
@Documented
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogOperation {
    /**
     *  操作所属的模块
     * @return
     */
    public String module() default "";

    /**
     * 操作
     * @return
     */
    public String operation() default "";

}

注解处理:

@Slf4j
@Component
@Aspect
public class LogAspect {

    @Autowired
    private MachineBO machine;

    @Autowired
    private LogMapper logMapper;

    @Pointcut("@annotation(com.sunpy.permissionservice.log.LogOperation)")
    public void logPoint() {

    }

    @AfterReturning(pointcut = "logPoint()", returning = "result")
    public void doLog(JoinPoint joinPoint, Object result) {
        // 从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();
        // 获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();

        LogOperation logOperation = method.getAnnotation(LogOperation.class);

        Log log = new Log();
        log.setLogModule(logOperation.module());
        log.setLogOperation(logOperation.operation());
        log.setLogClazz(className);
        log.setLogMethod(method.getName());
        log.setLogMethodResult(method.getReturnType().getName());
        StringBuilder sb = new StringBuilder();

        for (Parameter parameter : method.getParameters()) {
            sb.append(parameter.getParameterizedType().getTypeName());
            sb.append(",");
        }

        log.setLogMethodParam(sb.toString());
        insertLog(log);
    }

    private void insertLog(Log log) {
        long logId = new SnowFlakeIdUtil(machine.getWorkerId(), machine.getDatacenterId()).genNextId();
        log.setLogId(logId);
        log.setCreateTime(TimeUtil.getLocalDateTime());
        logMapper.insert(log);
    }
}

分布式日志系统


  • 上面的方式部署到单台机器:
    单台机器的日志如果发生暴增,那么我们需要将大日志文件切割。
  • 上面的方式部署到多台机器:
    因为负载均衡,会将请求分发到不同的机器上,所以每台机器都会存有一个日志,这样如果我们排查问题时,需要先找到处理请求的机器,然后查看日志,这样排查日志就麻烦一些。
  • 实现方式1:Elasticsearch + Logstash + filebeat + redis架构
  • 实现方式2:Elasticsearch + Logstash + filebeat + kafka + Kibana架构

参考官网


logback官网
springboot的logging官网

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,941评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,397评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,345评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,851评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,868评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,688评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,414评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,319评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,775评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,945评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,096评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,789评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,437评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,993评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,107评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,308评论 3 372
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,037评论 2 355

推荐阅读更多精彩内容