系统日志设计与实现

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日志



    org.springframework.boot
    spring-boot-starter-web

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




    
        
            
                %30(%green(%date{yyyy-MM-dd HH:mm:ss.SSS})  %highlight(%-5level) [%thread]) %cyan(%logger{32}.%method) - %msg%n
            
        
    
    
        
    

logback输出到文件




    
        
            ERROR
            DENY
            ACCEPT
        
        
            
                %30(%green(%date{yyyy-MM-dd HH:mm:ss.SSS})  %highlight(%-5level) [%thread]) %cyan(%logger{32}.%method) - %msg%n
            
        
        
            log/eureka-provider.%d.log
        
    
    
        
    

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

logback配置滚动记录设置




    
        
            ERROR
        
        
            
                %30(%green(%date{yyyy-MM-dd HH:mm:ss.SSS})  %highlight(%-5level) [%thread]) %cyan(%logger{32}.%method) - %msg%n
            
        

        
            log/error.%d.log
            
            1
        
    
    
        
    

logback配置生产环境




    
    
    
    
    
    
    
    
    
    
        
            ${LOG_PATTERN_CONSOLE}
        
    
    
    
        
            ${LOG_PATTERN_FILE}
        
        
        ${LOG_PATH}/${LOG_FILE}.log
        
            
            ${LOG_PATH}/${LOG_FILE}-%d{yyyy-MM-dd}.log
            
            30
            
            true 
            
            20GB
                
            20MB
        
    

    
    
        
        
    

配置多源日志


思路:利用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官网

你可能感兴趣的:(系统日志设计与实现)