javascript
qt能使用logback_SpringBoot 中使用 LogBack 配置
LogBack是一個日志框架,它與Log4j可以說是同出一源,都出自?CekiGülcü之手。(?log4j的原型是早前由?CekiGülcü貢獻給?Apache基金會的)下載地址?https://logback.qos.ch/download.html
LogBack、Slf4j和Log4j之間的關系
Slf4j是?TheSimpleLoggingFacadeforJava的簡稱,是一個簡單日志門面抽象框架,它本身只提供了日志Facade?API和一個簡單的日志類實現,一般常配合?Log4j,LogBack,java.util.logging使用。?Slf4j作為應用層的Log接入時,程序可以根據實際應用場景動態調整底層的日志實現框架?(Log4j/LogBack/JdkLog…)。
LogBack和?Log4j都是開源日記工具庫,?LogBack是?Log4j的改良版本,比?Log4j擁有更多的特性,同時也帶來很大性能提升。詳細數據可參照下面地址:?Reasonsto prefer logback over log4j。
LogBack官方建議配合?Slf4j使用,這樣可以靈活地替換底層日志框架。
TIPS:為了優化?log4j,以及更大性能的提升,Apache基金會已經著手開發了?log4j2.0, 其中也借鑒和吸收了?logback的一些先進特性,目前?log4j2還處于?beta階段
logback取代log4j的理由
1、更快的實現:?Logback的內核重寫了,在一些關鍵執行路徑上性能提升10倍以上。而且?logback不僅性能提升了,初始化內存加載也更小了。
2、非常充分的測試:?Logback經過了幾年,數不清小時的測試。?Logback的測試完全不同級別的。
3、?Logback-classic非常自然實現了?SLF4j:Logback-classic實現了?SLF4j。在使用SLF4j中,你都感覺不到?logback-classic。而且因為?logback-classic非常自然地實現了?slf4j?, 所 以切換到?log4j或者其他,非常容易,只需要提供成另一個?jar包就OK,根本不需要去動那些通過?SLF4JAPI實現的代碼。
4、非常充分的文檔 官方網站有兩百多頁的文檔。
5、自動重新加載配置文件,當配置文件修改了,?Logback-classic能自動重新加載配置文件。掃描過程快且安全,它并不需要另外創建一個掃描線程。這個技術充分保證了應用程序能跑得很歡在JEE環境里面。
6、?Lilith是?log事件的觀察者,和?log4j的?chainsaw類似。而?lilith還能處理大數量的log數據 。
7、謹慎的模式和非常友好的恢復,在謹慎模式下,多個?FileAppender實例跑在多個JVM下,能 夠安全地寫道同一個日志文件。?RollingFileAppender會有些限制。?Logback的?FileAppender和它的子類包括?RollingFileAppender能夠非常友好地從I/O異常中恢復。
8、配置文件可以處理不同的情況,開發人員經常需要判斷不同的Logback配置文件在不同的環境下(開發,測試,生產)。而這些配置文件僅僅只有一些很小的不同,可以通過,和來實現,這樣一個配置文件就可以適應多個環境。
9、?Filters(過濾器)有些時候,需要診斷一個問題,需要打出日志。在?log4j,只有降低日志級別,不過這樣會打出大量的日志,會影響應用性能。在?Logback,你可以繼續 保持那個日志級別而除掉某種特殊情況,如?alice這個用戶登錄,她的日志將打在?DEBUG級別而其他用戶可以繼續打在?WARN級別。要實現這個功能只需加4行?XML配置。可以參考?MDCFIlter。
10、?SiftingAppender(一個非常多功能的?Appender):它可以用來分割日志文件根據任何一個給定的運行參數。如,?SiftingAppender能夠區別日志事件跟進用戶的?Session,然后每個用戶會有一個日志文件。
11、自動壓縮已經打出來的?log:RollingFileAppender在產生新文件的時候,會自動壓縮已經打出來的日志文件。壓縮是個異步過程,所以甚至對于大的日志文件,在壓縮過程中應用不會受任何影響。
12、堆棧樹帶有包版本:?Logback在打出堆棧樹日志時,會帶上包的數據。
13、自動去除舊的日志文件:通過設置?TimeBasedRollingPolicy或者?SizeAndTimeBasedFNATP的maxHistory屬性,你可以控制已經產生日志文件的最大數量。如果設置?maxHistory12,那那些?log文件超過?12個月的都會被自動移除。
LogBack的結構
LogBack被分為3個組件,?logback-core,logback-classic?和?logback-access。
logback-core提供了LogBack的核心功能,是另外兩個組件的基礎。
logback-classic則實現了?Slf4j的?API,所以當想配合Slf4j使用時,需要將?logback-classic加入?classpath。
logback-access是為了集成?Servlet環境而準備的,可提供?HTTP-access的日志接口。
配置詳解
Github 代碼
代碼已放到 Github ,導入?spring-boot-logback?項目
github?spring-boot-logback
Maven依賴
假如maven依賴中添加了?spring-boot-starter-logging:
? ?org.springframework.boot
? ?spring-boot-starter-logging
那么,我們的Spring Boot應用將自動使用logback作為應用日志框架,Spring Boot啟動的時候,由org.springframework.boot.logging.Logging-Application-Listener根據情況初始化并使用。
但是呢,實際開發中我們不需要直接添加該依賴,你會發現spring-boot-starter其中包含了 spring-boot-starter-logging,該依賴內容就是 Spring Boot 默認的日志框架 logback
配置文件
配置文件 logback-spring.xml
xml version="1.0" encoding="UTF-8"?>
? ? scope="context" name="LOG_HOME" source="logging.path" defaultValue="/data/logs/spring-boot-logback"/>
? ? scope="context" name="LOG_ROOT_LEVEL" source="logging.level.root" defaultValue="DEBUG"/>
? ? scope="context" name="STDOUT" source="log.stdout" defaultValue="STDOUT"/>
? ? name="LOG_PREFIX" value="spring-boot-logback" />
? ? name="LOG_CHARSET" value="UTF-8" />
? ? name="LOG_DIR" value="${LOG_HOME}/%d{yyyyMMdd}" />
? ? name="LOG_MSG" value="- | [%X{requestUUID}] | [%d{yyyyMMdd HH:mm:ss.SSS}] | [%level] | [${HOSTNAME}] | [%thread] | [%logger{36}] | --> %msg|%n "/>
? ? name="MAX_FILE_SIZE" value="50MB" />
? ? name="MAX_HISTORY" value="10"/>
? ? name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
? ? ? ? class="ch.qos.logback.classic.PatternLayout">
? ? ? ? ? ?${LOG_MSG}
? ? name="0" class="ch.qos.logback.core.rolling.RollingFileAppender">
? ? name="FILE_ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
? ? ? ?${LOG_HOME}/all_${LOG_PREFIX}.log
? ? ? ? class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
? ? ? ? ? ?${LOG_DIR}/all_${LOG_PREFIX}%i.log
? ? ? ? ? ?${MAX_HISTORY}
? ? ? ? ? ? class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
? ? ? ? ? ? ? ?${MAX_FILE_SIZE}
? ? ? ? class="ch.qos.logback.classic.PatternLayout">
? ? ? ? ? ?${LOG_MSG}
? ? name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
? ? ? ? class="ch.qos.logback.classic.filter.LevelFilter">
? ? ? ? ? ?ERROR
? ? ? ? ? ?DENY
? ? ? ? ? ?ACCEPT
? ? ? ?${LOG_HOME}/err_${LOG_PREFIX}.log
? ? ? ? class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
? ? ? ? ? ?${LOG_DIR}/err_${LOG_PREFIX}%i.log
? ? ? ? ? ?${MAX_HISTORY}
? ? ? ? ? ? class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
? ? ? ? ? ? ? ?${MAX_FILE_SIZE}
? ? ? ? class="ch.qos.logback.classic.PatternLayout">
? ? ? ? ? ?${LOG_MSG}
? ? name="org.springframework" ? ? level="ERROR" />
? ? name="org.apache.commons" ? ? ?level="ERROR" />
? ? name="org.apache.zookeeper" ? ?level="ERROR" ?/>
? ? name="com.alibaba.dubbo.monitor" level="ERROR"/>
? ? name="com.alibaba.dubbo.remoting" level="ERROR" />
? ? level="${LOG_ROOT_LEVEL}">
? ? ? ? ref="${STDOUT}"/>
? ? ? ? ref="FILE_ALL"/>
? ? ? ? ref="FILE_ERROR"/>
配置文件 application.properties
#日志級別從低到高分為TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果設置為WARN,則低于WARN的信息都不會輸出
logging.level.root=trace
logging.path=/data/logs/spring-boot-logback
節點介紹
logback節點配置詳解
日志會每天新建一個文件夾,日文文件配置的每50兆,一個文本文件,超過新寫入一個
文件夾:20171031
文件夾內容:all_spring-boot-logback0.log
文件夾內容:all_spring-boot-logback1.log
文件夾內容:all_spring-boot-logback2.log
文件夾內容:err_spring-boot-logback0.log
MDC requestUUID
一種多線程下日志管理實踐方式
logback MDC(Mapped Diagnostic Context)與分布式系統的跟蹤系統
Slf4j MDC 使用和 基于 Logback 的實現分析
MDC介紹 -- 一種多線程下日志管理實踐方式
MDC(Mapped Diagnostic Context,映射調試上下文)是 log4j 和 logback 提供的一種方便在多線程條件下記錄日志的功能。某些應用程序采用多線程的方式來處理多個用戶的請求。在一個用戶的使用過程中,可能有多個不同的線程來進行處理。典型的例子是 Web 應用服務器。當用戶訪問某個頁面時,應用服務器可能會創建一個新的線程來處理該請求,也可能從線程池中復用已有的線程。在一個用戶的會話存續期間,可能有多個線程處理過該用戶的請求。這使得比較難以區分不同用戶所對應的日志。當需要追蹤某個用戶在系統中的相關日志記錄時,就會變得很麻煩。
一種解決的辦法是采用自定義的日志格式,把用戶的信息采用某種方式編碼在日志記錄中。這種方式的問題在于要求在每個使用日志記錄器的類中,都可以訪問到用戶相關的信息。這樣才可能在記錄日志時使用。這樣的條件通常是比較難以滿足的。MDC 的作用是解決這個問題。
MDC 可以看成是一個與當前線程綁定的哈希表,可以往其中添加鍵值對。MDC 中包含的內容可以被同一線程中執行的代碼所訪問。當前線程的子線程會繼承其父線程中的 MDC 的內容。當需要記錄日志時,只需要從 MDC 中獲取所需的信息即可。MDC 的內容則由程序在適當的時候保存進去。對于一個 Web 應用來說,通常是在請求被處理的最開始保存這些數據。
自定義攔截器 logback requestUUID
/**
* 描述: 自定義攔截器 logback requestUUID
*
* @author yanpenglei
* @create 2017-10-30 16:15
**/
public class ControllerInterceptor extends HandlerInterceptorAdapter {
? ?private Logger LOGGER = LoggerFactory.getLogger(ControllerInterceptor.class);
? ?//在請求處理之前回調方法
? ?@Override
? ?public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
? ? ? ?String requestUUID = MDC.get("requestUUID");
? ? ? ?if (requestUUID == null || "".equals(requestUUID)) {
? ? ? ? ? ?String uuid = UUID.randomUUID().toString();
? ? ? ? ? ?uuid = uuid.replaceAll("-", "").toUpperCase();
? ? ? ? ? ?MDC.put("requestUUID", uuid);
? ? ? ? ? ?LOGGER.info("ControllerInterceptor preHandle 在請求處理之前生成 logback requestUUID:{}", uuid);
? ? ? ?}
? ? ? ?return true;// 只有返回true才會繼續向下執行,返回false取消當前請求
? ?}
? ?//請求處理之后回調方法
? ?@Override
? ?public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
? ? ? ?/* 線程結束后需要清除,否則當前線程會一直占用這個requestId值 */
? ? ? ?MDC.remove("requestUUID");
? ? ? ?LOGGER.info("ControllerInterceptor postHandle 請求處理之后清除 logback MDC requestUUID");
? ?}
? ?//整個請求處理完畢回調方法
? ?@Override
? ?public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
? ? ? ?/*整個請求線程結束后需要清除,否則當前線程會一直占用這個requestId值 */
? ? ? ?MDC.clear();
? ? ? ?LOGGER.info("ControllerInterceptor afterCompletion 整個請求處理完畢清除 logback MDC requestUUID");
? ?}
}
對日志進行格式化,時候用到
name="LOG_MSG" value="- | [%X{requestUUID}] | [%d{yyyyMMdd HH:mm:ss.SSS}] | [%level] | [${HOSTNAME}] | [%thread] | [%logger{36}] | --> %msg|%n "/>
/**
* 描述:攔截器配置
*
* @author yanpenglei
* @create 2017-10-30 16:54
**/
@Configuration
public class MyWebMvcConfigurer extends WebMvcConfigurerAdapter {
? ?@Override
? ?public void addInterceptors(InterceptorRegistry registry) {
? ? ? ?/**
? ? ? ? * 多個攔截器組成一個攔截器鏈
? ? ? ? * addPathPatterns 用于添加攔截規則
? ? ? ? * excludePathPatterns 用于排除攔截
? ? ? ? */
? ? ? ?registry.addInterceptor(new ControllerInterceptor()).addPathPatterns("/**");
? ? ? ?super.addInterceptors(registry);
? ?}
}
日志切面
@Aspect
@Component
public class LogAspect {
? ?private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
? ?private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";
? ?private static final String STRING_START = "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n";
? ?private static final String STRING_END ? = "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<;
? ?@Pointcut("execution(* io.ymq.logback.controller..*(..))")
? ?public void serviceLog() {
? ?}
? ?@Around("serviceLog()")
? ?public Object around(ProceedingJoinPoint joinPoint) {
? ? ? ?try {
? ? ? ? ? ?MethodSignature signature = (MethodSignature) joinPoint.getSignature();
? ? ? ? ? ?Method method = signature.getMethod();
? ? ? ? ? ?Class> targetClass = method.getDeclaringClass();
? ? ? ? ? ?StringBuffer classAndMethod = new StringBuffer();
? ? ? ? ? ?Log classAnnotation = targetClass.getAnnotation(Log.class);
? ? ? ? ? ?Log methodAnnotation = method.getAnnotation(Log.class);
? ? ? ? ? ?if (classAnnotation != null) {
? ? ? ? ? ? ? ?if (classAnnotation.ignore()) {
? ? ? ? ? ? ? ? ? ?return joinPoint.proceed();
? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ?classAndMethod.append(classAnnotation.value()).append("-");
? ? ? ? ? ?}
? ? ? ? ? ?if (methodAnnotation != null) {
? ? ? ? ? ? ? ?if (methodAnnotation.ignore()) {
? ? ? ? ? ? ? ? ? ?return joinPoint.proceed();
? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ?classAndMethod.append(methodAnnotation.value());
? ? ? ? ? ?}
? ? ? ? ? ?String target = targetClass.getName() + "#" + method.getName();
? ? ? ? ? ?String params = JSONObject.toJSONStringWithDateFormat(joinPoint.getArgs(), dateFormat, SerializerFeature.WriteMapNullValue);
? ? ? ? ? ?log.info(STRING_START + "{} 開始調用--> {} 參數:{}", classAndMethod.toString(), target, params);
? ? ? ? ? ?long start = System.currentTimeMillis();
? ? ? ? ? ?Object result = joinPoint.proceed();
? ? ? ? ? ?long timeConsuming = System.currentTimeMillis() - start;
? ? ? ? ? ?log.info("\n{} 調用結束 + STRING_END, classAndMethod.toString(), target, JSONObject.toJSONStringWithDateFormat(result, dateFormat, SerializerFeature.WriteMapNullValue), timeConsuming);
? ? ? ? ? ?return result;
? ? ? ?} catch (Throwable throwable) {
? ? ? ? ? ?log.error(throwable.getMessage(), throwable);
? ? ? ?}
? ? ? ?return null;
? ?}
}
測試 logback
瀏覽器訪問:http://127.0.0.1:8080/index/?content="我是測試內容"
@RestController
@RequestMapping(value = "/index")
public class IndexController {
? ?private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
? ?/**
? ? * http://127.0.0.1:8080/index/?content="我是測試內容"
? ? *
? ? * @param content
? ? * @return
? ? */
? ?@Log("首頁IndexController")
? ?@RequestMapping(value="", method= RequestMethod.GET)
? ?public String index(@RequestParam String content) {
? ? ? ?LocalDateTime localDateTime = LocalDateTime.now();
? ? ? ?LOGGER.trace("請求參數:content:{}", content);
? ? ? ?LOGGER.debug("請求參數:content:{}", content);
? ? ? ?LOGGER.info("請求參數:content:{}", content);
? ? ? ?LOGGER.warn("請求參數:content:{}", content);
? ? ? ?LOGGER.error("請求參數:content:{}", content);
? ? ? ?return localDateTime + ",content:" + content;
? ?}
}
前面的?07E94BA525CF4C97851E4B9E4ABB4890?就是通過?logback?的 MDC 做到的
首頁IndexController 開始調用--> io.ymq.logback.controller.IndexController#index 參數:["\"我是測試內容\""]|
- | [07E94BA525CF4C97851E4B9E4ABB4890] | [20171101 10:02:35.589] | [DEBUG] | [DESKTOP-VG43S0C] | [http-nio-8080-exec-1] | [i.y.l.controller.IndexController] | --> 請求參數:content:"我是測試內容"|
- | [07E94BA525CF4C97851E4B9E4ABB4890] | [20171101 10:02:35.589] | [INFO] | [DESKTOP-VG43S0C] | [http-nio-8080-exec-1] | [i.y.l.controller.IndexController] | --> 請求參數:content:"我是測試內容"|
- | [07E94BA525CF4C97851E4B9E4ABB4890] | [20171101 10:02:35.589] | [WARN] | [DESKTOP-VG43S0C] | [http-nio-8080-exec-1] | [i.y.l.controller.IndexController] | --> 請求參數:content:"我是測試內容"|
- | [07E94BA525CF4C97851E4B9E4ABB4890] | [20171101 10:02:35.590] | [ERROR] | [DESKTOP-VG43S0C] | [http-nio-8080-exec-1] | [i.y.l.controller.IndexController] | --> 請求參數:content:"我是測試內容"|
- | [07E94BA525CF4C97851E4B9E4ABB4890] | [20171101 10:02:35.606] | [INFO] | [DESKTOP-VG43S0C] | [http-nio-8080-exec-1] | [i.y.logback.config.commons.LogAspect] | -->
首頁IndexController 調用結束 io.ymq.logback.controller.IndexController#index 返回值:"2017-11-01T10:02:35.589,content:\"我是測試內容\"" 耗時:23ms
從上圖可以看到,日志輸出內容元素具體如下:
requestUUID:一次請求是唯一的時間日期:精確到毫秒日志級別:ERROR,WARN,INFO,DEBUGorTRACE主機名:進程ID:類路徑:分隔符:-->標識實際日志的開始日志內容:
日志切面的響應:
首頁IndexController 開始調用--> io.ymq.logback.controller.IndexController#index 參數:["\"我是測試內容\""]|
首頁IndexController 調用結束 io.ymq.logback.controller.IndexController#index 返回值:"2017-11-01T10:02:35.589,content:\"我是測試內容\"" 耗時:23ms
代碼已放到 Github ,導入?spring-boot-logback?項目
github?spring-boot-logback
slf4j-logback 日志以json格式導入ELK
出處:
公眾號:搜云庫技術團隊
總結
以上是生活随笔為你收集整理的qt能使用logback_SpringBoot 中使用 LogBack 配置的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: unity随机方向的代码_ECCV 20
- 下一篇: python 主语_前深度学习时代--F