我们如何使用 Elasticsearch 实现自定义日志记录机制?我们自定义审计机制的方式
微信搜索关注《Java学研大本营》,加入读者群,分享更多精彩
作为主页和推荐团队,由于我们有一个后台门户,我们最近一直在研究审计日志机制,并希望保留进行更改的人员的记录。
我们想分享我们的故事并告诉您我们是如何做到的。
为此,我们使用 Elasticsearch 实现了自定义日志记录机制。实际上,我们已经有了一个使用 Fluentbit&Elasticsearch 和 Kibana 的集中式日志记录机制,但我们希望我们的自定义日志记录机制只存储我们的审计日志,而不是其他任何东西。
如果您正在寻找如何使用 Elasticsearch 创建集中式日志记录,您可以参考这篇文章。
https://medium.com/trendyol-tech/elasticsearch-logging-setup-7-x-7c1d15e774cf
最初的想法
首先,我们考虑过创建一个带有写入方法的 ElasticSearchClient,这将帮助我们编写自定义日志消息,例如:
AUDIT: User fatih.yilmaz has changed this widget with Id: 123, data: {...}
并在这样的代码中使用它:
private WidgetDTO dataManipulativeMethod(WidgetRequest request) {
// business logic
if (request.isWidgetChangeRequest()) {
jestClient.execute(new Update.Builder(changesThatFatihMadeObject)
.index(auditIndex)
.id(request.getId())
.build());
}
// business logic
}
当然,我们可以为此编写一个简单的包装器,但最终,我们会做到这一点,以便业务代码与我们不希望拥有的基础设施代码紧密耦合。
所以我们寻找更方便的替代方案并继续这样做。
执行
在为 Trendyol 切换到集中式 Elasticsearch Logging 之前,我们一直在使用 Graylog,因为我们熟悉 logback appender,这就是我们想到使用 appender 的原因。然后我们开始研究图书馆以实现这种行为。
我们从internetitem找到了这个库,你也可以从这里检查可用的配置。
https://github.com/internetitem/logback-elasticsearch-appender
这是我们的配置:
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="com.trendyol.loremipsumapi.configuration
.logger.AuditExcludedLogFilter"/> <!-- important -->
</appender>
<appender name="ELASTIC" class="com.internetitem.logback
.elasticsearch.ElasticsearchAppender">
<url>http://lorem-ipsum-dolor-elastic.trendyol.com/_bulk</url>
<index>widget-audit-%date{yyyy-MM-dd}</index>
<errorLoggerName>widget-audit-error-logger</errorLoggerName>
<properties>
<property>
<name>level</name>
<value>%level</value>
</property>
<property>
<name>logger-name</name>
<value>%logger</value>
</property>
<property>
<name>user-email</name>
<value>%X{useremail}</value>
</property>
</properties>
<headers>
<header>
<name>Content-Type</name>
<value>application/json</value>
</header>
</headers>
<filter class="com.trendyol.loremipsumapi.configuration
.logger.AuditLogFilter"/> <!-- important -->
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ELASTIC"/>
</root>
为了将用户电子邮件数据放入我们的 appender,我们得到了这样的用户数据:
public class HttpRequestInterceptor implements AsyncHandlerInterceptor {
private static final String USER_EMAIL = "useremail";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
User currentUser = request.getUser();
String currentUserEmail = currentUser.getEmail();
MDC.put(X_USER_EMAIL, currentUserEmail);
return true;
}
}
我们的日志附加程序正在从其 ThreadLocal 获取用户电子邮件的数据, 对于这种行为,我们需要将数据放入 MDC。
有关 ThreadLocal 和 MDC 的更多详细信息,您可以查看这篇文章。
https://www.baeldung.com/mdc-in-log4j-2-logback
关注点分离
我们在 appender 中标记了AuditExcludedLogFilter和AuditLogFilter ,这是因为:
我们想像以前一样继续编写我们的应用程序日志,但只有一个小的变化:从 我们的中央日志 系统中丢弃我们的审计日志,并将它们仅放入我们的自定义 Elastic 中。
public class AuditExcludedLogFilter extends
AbstractMatcherFilter<ILoggingEvent> {
@Override
public FilterReply decide(ILoggingEvent event) {
if (!isStarted() || StringUtils.isBlank(event.getMessage())) {
return FilterReply.NEUTRAL;
}
return event.getMessage().startsWith(AUDIT_LOG_PREFIX) ?
FilterReply.DENY : FilterReply.ACCEPT;
}
}
public class AuditLogFilter extends
AbstractMatcherFilter<ILoggingEvent> {
@Override
public FilterReply decide(ILoggingEvent event) {
if (!isStarted() || StringUtils.isBlank(event.getMessage()) ||
!event.getMessage().startsWith(AUDIT_LOG_PREFIX)) {
return FilterReply.DENY;
}
return FilterReply.ACCEPT;
}
}
为了能够自定义我们的过滤,我们需要扩展AbstractMatcherFilter,如上所示。
Elasticsearch 配置
我们想设置一个 60 天的保留策略,为了实现这个行为,我们在我们的 appender 中设置当前日期作为后缀。
手动执行:
PUT _ilm/policy/widget-audit-retention-policy {
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"set_priority": {
"priority": 100
}
}
},
"delete": {
"min_age": "60d",
"actions": {
"delete": {
"delete_searchable_snapshot": true
}
}
}
}
}
}
我们需要创建一个索引模板主要是因为以下两个原因:
-
我们的索引在逻辑上是一个整体的一部分。后缀包含日期(用于搜索目的),但总体而言它们是“widget-audit”的一部分。
-
保留策略只能应用于索引模板。
索引模板预览:
![](https://files.mdnice.com/user/30879/fff8c9f6-47db-4951-8f25-23f4967337f4.png)
您可以创建一个索引模式,以便能够根据需要搜索您的日志。
由于我们在索引模板中使用别名,我们的索引模式只是一个简单的定义。
如果我们没有将别名连接到索引模板,我们将需要在索引模式的末尾放置一个通配符。
行动中的搜索
左操作数:
感谢您的阅读!❤️
推荐书单
《项目驱动零起点学Java》
《项目驱动零起点学Java》共分 13 章,围绕 6 个项目和 258 个代码示例,分别介绍了走进Java 的世界、变量与数据类型、运算符、流程控制、方法、数组、面向对象、异常、常用类、集合、I/O流、多线程、网络编程相关内容。《项目驱动零起点学Java》总结了马士兵老师从事Java培训十余年来经受了市场检验的教研成果,通过6 个项目以及每章的示例和习题,可以帮助读者快速掌握Java 编程的语法以及算法实现。扫描每章提供的二维码可观看相应章节内容的视频讲解。
《项目驱动零起点学Java》贯穿6个完整项目,经过作者多年教学经验提炼而得,项目从小到大、从短到长,可以让读者在练习项目的过程中,快速掌握一系列知识点。
马士兵,马士兵教育创始人,毕业于清华大学,著名IT讲师,所讲课程广受欢迎,学生遍布全球大厂,擅长用简单的语言讲授复杂的问题,擅长项目驱动知识的综合学习。马士兵教育获得在线教育“名课堂”奖、“最受欢迎机构”奖。
赵珊珊,从事多年一线开发,曾为国税、地税税务系统工作。拥有7年一线教学经验,多年线上、线下教育的积累沉淀,培养学员数万名,讲解细致,脉络清晰。
精彩回顾
微信搜索关注《Java学研大本营》
访问【IT今日热榜】,发现每日技术热点