淘宝返利软件的日志审计系统:Java Logback+ELK Stack实现操作日志的可追溯与可视化分析
大家好,我是 微赚淘客系统3.0 的研发者省赚客!
在淘宝返利类软件中,用户提现、订单绑定、佣金结算等关键操作必须具备完整、不可篡改的日志记录,以满足安全审计与问题回溯需求。我们基于 Java 技术栈,采用 Logback 作为日志框架,结合 ELK(Elasticsearch + Logstash + Kibana)构建集中式日志审计系统,实现结构化日志采集、存储与可视化分析。
日志结构化设计
首先定义统一的操作日志模型,确保字段语义清晰:
java
package juwatech.cn.log.model;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
public class AuditLog {
private String traceId; // 全链路追踪ID
private String userId; // 操作用户ID
private String action; // 操作类型,如 "WITHDRAW_SUBMIT"
private String resourceId; // 关联资源ID,如订单号
private String clientIp; // 客户端IP
private String userAgent; // 客户端标识
private String status; // SUCCESS/FAIL
private String errorMessage; // 错误详情(可选)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime timestamp;
// 省略 getter/setter
}
Logback 配置输出 JSON 格式日志
在 logback-spring.xml 中配置 net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder,将日志直接输出为 JSON:
xml
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp/>
<logLevel/>
<loggerName/>
<message/>
<arguments/>
<mdc/> <!-- 支持 MDC 注入 traceId、userId 等 -->
<stackTrace/>
</providers>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
MDC 上下文注入关键字段
在请求入口处通过拦截器注入用户ID、TraceID等信息到 MDC:
java
package juwatech.cn.log.interceptor;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
@Component
public class AuditLogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String traceId = UUID.randomUUID().toString().replace("-", "");
String userId = request.getHeader("X-User-ID"); // 由网关注入
String clientIp = getClientIp(request);
MDC.put("traceId", traceId);
MDC.put("userId", userId != null ? userId : "anonymous");
MDC.put("clientIp", clientIp);
MDC.put("userAgent", request.getHeader("User-Agent"));
return true;
}
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty()) {
ip = request.getRemoteAddr();
}
return ip;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
MDC.clear();
}
}
注册拦截器:
java
package juwatech.cn.config;
import juwatech.cn.log.interceptor.AuditLogInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuditLogInterceptor())
.addPathPatterns("/api/**");
}
}
业务层记录审计日志
在关键服务方法中记录结构化日志:
java
package juwatech.cn.service;
import juwatech.cn.log.model.AuditLog;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class WithdrawService {
private static final Logger auditLogger = LoggerFactory.getLogger("AUDIT_LOG");
private final ObjectMapper objectMapper = new ObjectMapper();
public void submitWithdraw(String userId, String orderId, double amount) {
try {
// 执行提现逻辑
processWithdraw(userId, orderId, amount);
AuditLog log = new AuditLog();
log.setAction("WITHDRAW_SUBMIT");
log.setUserId(userId);
log.setResourceId(orderId);
log.setStatus("SUCCESS");
log.setTimestamp(java.time.LocalDateTime.now());
auditLogger.info(objectMapper.writeValueAsString(log));
} catch (Exception e) {
AuditLog log = new AuditLog();
log.setAction("WITHDRAW_SUBMIT");
log.setUserId(userId);
log.setResourceId(orderId);
log.setStatus("FAIL");
log.setErrorMessage(e.getMessage());
log.setTimestamp(java.time.LocalDateTime.now());
auditLogger.warn(objectMapper.writeValueAsString(log));
throw e;
}
}
private void processWithdraw(String userId, String orderId, double amount) {
// 实际业务逻辑
}
}
Logstash 配置解析与转发
Logstash 配置文件 logstash.conf 如下,从 Filebeat 或直接从 Docker 日志读取并写入 Elasticsearch:
conf
input {
beats {
port => 5044
}
}
filter {
json {
source => "message"
target => "log"
}
date {
match => ["[log][timestamp]", "yyyy-MM-dd HH:mm:ss"]
target => "@timestamp"
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch.juwatech.cn:9200"]
index => "juwatech-audit-%{+YYYY.MM.dd}"
}
}
Kibana 可视化看板
在 Kibana 中创建索引模式 juwatech-audit-*,并基于以下字段构建看板:
- 按
log.action统计操作分布 - 按
log.status监控失败率 - 通过
log.traceId追踪全链路 - 设置告警规则:当
WITHDRAW_SUBMIT失败率 > 1% 时触发通知
日志安全与合规
所有审计日志禁止删除,Elasticsearch 索引设置生命周期策略(ILM)保留180天,并开启 HTTPS 与 Basic Auth 访问控制。同时,敏感字段(如手机号)在业务层脱敏后再记录。
本文著作权归 微赚淘客系统3.0 研发团队,转载请注明出处!