描述:根据业务需要现需根据不同关键字将日志打印到不同的文件中,便于日志采集。
日志中需要体现出应用名称、IP等信息
- 自定义配置,获取节点ip
在logback-spring.xml中通过<conversionRule conversionWord="ip" converterClass="com.apigateway.config.IpLogConfig"/>
可以获取当前节点ip地址
package com.apigateway.config;
import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.apigateway.enums.LogLevelEnum;
import com.apigateway.util.LogUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @Author:xingpf
* @Date: 2020/4/22 9:18
* @Descriptions: logback 打印ip地址配置类
*/
public class IpLogConfig extends ClassicConverter {
private static final Logger log = LoggerFactory.getLogger(IpLogConfig.class);
private static String localIp = null;
@Override
public String convert(ILoggingEvent event) {
try {
if (StringUtil.isBlank(localIp)) {
localIp = InetAddress.getLocalHost().getHostAddress();
}
return localIp;
} catch (UnknownHostException e) {
log.error("获取ip地址异常,exception={}", e.toString());
LogUtil.logInfo("[IpLogConfig][convert] 获取ip地址异常,exception={}",
e.toString(), LogLevelEnum.EVENT.getLevel());
}
return null;
}
}
- application.yml配置
spring:
profiles:
active: ${RUN_ENV:dev}
application:
name: apigateway
http:
encoding:
charset: UTF-8
enabled: true
force: true
server:
port: 5106
servlet:
context-path: /apigateway
tomcat:
uri-encoding: UTF-8
sentinel:
degradeRule:
ex-count: ${ex-count:5}
time-window: ${time-window:10}
#0:关闭,1:打开
logToMqSwitch: ${mq-flag:1}
#日志保存路径
log:
#日志保存路径
path: /logs/apigateway
#日志字符集
charset: UTF-8
#最大历史天数
max-history: 30
#单个日志文件最大体积
max-file-size: 10MB
#全部日志文件最大体积
total-size-cap: 2GB
#日志格式
pattern: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n'
- logback-spring.xml配置,
- 该配置文件中可以用过
<springProperty scope="context" name="logPath" source="log.path"/>
该标签读取application.yml配置文件中的配置信息变量; - 通过自定义appender及logger可以在代码中通过定义 的logger获取自定义的logger对象,将某些需要收集的日志信息单独打印到指定文件中
<!--自定义appender打印采集日志-->
<appender name="COLLECT_LOG_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--打印日志用于日志采集-->
<logger name="COLLECT_LOG_INFO_FILE">
<appender-ref ref="COLLECT_LOG_INFO"/>
</logger>
/**
* 获取采集日志专用logger对象
*/
private static final Logger logger = LoggerFactory.getLogger("COLLECT_LOG_INFO_FILE");
完整logback-spring.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<contextName>apigateway</contextName>
<springProperty scope="context" name="logPath"
source="log.path"/>
<springProperty scope="context" name="logCharset"
source="log.charset"/>
<springProperty scope="context" name="logMaxHistory"
source="log.max-history"/>
<springProperty scope="context" name="logMaxFileSize"
source="log.max-file-size"/>
<springProperty scope="context" name="logTotalSizeCap"
source="log.total-size-cap"/>
<springProperty scope="context" name="logPattern"
source="log.pattern"/>
<!--配置规则类位置,获取节点ip-->
<conversionRule conversionWord="ip" converterClass="com.apigateway.config.IpLogConfig"/>
<conversionRule conversionWord="apigateway" converterClass="ch.qos.logback.classic.pattern.LoggerConverter"/>
<property name="collectLogPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS}#|###|#%ip#|###|#%contextName#|###|#%msg%n"/>
<!--打印采集日志-->
<appender name="COLLECT_LOG_INFO"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 活动日志文件路径-->
<file>${logPath}/log_collect.log</file>
<encoder>
<pattern>${collectLogPattern}</pattern>
<charset>${logCharset}</charset>
</encoder>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 归档日志文件路径-->
<fileNamePattern>${logPath}/collect/log-collect-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- 单个日志文件最大大小-->
<maxFileSize>${logMaxFileSize}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 最大保留天数-->
<maxHistory>${logMaxHistory}</maxHistory>
<!-- 所有日志文件最大容量-->
<totalSizeCap>${logTotalSizeCap}</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="CONSOLE"
class="ch.qos.logback.core.ConsoleAppender">
<encoder
class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>${logPattern}</Pattern>
<charset>${logCharset}</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
</appender>
<appender name="DEBUG_FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/log_debug.log</file>
<encoder>
<pattern>${logPattern}</pattern>
<charset>${logCharset}</charset>
</encoder>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logPath}/debug/log-debug-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${logMaxFileSize}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>${logMaxHistory}</maxHistory>
<totalSizeCap>${logTotalSizeCap}</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="INFO_FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/log_info.log</file>
<encoder>
<pattern>${logPattern}</pattern>
<charset>${logCharset}</charset>
</encoder>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logPath}/info/log-info-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${logMaxFileSize}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>${logMaxHistory}</maxHistory>
<totalSizeCap>${logTotalSizeCap}</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="WARN_FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/log_warn.log</file>
<encoder>
<pattern>${logPattern}</pattern>
<charset>${logCharset}</charset>
</encoder>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logPath}/warn/log-warn-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${logMaxFileSize}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>${logMaxHistory}</maxHistory>
<totalSizeCap>${logTotalSizeCap}</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="ERROR_FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/log_error.log</file>
<encoder>
<pattern>${logPattern}</pattern>
<charset>${logCharset}</charset>
</encoder>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logPath}/error/log-error-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${logMaxFileSize}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>${logMaxHistory}</maxHistory>
<totalSizeCap>${logTotalSizeCap}</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--打印日志用于日志采集-->
<logger name="COLLECT_LOG_INFO_FILE">
<appender-ref ref="COLLECT_LOG_INFO"/>
</logger>
<springProfile name="dev">
<root level="debug">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
<springProfile name="pro,test">
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
<logger name="org.springframework.scheduling">
<level value="info"/>
</logger>
</configuration>
- 日志配置文件中的配置信息一经打包将不再改变,如何实现根据不同关键字将对应的相关日志信息打到不同的日志文件中呢?关键字已知的情况下可以通过在logback-spring.xml中自定义appender实现,但如果关键字非常多或关键字未知时如何实现呢?这时候就需要通过代码实现动态创建appender了:
package com.apigateway.config;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.util.FileSize;
import ch.qos.logback.core.util.OptionHelper;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Author:xingpf
* @Date: 2020/4/30 14:22
* @Descriptions:动态生成logback.xml配置文件的appender
*/
public class LogAppenderConfig {
/**
* 日志打印根目录
*/
private static final String LOG_BASE_PATH = "/logs/";
/**
* 本地静态ConcurrentHashMap存储已有的日志文件名,线程安全
*/
private static Map<String, Logger> logFileNameMap = new ConcurrentHashMap<>();
/**
* 根据logFileName获取logger对象
*
* @param logFileName
* @return
*/
public static Logger getLogger(String logFileName) {
Logger logger = logFileNameMap.get(logFileName);
if (logger != null) {
return logger;
}
logger = createLogger(logFileName);
logFileNameMap.put(logFileName, logger);
return logger;
}
/**
* 根据传入logFileName创建当前Logger对象输出日志的文件名
*
* @param logFileName 要创建的日志文件名称
* @return
*/
private static Logger createLogger(String logFileName) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
//创建logger对象
Logger logger = loggerContext.getLogger(logFileName);
logger.setAdditive(false);
//创建appender,滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件
RollingFileAppender appender = new RollingFileAppender();
appender.setContext(loggerContext);
// 设置appender的名称
appender.setName(logFileName);
// 创建活动日志(当天日志)打印文件
appender.setFile(OptionHelper.substVars(LOG_BASE_PATH + logFileName + "/collection.log", loggerContext));
// 设置日志是否追加到文件结尾,true:时是,false:否
appender.setAppend(true);
// 日志是否线程安全写入文件,true:是,false:否,默认false
appender.setPrudent(false);
// 定义滚动策略,按时间及大小进行滚动
SizeAndTimeBasedRollingPolicy policy = new SizeAndTimeBasedRollingPolicy();
// 定义归档文件路径及名称
String filePath = OptionHelper.substVars(LOG_BASE_PATH + logFileName + "/collection/.%d{yyyy-MM-dd}.%i.log", loggerContext);
policy.setParent(appender);
policy.setContext(loggerContext);
// 设置单个文件大小
policy.setMaxFileSize(FileSize.valueOf("50MB"));
// 设置归档文件名
policy.setFileNamePattern(filePath);
// 设置归档文件保留的最大数量,这里设置30天
policy.setMaxHistory(30);
// 设置全部日志文件最大体积
policy.setTotalSizeCap(FileSize.valueOf("2GB"));
policy.start();
//设置输出到日志文件的格式
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setContext(loggerContext);
// 日志格式
encoder.setPattern("%d#|###|#%m%n");
encoder.start();
//设置输出到控制台的日志文件格式
PatternLayoutEncoder encoder1 = new PatternLayoutEncoder();
encoder1.setContext(loggerContext);
encoder1.setPattern("%d %p (%file:%line\\)- %m%n");
encoder1.start();
/*设置动态日志控制台输出*/
ConsoleAppender consoleAppender = new ConsoleAppender();
consoleAppender.setContext(loggerContext);
consoleAppender.setEncoder(encoder1);
consoleAppender.start();
logger.addAppender(consoleAppender);
// 设置appender记录日志的滚动策略
appender.setRollingPolicy(policy);
appender.setEncoder(encoder);
appender.start();
logger.addAppender(appender);
return logger;
}
}
在其它类中可以直接调用工具类LogAppenderConfig中的getLogger方法获取到对应的logger对象
// 定义
private static Logger logger = null;
// 方法中调用,入参test即为关键字,logger对象会将日志打印到/logs/test/文件夹下
logger = LogAppenderConfig.getLogger("test");
如图所示:
ps:至此,此次项目中自定义输出日志记录完毕,以备后用。水平有限,文章中如有错误或不当之处,欢迎批评指正,谢谢!