Trong bài viết này, tôi sẽ hướng dẫn các bạn sử dụng logback trong spring boot và tạo thư mục chứa log file theo định dạng “yyyy-MM-dd” và “yyyy-MM-dd hhmmss”:
Công cụ và thư viện được sử dụng trong bài viết:
- Spring tool suite 4
- Spring boot 2.7.3
- Maven 3
- Java 8
- Logback 1.2.11
1. Cấu trúc thư mục:
2. Nội dung file pom:
Mặc định, trong spring boot khi bạn add “spring-boot-starter-web” -> logback sẽ được bao gồm:
3. Cấu hình logback:
Spring boot sẽ tự động đọc logback config theo những tên bên dưới:
- logback-spring.xml
- logback.xml
Trong bài viết này tôi sử dụng “logback-spring.xml”:
<?xml version=”1.0″ encoding=”UTF-8″?>
<configuration>
<!--<timestamp key="byDate" datePattern="yyyy-MM-dd" /> -->
<timestamp key="bySecond" datePattern="yyyy-MM-dd'T'HHmmss" timeReference="contextBirth"/>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable
</Pattern>
</layout>
</appender>
<appender name="roleSiftingAppender" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator>
<key>folderDate</key>
<defaultValue>${bySecond}</defaultValue>
</discriminator>
<sift>
<appender name="RollingFile"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${folderDate}/service-log.log</file>
<encoder
class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n</Pattern>
</encoder>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- rollover daily and when the file reaches x MegaBytes -->
<fileNamePattern>${LOG_PATH}/${folderDate}/archived/service-log-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
</sift>
</appender>
<!-- LOG everything at INFO level -->
<root level="info">
<appender-ref ref="roleSiftingAppender" />
<appender-ref ref="Console" />
</root>
<!-- LOG "com.example.logback.*" at TRACE level -->
<logger name="com.example.logback" level="trace" additivity="false">
<appender-ref ref="roleSiftingAppender" />
<appender-ref ref="Console" />
</logger>
</configuration>
Tôi sẽ phân tích một vài biến giá trị trong log file config:
- Biến giá trị “byDate” -> tạo ra folder chứa log file với format “yyyy-MM-dd”
- Biến giá trị “bySecond” -> tạo ra folder chứa log file với format “yyyy-MM-dd’T’HHmmss”
- Biến giá trị “${LOG_PATH}” -> mặc định giá trị sẽ được lấy từ application.yml hoặc application.properties:
Tiếp theo tôi sẽ start service:
- Service started success:
- Kiểm tra thư mục chứa log file:
=> Thư mục chứa log và log file được tạo thành công.
Tiếp theo tôi sẽ tạo một controller và khai báo mapping để kiểm tra xem sau khi thực hiện một hành động(truy cập đường dẫn được khai báo trong controller) -> folder chứa log có tự động sinh ra 1 folder mới hay không?
Tôi tạo 1 class như bên dưới:
Tôi truy cập vào đường dẫn vừa được tạo trong controller và kết quả:
Kiểm tra thư mục chứa log:
Kiểm tra nội dung log file:
Các bạn thấy có một vấn đề ở đây, thư mục chứa log không được tự động tạo mới sau khi thực hiện một hành động, hệ thống sẽ tự động cập nhật nội dung log file đến thự mục chứa log cũ đã được tạo trước đó trong lần start service đầu tiên. Để hệ thống tạo ra 1 thư mục chứa log file mới thì cần phải restart service. Nguyên nhân là do trong lần start service đầu tiên hệ thống ghi nhớ đường dẫn chứa log file và trong những lần sau log sinh ra hệ thống không tự động cập nhật lại theo thời gian thực.
Tới đây tôi sẽ giải quyết vấn đề bằng việc sử dụng SiftingAppender trong logback:
<appender name="roleSiftingAppender" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator>
<key>folderDate</key>
<defaultValue>${bySecond}</defaultValue>
</discriminator>
<sift>
<appender name="RollingFile"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${folderDate}/service-log.log</file>
<encoder
class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n</Pattern>
</encoder>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- rollover daily and when the file reaches x MegaBytes -->
<fileNamePattern>${LOG_PATH}/${folderDate}/archived/service-log-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
</sift>
</appender>
Trong tag <discriminator> có 2 tag:
- Tag <key> chứa tên biến, tại đây tôi để tên biến là “folderDate”
- Tag <defaultValue> chứa giá trị tên thư mục chứa log. chúng ta cần cập nhật giá trị này theo thời gian thực mỗi lần thực hiện 1 hành động
Đế cập nhật giá trị tag <defaultValue> tôi sẽ tạo thêm 1 class implement Filter, mục đích là để bắt lấy các hành động.
Trong class filter tôi sử dụng hàm “doFilter()” để bắt các hành động. Trong hàm “doFilter()” tôi sẽ lấy giá trị thời gian thực và dùng “MDC” để update lại giá trị cho tên thư mục chứa log với tên biến giá trị là “${folderDate}”
Khai báo filter class này trong class khởi động “LogBackdemoApplication”:
Tôi sẽ restart service và kiểm tra lại:
- Cho lần đầu start service:
- Cho lần thứ 2 khi thử truy cập đường dẫn khai báo trong controller:
Như vậy, vấn đề đã được giải quyết. Với cách này có thể tạo thư mục chứa log theo thời gian thực. Trong thực tế có thể áp dụng phương pháp này để mỗi ngày có thể tự tạo thư mục chứa log theo thời gian thực, nếu muốn tạo thư mục chứa log theo định dạng “yyyy-MM-dd” chỉ cần sử dụng biến giá trị “byDate”thayvıˋ”{byDate}” thay vì “{bySecond}”.
Nguồn: viblo.asia