当前位置: 首页 > news >正文

使用注解将日志存入Elasticsearch

方案一:使用Spring AOP + 自定义注解

1. 自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EsLog {String module() default "";String operation() default "";boolean saveParams() default true;boolean saveResult() default false;String index() default "app-logs";
}

2. 日志实体类


@Data
@AllArgsConstructor
@NoArgsConstructor
public class LogEntry {private String id;private String module;private String operation;private String method;private String className;private Object params;private Object result;private String userId;private String username;private String ip;private Long executionTime;private Date createTime;private Boolean success;private String errorMsg;
}

3. AOP切面处理

@Aspect
@Component
@Slf4j
public class EsLogAspect {@Autowiredprivate ElasticsearchRestTemplate elasticsearchRestTemplate;@Autowiredprivate HttpServletRequest request;@Around("@annotation(esLog)")public Object around(ProceedingJoinPoint joinPoint, EsLog esLog) throws Throwable {long startTime = System.currentTimeMillis();LogEntry logEntry = new LogEntry();try {// 设置基本信息setupBasicInfo(joinPoint, esLog, logEntry);// 执行目标方法Object result = joinPoint.proceed();// 记录成功信息long endTime = System.currentTimeMillis();logEntry.setSuccess(true);logEntry.setExecutionTime(endTime - startTime);if (esLog.saveResult()) {logEntry.setResult(result);}// 异步保存到ESsaveToEsAsync(logEntry);return result;} catch (Exception e) {// 记录异常信息long endTime = System.currentTimeMillis();logEntry.setSuccess(false);logEntry.setErrorMsg(e.getMessage());logEntry.setExecutionTime(endTime - startTime);saveToEsAsync(logEntry);throw e;}}private void setupBasicInfo(ProceedingJoinPoint joinPoint, EsLog esLog, LogEntry logEntry) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();logEntry.setId(UUID.randomUUID().toString());logEntry.setModule(esLog.module());logEntry.setOperation(esLog.operation());logEntry.setMethod(method.getName());logEntry.setClassName(method.getDeclaringClass().getName());logEntry.setCreateTime(new Date());// 获取请求参数if (esLog.saveParams()) {Object[] args = joinPoint.getArgs();String[] paramNames = signature.getParameterNames();Map<String, Object> params = new HashMap<>();for (int i = 0; i < args.length; i++) {// 过滤敏感参数if (!isSensitiveParam(paramNames[i])) {params.put(paramNames[i], args[i]);}}logEntry.setParams(params);}// 获取用户信息setupUserInfo(logEntry);// 获取IP地址logEntry.setIp(getClientIp());}@Asyncpublic void saveToEsAsync(LogEntry logEntry) {try {IndexQuery indexQuery = new IndexQueryBuilder().withId(logEntry.getId()).withObject(logEntry).build();elasticsearchRestTemplate.index(indexQuery, IndexCoordinates.of(logEntry.getIndex()));} catch (Exception e) {log.error("保存日志到ES失败: {}", e.getMessage(), e);}}private String getClientIp() {String ip = request.getHeader("X-Forwarded-For");if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return ip;}private void setupUserInfo(LogEntry logEntry) {// 从SecurityContext获取用户信息Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication != null && authentication.isAuthenticated()) {Object principal = authentication.getPrincipal();if (principal instanceof UserDetails) {UserDetails userDetails = (UserDetails) principal;logEntry.setUsername(userDetails.getUsername());}}}private boolean isSensitiveParam(String paramName) {return paramName.toLowerCase().contains("password") || paramName.toLowerCase().contains("token") ||paramName.toLowerCase().contains("secret");}
}

方案二:使用Logback直接输出到ES

1. 添加依赖

<dependency><groupId>net.logstash.logback</groupId><artifactId>logstash-logback-encoder</artifactId><version>7.2</version>
</dependency>

2. logback-spring.xml配置


<?xml version="1.0" encoding="UTF-8"?>
<configuration><appender name="ES" class="org.elasticsearch.logback.appender.ElasticsearchAppender"><url>http://localhost:9200</url><index>app-logs</index><type>_doc</type><connectTimeout>3000</connectTimeout><errorsToStderr>true</errorsToStderr><includeCallerData>true</includeCallerData><logsToStderr>false</logsToStderr><maxQueueSize>1024</maxQueueSize><readTimeout>3000</readTimeout></appender><appender name="ASYNC_ES" class="ch.qos.logback.classic.AsyncAppender"><appender-ref ref="ES" /><queueSize>1024</queueSize><discardingThreshold>0</discardingThreshold><includeCallerData>true</includeCallerData><maxFlushTime>0</maxFlushTime><neverBlock>true</neverBlock></appender><logger name="com.example.annotation" level="INFO" additivity="false"><appender-ref ref="ASYNC_ES" /></logger><root level="INFO"><appender-ref ref="ASYNC_ES" /></root>
</configuration>

方案三:结合业务使用的示例

1. 在Service中使用注解


@Service
@Slf4j
public class UserService {@EsLog(module = "用户管理", operation = "创建用户", saveParams = true)public User createUser(CreateUserRequest request) {// 业务逻辑return userRepository.save(request.toUser());}@EsLog(module = "用户管理", operation = "删除用户", saveParams = false)public void deleteUser(Long userId) {userRepository.deleteById(userId);}@EsLog(module = "用户管理", operation = "查询用户列表", saveResult = true)public Page<User> getUserList(UserQuery query) {return userRepository.findByCondition(query);}
}

2. 配置类

@Configuration
@EnableElasticsearchRepositories
@EnableAsync
@EnableAspectJAutoProxy
public class EsLogConfig {@Beanpublic ElasticsearchRestTemplate elasticsearchRestTemplate() {return new ElasticsearchRestTemplate(elasticsearchClient());}@Beanpublic RestHighLevelClient elasticsearchClient() {ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo("localhost:9200").build();return RestClients.create(clientConfiguration).rest();}
}

方案四:高级特性

1. 条件日志注解


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ConditionalEsLog {String module();String operation();LogLevel level() default LogLevel.INFO;String condition() default "";Class<? extends LogEvaluator> evaluator() default DefaultLogEvaluator.class;
}public interface LogEvaluator {boolean shouldLog(ProceedingJoinPoint joinPoint);
}

2. 批量操作日志

@Component
public class BatchLogProcessor {private final List<LogEntry> logBuffer = new ArrayList<>();private final int batchSize = 100;@Scheduled(fixedRate = 5000) // 每5秒执行一次@Asyncpublic void batchSaveLogs() {if (logBuffer.isEmpty()) {return;}List<LogEntry> logsToSave;synchronized (logBuffer) {logsToSave = new ArrayList<>(logBuffer);logBuffer.clear();}List<IndexQuery> queries = logsToSave.stream().map(log -> new IndexQueryBuilder().withId(log.getId()).withObject(log).build()).collect(Collectors.toList());elasticsearchRestTemplate.bulkIndex(queries, IndexCoordinates.of("app-logs"));}public void addLog(LogEntry logEntry) {synchronized (logBuffer) {logBuffer.add(logEntry);if (logBuffer.size() >= batchSize) {batchSaveLogs();}}}
}

总结

这种基于注解的ES日志方案具有以下优点:

  1. 无侵入性:通过注解实现,不干扰业务逻辑

  2. 灵活配置:可以控制记录参数、返回值等

  3. 异步处理:不影响主业务流程性能

  4. 结构化存储:便于后续查询和分析

  5. 易于扩展:可以轻松添加新的日志字段或处理逻辑

http://www.dtcms.com/a/540717.html

相关文章:

  • 【STM32】WDG看门狗
  • 无锡市建设安全监督网站全国统一核酸检测价格
  • 做网站购买备案域名织梦高端html5网站建设工作室网络公司网站模板
  • 2.CSS3.(4).html
  • 记一次诡异的“偶发 404”排查:CDN 回源到 OSS 导致 REST API 失败
  • C++笔记(面向对象)类模板
  • Selenium IDE下载和安装教程(附安装包)
  • Quartz框架实现根据设置的cron表达式进行定时任务执行
  • linux20 线程同步--信号量
  • 内核的文件预取逻辑及blockdev的相关配置
  • [特殊字符] Web 字体裁剪优化实践:把 42MB 字体包瘦到 1.6MB
  • 平滑过渡,破解多库并存:浙人医基于金仓KFS的医疗信创实战解析
  • 做经营性的网站需要注册什么条件网站构思
  • Answer企业社区实战:零成本搭建技术问答平台,远程协作效率提升300%!
  • “听书”比“看书”更省力?
  • 大连 手机网站案例网站定位方案
  • window安装MYSQL5.5出错:a windows service with the name MYSQL alreadyexists....
  • 珠海做网站报价影响网站排名的因素
  • 6.1.2.2 大数据方法论与实践指南-离线任务SQL 任务开发规范
  • Java 大视界 -- Java 大数据在智能交通高速公路收费系统优化与通行效率提升实战(429)
  • 网站可以做怀孕单吗平面设计图数字标识
  • 图神经网络入门:手写一个 VanillaGNN-从邻接矩阵理解图神经网络的消息传递
  • 网站模版带后台酒类招商网站大全
  • 营销型网站创建网页制作三剑客通常指
  • 【笔试真题】- 电信-2025.10.11
  • 云渲染与传统渲染:核心差异与适用场景分析
  • 什么是流程监控?如何构建跨系统BPM的实时监控体系?
  • 直通滤波....
  • eclipse做网站代码惠州市
  • 零基础新手小白快速了解掌握服务集群与自动化运维(十五)Redis模块-Redis主从复制