Java案例如何筛选日志信息?

wen java案例 70

本文目录导读:

Java案例如何筛选日志信息?

  1. 使用日志框架的级别过滤(最常用)
  2. 使用 Markers 或 MDC 进行上下文过滤
  3. 使用正则表达式或字符串匹配(自定义过滤器)
  4. 使用框架内置的高级过滤器(如 ThrottleFilter)
  5. 使用 AOP(面向切面编程)过滤日志
  6. 生产环境的最佳实践:动态切换过滤
  7. 总结对比

使用日志框架的级别过滤(最常用)

这是最基础、最标准的方法,通过配置日志级别(TRACE, DEBUG, INFO, WARN, ERROR),框架会自动过滤掉低于设定级别的日志。

案例:使用 Logback

配置文件 logback.xml

<configuration>
    <!-- 定义全局日志级别为 INFO,意味着 TRACE 和 DEBUG 日志不会被打印 -->
    <root level="INFO"/>
    <!-- 针对某个特定包(如 com.example.dao)设置为 DEBUG 级别 -->
    <logger name="com.example.dao" level="DEBUG" additivity="false">
        <appender-ref ref="FILE"/>
    </logger>
    <!-- 针对另一个包(如 com.example.service)只显示 ERROR 级别以上的日志 -->
    <logger name="com.example.service" level="ERROR"/>
</configuration>

Java代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyService {
    private static final Logger log = LoggerFactory.getLogger(MyService.class);
    public void process() {
        log.trace("这是一个跟踪日志"); // 不会被打印(如果全局级别是 INFO)
        log.info("这是一个信息日志");
        log.error("这是一个错误日志");
    }
}

结果: 只有 INFO 及以上的日志会被输出;但 com.example.dao 包下的 DEBUG 日志会被单独保留(输出到文件)。


使用 Markers 或 MDC 进行上下文过滤

当需要基于特定的业务维度(如用户ID、订单号)筛选日志时,使用 MDC(Mapped Diagnostic Context)。

案例:基于用户ID筛选

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class UserService {
    private static final Logger log = LoggerFactory.getLogger(UserService.class);
    public void handleRequest(String userId, String requestData) {
        // 将用户ID放入上下文中
        MDC.put("userId", userId);
        MDC.put("requestType", "API_CALL");
        try {
            log.info("开始处理用户请求: {}", requestData);
            // ... 业务逻辑
            log.debug("中间处理细节: {}", someDetail);
        } catch (Exception e) {
            log.error("处理用户请求失败", e);
        } finally {
            // 务必清理上下文,防止内存泄漏
            MDC.clear();
        }
    }
}

配合 Logback 配置(使用过滤器):

<appender name="USER_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
        <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
            <expression>
                // 只记录 userId = "user123" 的日志
                return mdc.get("userId") != null && mdc.get("userId").equals("user123");
            </expression>
        </evaluator>
        <OnMismatch>DENY</OnMismatch>  <!-- 不匹配则拒绝 -->
        <OnMatch>ACCEPT</OnMatch>      <!-- 匹配则接受 -->
    </filter>
    <encoder>
        <pattern>%date [%thread] %-5level %logger{36} - [%X{userId}] %msg%n</pattern>
    </encoder>
</appender>

效果: 只有用户ID为 user123 的日志才会被写入该文件,其他用户的日志被过滤掉。


使用正则表达式或字符串匹配(自定义过滤器)

当需要基于日志消息内容(不仅仅是级别)过滤时,可以编写自定义的 TurborFilter 或实现 Logback 的 Filter。

案例:过滤掉所有包含“健康检查”或“心跳”的日志

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;
public class HealthCheckFilter extends Filter<ILoggingEvent> {
    @Override
    public FilterReply decide(ILoggingEvent event) {
        // 如果日志消息包含“健康检查”或“心跳”,则拒绝输出
        if (event.getMessage().contains("健康检查") || event.getMessage().contains("心跳")) {
            return FilterReply.DENY;
        }
        return FilterReply.NEUTRAL;  // 其他日志正常处理
    }
}

配置中引用:

<appender name="MAIN_LOG" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="com.example.filter.HealthCheckFilter"/>
    <encoder>
        <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

使用框架内置的高级过滤器(如 ThrottleFilter)

对于生产环境中的紧急问题排查,可以使用 Logback 的 TurboFilter 实现基于频率的过滤。

案例:限制某个异常日志的打印频率(防止打爆磁盘)

<configuration>
    <turboFilter class="ch.qos.logback.classic.turbo.DuplicateMessageFilter">
        <!-- 在5秒内,相同内容的日志最多打印10次 -->
        <cacheSize>100</cacheSize>
        <allowedRepetitions>10</allowedRepetitions>
        <cacheExpirationTimeInSeconds>5</cacheExpirationTimeInSeconds>
    </turboFilter>
</configuration>

使用 AOP(面向切面编程)过滤日志

如果你需要更复杂的业务逻辑过滤(例如根据方法参数、返回值),可以使用 Spring AOP。

案例:只记录返回值为 null 的方法日志

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
    private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);
    @Around("execution(* com.example.service.*.*(..))")
    public Object filterLog(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        // 只记录返回值为 null 的方法调用
        if (result == null) {
            log.warn("方法 {} 返回了 null,参数为: {}", joinPoint.getSignature(), joinPoint.getArgs());
        }
        return result;
    }
}

生产环境的最佳实践:动态切换过滤

在生产环境中,通常推荐使用 Elasticsearch + KibanaSplunk 等日志聚合平台,而不是在应用层做复杂的过滤,但依然可以结合 MDC 实现按需过滤:

// 在入口处动态设置
MDC.put("traceId", UUID.randomUUID().toString());
// 在出口处清理

然后在日志系统中(如 Kibana)通过 traceId 字段精确筛选出一次完整的请求链路。


总结对比

方法 适用场景 优缺点
日志级别过滤 区分调试/生产环境的日志量 简单高效,但不能基于内容过滤
MDC 上下文过滤 按用户、请求ID、事务ID筛选 灵活,便于链路追踪和问题排查
自定义过滤器 (如排除心跳) 高度定制,但需维护
AOP 过滤 按方法执行结果过滤 适合业务级监控,但有一定的性能影响
外部日志平台 全量收集后在线搜索 最适合生产环境,但需要额外组件

推荐方案:

  • 开发阶段:使用级别过滤 + MDC
  • 生产环境:全量收集到 Elasticsearch 或 Loki,通过 UI 进行动态过滤,同时在应用层只保留 ERROR 和 WARN 级别的日志到本地文件以备紧急查看。

抱歉,评论功能暂时关闭!