如何用Prometheus指标捕获Rest API中slf4j的log.error()调用并告警
通用实现方案:基于SLF4J日志自动统计Prometheus错误计数器并告警
核心思路
通过拦截SLF4J的log.error()调用动作,自动触发Prometheus计数器递增,再利用Prometheus的告警规则,对15分钟内的错误次数阈值进行监控,满足“不中断业务但捕获校验失败场景”的需求。
具体实现方案
方案1:自定义SLF4J Appender(以Logback为例)
这是无侵入性最强的通用方案,无需修改业务代码:
- 实现自定义Appender,继承
ch.qos.logback.core.AppenderBase<ILoggingEvent>,在append方法中判断日志级别为ERROR时,递增Prometheus计数器 - 在Logback配置文件中注册该Appender,确保所有日志输出都经过它
示例代码:
import ch.qos.logback.core.AppenderBase; import ch.qos.logback.classic.spi.ILoggingEvent; import io.prometheus.client.Counter; public class PrometheusErrorAppender extends AppenderBase<ILoggingEvent> { // 定义带标签的Prometheus计数器,可扩展业务模块、错误类型等标签 private static final Counter ERROR_COUNTER = Counter.build() .name("api_error_total") .help("Total number of ERROR logs emitted by the API") .labelNames("module", "logger") .register(); @Override protected void append(ILoggingEvent event) { if (event.getLevel().isGreaterOrEqual(ch.qos.logback.classic.Level.ERROR)) { // 根据项目包结构提取模块名,示例逻辑按需调整 String module = event.getLoggerName().split("\\.")[3]; ERROR_COUNTER.labels(module, event.getLoggerName()).inc(); } } }
Logback配置片段(logback.xml):
<appender name="PROMETHEUS_ERROR" class="com.example.PrometheusErrorAppender"/> <root level="info"> <appender-ref ref="CONSOLE"/> <appender-ref ref="PROMETHEUS_ERROR"/> </root>
方案2:AspectJ切面拦截
通过AOP切面拦截所有org.slf4j.Logger.error()方法调用,适合允许引入AspectJ的项目:
- 定义切面,匹配Logger的所有
error重载方法 - 在切面通知中触发计数器递增
示例代码:
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import io.prometheus.client.Counter; @Aspect public class LoggerErrorAspect { private static final Counter ERROR_COUNTER = Counter.build() .name("api_error_total") .help("Total number of ERROR logs emitted by the API") .labelNames("logger") .register(); // 匹配所有Logger的error方法 @Pointcut("execution(* org.slf4j.Logger.error(..))") public void logErrorPointcut() {} @AfterReturning("logErrorPointcut()") public void afterLogError(JoinPoint joinPoint) { Logger logger = (Logger) joinPoint.getTarget(); ERROR_COUNTER.labels(logger.getName()).inc(); } }
方案3:自定义日志工具类封装
适合愿意统一日志调用入口的项目,完全可控:
- 封装项目专属的日志工具类,内部调用SLF4J Logger并触发计数器
- 替换项目中所有直接使用
LoggerFactory.getLogger()的代码为该工具类
示例代码:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.prometheus.client.Counter; public class AppLogger { private static final Counter ERROR_COUNTER = Counter.build() .name("api_error_total") .help("Total number of ERROR logs emitted by the API") .labelNames("module") .register(); private final Logger logger; private final String module; private AppLogger(Class<?> clazz, String module) { this.logger = LoggerFactory.getLogger(clazz); this.module = module; } public static AppLogger getLogger(Class<?> clazz, String module) { return new AppLogger(clazz, module); } public void error(String message) { logger.error(message); ERROR_COUNTER.labels(module).inc(); } // 重载其他error方法,覆盖所有日志场景 public void error(String message, Throwable t) { logger.error(message, t); ERROR_COUNTER.labels(module).inc(); } }
Prometheus告警规则配置
在Prometheus配置文件中添加以下规则,实现15分钟内错误次数超过10次的告警:
groups: - name: api_error_monitoring rules: - alert: ApiErrorThresholdExceeded expr: increase(api_error_total[15m]) > 10 for: 1m labels: severity: critical annotations: summary: API错误次数超出阈值 description: 过去15分钟内API累计错误日志达到{{ $value }}次,已超过阈值10次
注意事项
- 计数器建议添加业务标签(如模块、错误类型),便于告警后快速定位问题
- 若日志量极大,可考虑异步处理计数器递增,避免影响业务性能
- 确保自定义Appender/切面不会屏蔽原有日志输出,保留正常的日志链路
内容的提问来源于stack exchange,提问作者Sachin Mankotia
相关产品推荐
相关产品推荐

