【java】日志优化,请求id+错误描述+错误行数

本文探讨了在Java中如何捕获并处理异常,通过获取异常堆栈的首个元素来定位错误发生的具体行数,并使用loggerException进行详细的错误日志记录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

catch (Exception e) {
    StackTraceElement stackTraceElement= e.getStackTrace()[0];// 得到异常棧的首个元素
    loggerException.error(String.format("seqid=%s,Exception=%s,getLineNumber=%s", seqId,
		e.toString().replaceAll("\n", "|"),stackTraceElement.getLineNumber()));
}

 

<think>我们正在设计Java中SQL执行器的日志监控链路。这个系统需要能够追踪SQL执行的全过程,包括执行时间、参数、结果等,并能够将相关日志关联起来,便于问题排查和性能分析。 设计考虑点: 1. 日志格式规范化:使用结构化日志(如JSON格式),包含traceId、spanId、SQL语句、参数、执行时间、执行结果(或错误信息)等。 2. 链路追踪:使用分布式追踪系统(如Zipkin、Jaeger)的traceId来串联整个请求链路,包括SQL执行部分。 3. 监控指标:记录SQL执行时间、是否成功等指标,便于后续监控报警。 4. 敏感信息处理:对SQL中的敏感参数进行脱敏处理。 5. 性能影响:日志记录应异步进行,避免影响主业务逻辑。 实现步骤: 1. 集成分布式追踪:在应用入口生成traceId,并传递到SQL执行层。 2. 在SQL执行前,记录开始时间,并生成一个唯一的spanId(如果使用OpenTracing等标准,可以使用其API)。 3. 执行SQL,捕获执行结果或异常。 4. 执行后,记录结束时间,计算耗时,然后异步输出日志。 5. 日志中需要包含:traceId, spanId, 执行时间,SQL语句(参数替换后的,或者参数单独列出并脱敏),执行结果(行数错误信息等)。 代码结构示例(伪代码): ```java public class LoggableSqlExecutor { // 使用SLF4J日志框架,配合Logback等,输出结构化日志 private static final Logger logger = LoggerFactory.getLogger(LoggableSqlExecutor.class); public <T> T execute(Connection connection, String sql, List<Object> parameters, ResultSetHandler<T> handler) { // 获取当前追踪上下文(traceId, spanId等) String traceId = TracingContext.getTraceId(); String spanId = TracingContext.createSpanId(); // 创建新的span long startTime = System.currentTimeMillis(); try (PreparedStatement stmt = connection.prepareStatement(sql)) { // 设置参数 for (int i = 0; i < parameters.size(); i++) { stmt.setObject(i + 1, parameters.get(i)); } // 执行 ResultSet rs = stmt.executeQuery(); T result = handler.handle(rs); // 记录成功日志(异步) logSuccess(traceId, spanId, sql, parameters, startTime, result); return result; } catch (SQLException e) { // 记录失败日志(异步) logError(traceId, spanId, sql, parameters, startTime, e); throw new RuntimeException(e); } } private void logSuccess(String traceId, String spanId, String sql, List<Object> parameters, long startTime, Object result) { long duration = System.currentTimeMillis() - startTime; Map<String, Object> logMap = new HashMap<>(); logMap.put("traceId", traceId); logMap.put("spanId", spanId); logMap.put("event", "sql_execution"); logMap.put("sql", sql); logMap.put("parameters", maskSensitiveParameters(parameters)); // 脱敏处理 logMap.put("duration", duration); logMap.put("result", result); // 注意:结果可能很大,可以只记录行数等摘要 logMap.put("status", "success"); // 异步记录日志 logger.info(JSON.toJSONString(logMap)); } private void logError(String traceId, String spanId, String sql, List<Object> parameters, long startTime, Throwable e) { long duration = System.currentTimeMillis() - startTime; Map<String, Object> logMap = new HashMap<>(); logMap.put("traceId", traceId); logMap.put("spanId", spanId); logMap.put("event", "sql_execution"); logMap.put("sql", sql); logMap.put("parameters", maskSensitiveParameters(parameters)); logMap.put("duration", duration); logMap.put("status", "error"); logMap.put("errorMessage", e.getMessage()); logger.error(JSON.toJSONString(logMap), e); } // 脱敏函数,例如对密码等敏感字段进行掩码 private List<Object> maskSensitiveParameters(List<Object> parameters) { // 实现脱敏逻辑,这里简单示例:将参数转为字符串,然后对特定字段脱敏 // 实际中可能需要根据参数名(如果知道位置对应关系)来脱敏,但这里参数名未知,所以需要根据上下文或配置 // 此处简化为全部参数都脱敏(或者只脱敏特定模式?),实际应更智能 return parameters.stream().map(this::mask).collect(Collectors.toList()); } private Object mask(Object obj) { // 简单示例:将字符串类型的参数替换为***,数字等不处理?实际需要更复杂的规则 if (obj instanceof CharSequence) { return "***"; } return obj; } } ``` 注意事项: 1. 异步日志:可以使用Logback的AsyncAppender,或者使用单独的线程池来处理日志,避免阻塞业务线程。 2. 日志量控制:对于批量操作,可能产生大量日志,需要控制日志级别或采样。 3. 追踪上下文传递:在分布式系统中,traceId需要跨线程传递(使用MDC或类似机制),在异步场景下需要特别注意。 监控链路: 日志收集 -> 日志存储(如Elasticsearch) -> 通过traceId在Kibana等工具中查询整个请求链路。 此外,还可以将执行时间等指标发送到监控系统(如Prometheus),设置报警规则。 §§相关问题§§ 1. Java中如何实现SQL执行器的日志脱敏? 2. 如何设计一个高性能的异步日志记录器避免影响业务? 3. 分布式追踪中如何传递traceId和spanId? 4. 如何监控SQL执行性能并设置报警?</think>### Java SQL执行器日志监控链路设计 #### 设计目标 构建可追踪的SQL执行日志监控链路,需实现: 1. **全链路追踪**:通过唯一ID串联SQL执行全流程 2. **关键指标监控**:执行时间、状态、参数等 3. **异常追踪**:精准定位SQL执行故障点 4. **低侵入性**:最小化业务代码改动 #### 核心组件设计 ```mermaid graph TD A[SQL执行入口] --> B[生成TraceID] B --> C[记录执行日志] C --> D[异步日志处理] D --> E[日志存储] E --> F[监控告警] F --> G[可视化展示] ``` #### 关键实现步骤 1. **唯一追踪标识生成** 使用`UUID`或分布式ID生成器(如Snowflake): ```java public class TraceContext { private static final ThreadLocal<String> traceId = new ThreadLocal<>(); public static String generateTraceId() { return UUID.randomUUID().toString().replace("-", ""); } public static void setTraceId(String id) { traceId.set(id); } public static String getTraceId() { return traceId.get(); } } ``` 2. **日志埋点设计** 在SQL执行器关键节点植入日志: ```java public class SqlExecutor { private static final Logger logger = LoggerFactory.getLogger("SQL_MONITOR"); public void execute(String sql, Object... params) { String traceId = TraceContext.getTraceId(); long start = System.currentTimeMillis(); try { // 1. 记录SQL执行开始 logger.info("{} | SQL_START | {}", traceId, sql); // 2. 实际执行逻辑(示例) jdbcTemplate.execute(sql); // 3. 记录成功日志 long cost = System.currentTimeMillis() - start; logger.info("{} | SQL_SUCCESS | {}ms | params={}", traceId, cost, Arrays.toString(params)); } catch (Exception e) { // 4. 记录异常日志 logger.error("{} | SQL_FAILED | error={}", traceId, e.getMessage()); throw e; } } } ``` 3. **异步日志处理** 使用`Logback`异步Appender避免阻塞主线程: ```xml <!-- logback.xml配置 --> <appender name="ASYNC_SQL" class="ch.qos.logback.classic.AsyncAppender"> <queueSize>1000</queueSize> <discardingThreshold>0</discardingThreshold> <appender-ref ref="SQL_JSON" /> </appender> <appender name="SQL_JSON" class="ch.qos.logback.core.FileAppender"> <file>logs/sql-monitor.log</file> <encoder class="net.logstash.logback.encoder.LogstashEncoder"/> </appender> ``` 4. **监控指标采集** 通过`Micrometer`暴露Prometheus指标: ```java public class SqlMetrics { private static final Counter sqlCounter = Counter.builder("sql_exec_count") .tag("status", "success") .register(Metrics.globalRegistry); private static final Timer sqlTimer = Timer.builder("sql_exec_time") .register(Metrics.globalRegistry); public static void recordSuccess(long cost) { sqlCounter.increment(); sqlTimer.record(cost, TimeUnit.MILLISECONDS); } } ``` 5. **日志关联分析** 使用`ELK`或`Grafana Loki`实现: ```bash # 日志格式示例 { "traceId": "8a7d6f3e1b", "timestamp": "2023-10-05T14:23:01Z", "sql": "SELECT * FROM users WHERE id=?", "params": [101], "durationMs": 42, "status": "SUCCESS", "service": "order-service" } ``` #### 敏感数据处理 对敏感参数进行脱敏: ```java public String maskSensitive(String sql, Object[] params) { Pattern pattern = Pattern.compile("password|pwd|token", Pattern.CASE_INSENSITIVE); if (pattern.matcher(sql).find()) { return sql.replaceAll("= \\?", "= [MASKED]"); } return sql; } ``` #### 监控告警规则 在Prometheus中配置: ```yaml alerting_rules: - name: SQL_Alerts rules: - alert: SlowQuery expr: histogram_quantile(0.9, rate(sql_exec_time_seconds_bucket[5m])) > 1 for: 2m - alert: HighFailureRate expr: rate(sql_exec_count{status="failed"}[5m]) / rate(sql_exec_count[5m]) > 0.1 ``` #### 性能优化建议 1. 日志采样:对高频查询启用采样率控制 2. 批量写入:日志使用批量压缩写入 3. 动态开关:运行时动态调整日志级别 4. 链路聚合:相同TraceID日志自动聚合展示 > 最佳实践:结合分布式追踪系统(如Jaeger)实现跨服务SQL追踪[^1],通过OpenTelemetry自动注入追踪上下文[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木有会

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值