日志设计分析
- 此项目总共分为四种日志:性能日志(performance.log),业务日志(business.log),错误输出日志(error.log),控制台输出日志
- 业务日志包括:基本的业务日志(logBusiness),用户操作日志(logUserOperation),业务错误日志(logBusinessError在配置文件里面有专门的配置到单独输出日志文件中),API调用日志(logAPICall),文件操作日志(logFileOperation),记录聊天日志(logChat)
- 性能监控修饰器分析
// 性能日志记录器,Slf4j依赖,getLogger获取在logback中的性能输出配置
//private为了控制访问权限,避免被外部类修改了日志配置,应该在xml文件中统一管理而不是能被外部类修改
//static共享变量节省空间,日志对象本身是无状态的只负责日志输出,不存储实际数据,可以避免多个logger对象创建浪费空间
//final保证不可改变,避免错误的重新对记录器赋值,比如赋值到了业务日志,输出错乱
private static final Logger PERFORMANCE_LOGGER = LoggerFactory.getLogger("com.yizhaoqi.smartpai.performance");
/**
* 记录性能日志
*/
public static void logPerformance(String operation, long duration, String details) {
try {
MDC.put(OPERATION, operation);
PERFORMANCE_LOGGER.info("[性能] [{}] 耗时:{}ms {}", operation, duration, details);
} finally {
MDC.clear();
}
}
/**
* 性能监控装饰器
*/
public static class PerformanceMonitor {
private final String operation;
private final long startTime;
public PerformanceMonitor(String operation) {
this.operation = operation;
this.startTime = System.currentTimeMillis();
}
public void end() {
end("");
}
public void end(String details) {
long duration = System.currentTimeMillis() - startTime;
logPerformance(operation, duration, details);
}
}
/**
* 创建性能监控器,这是一个静态方法工厂,不直接使用该类的构造方法,第一可读性增强,直接new对象不知道作用
* 封装对象创建的细节,内部修改不影响外部代码,灵活性扩展性更好,实现开放封闭原则,如果修改对象创建的规则,那么所有new的对象都要进行修改
* 这个类里面的构造方法目前是public,如果我们对象创建有设置的逻辑不希望被绕开通过构造方法创建,就可以换成private编程单例模式
*/
public static PerformanceMonitor startPerformanceMonitor(String operation) {
return new PerformanceMonitor(operation);
}
logback-spring.xml中的配置
日志级别
- trace->debug->info->warn->error->fatal最高致命错误
控制台日志输出
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> //这里class绑定为控制台输出
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> //这个encoder是编码器,根据pattern格式编码成对应格式的日志
<!-- 格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
按照每天生成日志
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
//切割之后的日志名字要按照时间归档
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件输出的文件名 -->
<FileNamePattern>${LOG_HOME}/smartpai.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- 日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志文件最大的大小 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>100MB</MaxFileSize>
</triggeringPolicy>
</appender>
错误日志单独输出
<!-- 错误日志单独输出 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
//设置过滤器,只有ERROR级别能接受,否则拒绝,精确匹配
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
业务逻辑输出
//同样是按照天数进行分割输出到business开头文件中,保存30天
<appender name="BUSINESS_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/business.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
性能日志输出
<!-- 性能日志单独输出 -->
<appender name="PERFORMANCE_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/performance.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>7</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
精细化级别控制
<!-- 项目包日志级别配置,全局控制级别,additivity表示关闭日志向上传递,避免根目录重复输出 -->
<logger name="com.yizhaoqi.smartpai" level="INFO" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
<appender-ref ref="ERROR_FILE"/>
<appender-ref ref="BUSINESS_FILE"/>
</logger>
<!-- 业务日志记录器 两个业务输出日志-->
<logger name="com.yizhaoqi.smartpai.business" level="INFO" additivity="false">
<appender-ref ref="BUSINESS_FILE"/>
<appender-ref ref="CONSOLE"/>
</logger>
<!-- 性能日志记录器 针对性能日志配置-->
<logger name="com.yizhaoqi.smartpai.performance" level="INFO" additivity="false">
<appender-ref ref="PERFORMANCE_FILE"/>
</logger>
//对于第三方的日志进行级别设置,避免对业务日志淹没
<!-- Spring框架日志级别 -->
<logger name="org.springframework" level="WARN"/>
<logger name="org.springframework.web" level="INFO"/>
<logger name="org.springframework.security" level="INFO"/>
<logger name="org.springframework.boot" level="INFO"/>
<!-- 数据库相关日志 -->
<logger name="org.hibernate" level="WARN"/>
<logger name="org.hibernate.SQL" level="INFO"/>
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>
<!-- 第三方库日志级别 -->
<logger name="io.minio" level="WARN"/>
<logger name="org.apache.kafka" level="WARN"/>
<logger name="org.elasticsearch" level="WARN"/>
<logger name="co.elastic" level="WARN"/>
<logger name="org.apache.tika" level="WARN"/>
<logger name="io.netty" level="WARN"/>
<logger name="reactor.netty" level="WARN"/>
//配置开发环境选择
<!-- 开发环境配置 -->
<springProfile name="dev">
<logger name="com.yizhaoqi.smartpai" level="DEBUG"/>
<logger name="org.springframework.web" level="DEBUG"/>
<logger name="org.springframework.security" level="DEBUG"/>
</springProfile>
<!-- 生产环境配置 -->
<springProfile name="prod">
<logger name="com.yizhaoqi.smartpai" level="INFO"/>
<logger name="org.springframework" level="WARN"/>
<logger name="root" level="WARN"/>
</springProfile>
//最后根目录设置级别兜底
<!-- 根日志级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>