目录
3自定义日志格式和filter,并打印到指定文件appender
1 appender
指定输出格式,输出位置
1)指定文件目录,文件名
2)指定滚动策略,每天归档的格式:
一天一个文件,文件命名格式
一天多个文件,当单个文件达到指定大小时,就自动将新的日志生成到下一个文件;
通过后缀指定log,zip
3)指定文件的保留天数
4)指定filter过滤器,文件过滤,可以是以下的level类型的,不满足条件的将会被过滤掉。可以是阈值类型的,只有指定阈值以上的才会输出
过滤器种类:
1 级别过滤器,阈值过滤器
2 自定义过滤,自己指定条件accept or deny
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${LOG_HOME}/${LOG_FILENAME}-info.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${LOG_HOME}/${LOG_FILENAME}-info-%d{yyyy-MM-dd}.%i.zip</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1000MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
3自定义日志格式和filter,并打印到指定文件appender
特点:日志散落在各处,不能通过类名来定义单独的logger
方法:使用remarker
1 自定义过滤器
通过在写日志时,指定remark标记的方法
public class RetryLogFilter extends Filter<ILoggingEvent> {
@Override
public FilterReply decide(ILoggingEvent event) {
if(!ScsConstant.RETRY_MARKER.equals(event.getMarker().getName())){
return FilterReply.DENY;
}else {
return FilterReply.NEUTRAL;
}
}
}
2 自定义日志appender
此处我存储的是json串,一行就是一个json
<appender name="RETRY_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/retry.log</file>
<encoder>
<pattern>%msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy" >
<fileNamePattern>${LOG_HOME}/retry-%d{yyyy-MM-dd}.log</fileNamePattern>
<!--日志文档保留天数-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<filter class="com.atzy.mcrc.scs.job.notify.config.RetryLogFilter"/>
</appender>
3 写日志
使用@Slf4j注解,写日志时,指定remarker,方便追踪落地appender
log.error(MarkerFactory.getMarker(ScsConstant.RETRY_MARKER),new Gson().toJson(student));
2 关于mybatis sql的打印
mybatis,sql语句是否输出,这里涉及到2部分:
1)mybatis工具类本身打印的日志,可以自己决定输出级别
2)sql本身的打印,是通过日志实现类代理实现的。自己指定打印级别
<!---打印sql语句-->
<logger name="com.atzy.mcrc.tool.canal.mapper" level="DEBUG"></logger>
<!--屏蔽掉mybatis自己的debug日志-->
<logger name="org.mybatis" level="WARN"></logger>
<!--
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
不能设置为INHERITED或者同义词NULL。默认是DEBUG
可以包含零个或多个元素,标识这个appender将会添加到这个logger。
-->
<!-- 4. 最终的策略 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<!-- <appender-ref ref="DEBUG_FILE" />-->
<appender-ref ref="ERROR_FILE" />
<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、
以及指定<appender>。<logger>仅有一个name属性,
一个可选的level和一个可选的addtivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
如果未设置此属性,那么当前logger将会继承上级的级别。
addtivity:是否向上级logger传递打印信息。默认是true。
<logger name="org.springframework.web" level="info"/>
<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>
-->
<!--
使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
【logging.level.org.mybatis=debug logging.level.mybatis=debug】
-->
<!-- mybatis日志打印 -->
<!-- <logger name="com.ibatis" level="DEBUG" /> -->
<!-- <logger name="com.ibatis.common.jdbc.SimpleDataSource" level="DEBUG" /> -->
<!-- <logger name="com.ibatis.common.jdbc.ScriptRunner" level="DEBUG" /> -->
<!-- <logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate" level="DEBUG" /> -->
<!-- <logger name="java.sql.Connection" level="DEBUG" /> -->
<!-- <logger name="java.sql.Statement" level="DEBUG" />
<logger name="java.sql.PreparedStatement" level="DEBUG" />-->
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 --> <!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true --> <!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。 当scan为true时,此属性生效。默认的时间间隔为1分钟。 --> <!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> <!-- configuration : scan="true" scanPeriod="10 seconds"-->
4 日志级别,通过apollo动态修改
1 增加apollo-client依赖
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>${apollo-client.version}</version>
</dependency>
2 增加apollo配置,在apollo配置中心
logging.level.root = warn
3 在项目中,增加apollo配置更新的监控
此部分配置可见apollo的代码示例
@Service
public class LoggerConfiguration {
private static final Logger logger = LoggerFactory.getLogger(LoggerConfiguration.class);
private static final String LOGGER_TAG = "logging.level.";
@Autowired
private LoggingSystem loggingSystem;
@ApolloConfig
private Config config;
@ApolloConfigChangeListener
private void onChange(ConfigChangeEvent changeEvent) {
List<String> logKeys = changeEvent.changedKeys().stream().filter(key -> key.startsWith(LOGGER_TAG)).collect(Collectors.toList());
for (String key : logKeys) {
String strLevel = config.getProperty(key, "info");
LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());
loggingSystem.setLogLevel(key.replace(LOGGER_TAG, ""), level);
logger.info("log-change:{}:{}", key, strLevel);
}
}
@PostConstruct
private void refreshLoggingLevels() {
Set<String> keyNames = config.getPropertyNames();
for (String key : keyNames) {
if (containsIgnoreCase(key, LOGGER_TAG)) {
String strLevel = config.getProperty(key, "info");
LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());
loggingSystem.setLogLevel(key.replace(LOGGER_TAG, ""), level);
logger.info("log-change:{}:{}", key, strLevel);
}
}
}
private static boolean containsIgnoreCase(String str, String searchStr) {
if (str == null || searchStr == null) {
return false;
}
int len = searchStr.length();
int max = str.length() - len;
for (int i = 0; i <= max; i++) {
if (str.regionMatches(true, i, searchStr, 0, len)) {
return true;
}
}
return false;
}
}