From 57683ad64c5bad2db6d7b238f8adb2b8bee11537 Mon Sep 17 00:00:00 2001 From: msksbr515 Date: Sun, 24 May 2026 00:38:11 +0800 Subject: [PATCH] feat(logging): add structured audit logging with file export - Add logback-spring.xml with daily rolling file appenders - Add structured audit events to RequireRoleAspect - Add logging export configuration to application.yaml - Add janino dependency for logback evaluation - Ignore /log/ export directory --- .gitignore | 3 +- build.gradle.kts | 1 + .../bookmgr/config/RequireRoleAspect.kt | 17 +++-- src/main/resources/application.yaml | 7 ++ src/main/resources/logback-spring.xml | 75 +++++++++++++++++++ 5 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 src/main/resources/logback-spring.xml diff --git a/.gitignore b/.gitignore index 80931cb..2827609 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,5 @@ out/ **/application-dev.yaml **/application-dev.yml **/application-dev.properties -**/.env \ No newline at end of file +**/.env +/log/ diff --git a/build.gradle.kts b/build.gradle.kts index dd0afef..61aaf18 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { implementation("io.jsonwebtoken:jjwt-api:0.13.0") runtimeOnly("io.jsonwebtoken:jjwt-impl:0.13.0") runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.13.0") + runtimeOnly("org.codehaus.janino:janino") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") testRuntimeOnly("org.junit.platform:junit-platform-launcher") diff --git a/src/main/kotlin/com/msksbr/bookmgr/config/RequireRoleAspect.kt b/src/main/kotlin/com/msksbr/bookmgr/config/RequireRoleAspect.kt index ef08953..78acc74 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/config/RequireRoleAspect.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/config/RequireRoleAspect.kt @@ -1,7 +1,7 @@ package com.msksbr.bookmgr.config import com.msksbr.bookmgr.annotation.RequireRole -import com.msksbr.bookmgr.script.log +import com.msksbr.bookmgr.script.audit import com.msksbr.bookmgr.template.ApiResult import org.aspectj.lang.ProceedingJoinPoint import org.aspectj.lang.annotation.Around @@ -43,20 +43,25 @@ class RequireRoleAspect( val role = request.getAttribute("role") as? String if (username == null) { - log.warn("[AUDIT] unauthenticated | ip={} | path={} | required={}", ip, path, requireRole.role) + audit.warn( + "event=CTRL | user=${username ?: "N/A"} | ip={} | op={} | result=unauthenticated | required={}", + ip, path, requireRole.role + ) return ApiResult.unauthorized("Missing or invalid token") } val allowedRoles = rolePermissions[role] ?: emptySet() if (requireRole.role !in allowedRoles) { - log.warn( - "[AUDIT] access denied | user={} | ip={} | path={} | required={} | actual={}", + audit.warn( + "event=CTRL | user={} | ip={} | op={} | result=denied | required={} | actual={}", username, ip, path, requireRole.role, role ) return ApiResult.forbidden("Access denied: insufficient permissions") } - log.info("[AUDIT] access allowed | user={} | ip={} | path={} | role={}", - username, ip, path, role) + audit.info( + "event=CTRL | user={} | ip={} | op={} | result=allowed | role={}", + username, ip, path, role + ) return joinPoint.proceed() } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 63f6412..2848e6a 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -36,6 +36,13 @@ mybatis-plus: logging: level: com.msksbr.bookmgr: ${LOG_LEVEL:INFO} # 包级别日志级别,dev 配置中覆盖为 DEBUG + export: + path: ${LOG_PATH:} # 导出根目录,未设置则不写文件 + audit-enabled: ${LOG_EXPORT_AUDIT:true} # 审计日志持久化开关,默认开启 + runtime-enabled: ${LOG_EXPORT_RUNTIME:false} # 运行日志持久化开关,默认关闭 + # 导出路径: + # $LOG_PATH/audit/audit.log ← 审计日志(当天),旧文件压缩为 .gz + # $LOG_PATH/runtime/runtime.log ← 运行日志(当天),旧文件压缩为 .gz # ---- JWT ---- jwt: diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..47ec93b --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) [%thread] %cyan(%logger{36}) - %msg%n + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) [AUDIT] - %msg%n + + + + + + ${LOG_PATH}/audit/audit.log + + ${LOG_PATH}/audit/audit.%d{yyyy-MM-dd}.%i.log.gz + 100MB + 365 + 20GB + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level - %msg%n + + + + + + ${LOG_PATH}/runtime/runtime.log + + ${LOG_PATH}/runtime/runtime.%d{yyyy-MM-dd}.%i.log.gz + 100MB + 30 + 5GB + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + + + + + +