這里我們需要一個(gè)能指定輸出日志信息到某個(gè)文件的日志工具類,用以區(qū)分不同類型的log日志盐捷。
SpringBoot集成了slf4j日志門面蹬挤,默認(rèn)用logback實(shí)現(xiàn)日志底層功能贯底。但是基于SpringBoot的日志配置伴奥,并不支持我們的需求写烤,這里就需要我們手動封裝一個(gè)。
日志實(shí)現(xiàn)
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.filter.LevelFilter;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.spi.FilterReply;
import ch.qos.logback.core.util.FileSize;
import ch.qos.logback.core.util.OptionHelper;
import com.qiejk.commons.util.base.SpringContextTool;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ConcurrentHashMap;
public class LoggerHolder {
private static ConcurrentHashMap<String, Logger> map = new ConcurrentHashMap<>();
public static Logger getLogger(String name) {
Logger logger = map.get(name);
if (logger != null) {
return logger;
}
synchronized (LoggerHolder.class) {
name = name.toLowerCase();
logger = map.get(name);
if (logger != null) {
return logger;
}
logger = build(name);
map.put(name, logger);
}
return logger;
}
private static Logger build(String name) {
RollingFileAppender appender = new LoggerAppenderBuilder().createRollingFileAppender(name, Level.INFO);
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
Logger logger = context.getLogger(name);
//設(shè)置不向上級打印信息 不繼承祖先appender
logger.setAdditive(false);
logger.addAppender(appender);
return logger;
}
// 日志輸出
static class LoggerAppenderBuilder {
public RollingFileAppender createRollingFileAppender(String name, Level level) {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
String logHome = SpringContextTool.getBean(ConfigConfiguration.class).getLogsHome();
RollingFileAppender appender = new RollingFileAppender();
//這里設(shè)置級別過濾器
appender.addFilter(createLevelFilter(level));
//設(shè)置上下文渔伯,每個(gè)logger都關(guān)聯(lián)到logger上下文顶霞,默認(rèn)上下文名稱為default。
// 但可以使用<contextName>設(shè)置成其他名字锣吼,用于區(qū)分不同應(yīng)用程序的記錄。一旦設(shè)置蓝厌,不能修改玄叠。
appender.setContext(context);
//appender的name屬性
appender.setName(name);
//設(shè)置文件名
String fileName = logHome + name + "/" + name;
appender.setFile(OptionHelper.substVars(fileName + ".log", context));
// 追加
appender.setAppend(true);
// 并發(fā)安全
appender.setPrudent(false);
// 滾動策略
appender.setRollingPolicy(createSizeAndTimeBasedRollingPolicy(fileName, level, context, appender));
// 編碼器
appender.setEncoder(createEncoder(context));
// 啟動
appender.start();
return appender;
}
// 文件滾動策略
private SizeAndTimeBasedRollingPolicy createSizeAndTimeBasedRollingPolicy(String fileName, Level level, LoggerContext context, FileAppender appender) {
//設(shè)置文件創(chuàng)建時(shí)間及大小的類
SizeAndTimeBasedRollingPolicy policy = new SizeAndTimeBasedRollingPolicy();
//文件名格式
String fp = OptionHelper.substVars(fileName + "-%d{yyyy-MM-dd}.log.%i", context);
//最大日志文件大小
policy.setMaxFileSize(FileSize.valueOf("10MB"));
//設(shè)置文件名模式
policy.setFileNamePattern(fp);
//設(shè)置最大歷史記錄為50條
policy.setMaxHistory(50);
//總大小限制
policy.setTotalSizeCap(FileSize.valueOf("32GB"));
//設(shè)置父節(jié)點(diǎn)是appender
policy.setParent(appender);
//設(shè)置上下文,每個(gè)logger都關(guān)聯(lián)到logger上下文拓提,默認(rèn)上下文名稱為default读恃。
// 但可以使用<contextName>設(shè)置成其他名字,用于區(qū)分不同應(yīng)用程序的記錄代态。一旦設(shè)置寺惫,不能修改。
policy.setContext(context);
policy.start();
return policy;
}
// 編碼器
private PatternLayoutEncoder createEncoder(LoggerContext context) {
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
//設(shè)置上下文蹦疑,每個(gè)logger都關(guān)聯(lián)到logger上下文西雀,默認(rèn)上下文名稱為default。
// 但可以使用<contextName>設(shè)置成其他名字歉摧,用于區(qū)分不同應(yīng)用程序的記錄艇肴。一旦設(shè)置腔呜,不能修改。
encoder.setContext(context);
//設(shè)置格式
encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %msg%n");
encoder.start();
return encoder;
}
// 級別過濾器
private LevelFilter createLevelFilter(Level level) {
LevelFilter levelFilter = new LevelFilter();
levelFilter.setLevel(level);
levelFilter.setOnMatch(FilterReply.ACCEPT);
levelFilter.setOnMismatch(FilterReply.DENY);
levelFilter.start();
return levelFilter;
}
}
}
工具類
import com.qiejk.commons.util.base.u.LoggerHolder;
public class LogTool {
private static String DEFAULT_CATEGORY = "debug";
private static String ERROR_CATEGORY = "error";
public static void log(String log) {
LoggerHolder.getLogger(DEFAULT_CATEGORY).info(log);
}
public static void log(String error, Throwable t) {
LoggerHolder.getLogger(DEFAULT_CATEGORY).error(error, t);
}
public static void log(String category, String log) {
LoggerHolder.getLogger(category).info(log);
}
public static void log(String category, String error, Throwable t) {
LoggerHolder.getLogger(category).error(error, t);
}
public static void error(String msg, Throwable t) {
LoggerHolder.getLogger(ERROR_CATEGORY).error(msg, t);
}
}
這樣我們就能根據(jù)指定的category將日志信息輸出到不同的文件中去再悼,為以后做數(shù)據(jù)埋點(diǎn)核畴、日志收集做準(zhǔn)備。