当前位置:   article > 正文

Springboot2 Log4j2 Es7.8.1 Kibana7.8.1 做用户行为分析_es 可以分析用户行为路径吗

es 可以分析用户行为路径吗

目录

一.搭建ES和Kibana集群

二.Springboot2代码编写和依赖配置

1.pom文件依赖

2.编写ElasticSearchUtils

3.编写ElasticSearchConfig

三.通过Log4j2输出行为日志

1.编写行为日志接口

2.实现行为日志接口(之前的项目是插入数据库的这个实现用来插入ES)

3.创建ConsoleLogAppender用于Log4j2输出配置

四.编写AOP插入行为日志

1.编写日志类型枚举(老大之前写的,之前的日志是插入到数据库的.copy一下哈哈)

2.自定义Es日志注解

3.编写AOP切面具体业务

五.log4j2.xml配置文件增加ConsoleLogAppender

六.使用

七.创建Es索引和字段

八.创建kibana索引

九.在kibana中创建仪表盘等(前提是ES有了一定量数据哈)

仿照kibana系统数据创建自己的仪表盘等


一.搭建ES和Kibana集群

https://blog.csdn.net/qq_40174211/article/details/113255162

按照文章中的步骤  只搭建ES和Kibana集群即可

二.Springboot2代码编写和依赖配置

1.pom文件依赖

  1. <!--ElasticSearch 依赖-->
  2. <dependency>
  3. <groupId>org.elasticsearch.client</groupId>
  4. <artifactId>elasticsearch-rest-high-level-client</artifactId>
  5. <version>7.8.1</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.elasticsearch</groupId>
  9. <artifactId>elasticsearch</artifactId>
  10. <version>7.8.1</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.elasticsearch.client</groupId>
  14. <artifactId>elasticsearch-rest-client</artifactId>
  15. <version>7.8.1</version>
  16. </dependency>

2.编写ElasticSearchUtils

  1. package com.sinosoft.springbootplus.util;
  2. import cn.hutool.core.util.IdUtil;
  3. import com.alibaba.fastjson.JSONObject;
  4. import org.apache.commons.collections.CollectionUtils;
  5. import org.apache.commons.collections.MapUtils;
  6. import org.apache.commons.lang3.StringUtils;
  7. import org.elasticsearch.action.bulk.BulkRequest;
  8. import org.elasticsearch.action.get.GetRequest;
  9. import org.elasticsearch.action.get.GetResponse;
  10. import org.elasticsearch.action.index.IndexRequest;
  11. import org.elasticsearch.action.index.IndexResponse;
  12. import org.elasticsearch.action.search.SearchRequest;
  13. import org.elasticsearch.action.search.SearchResponse;
  14. import org.elasticsearch.action.update.UpdateRequest;
  15. import org.elasticsearch.action.update.UpdateResponse;
  16. import org.elasticsearch.client.RequestOptions;
  17. import org.elasticsearch.client.RestHighLevelClient;
  18. import org.elasticsearch.common.unit.TimeValue;
  19. import org.elasticsearch.index.query.*;
  20. import org.elasticsearch.index.reindex.BulkByScrollResponse;
  21. import org.elasticsearch.index.reindex.DeleteByQueryRequest;
  22. import org.elasticsearch.search.SearchHit;
  23. import org.elasticsearch.search.builder.SearchSourceBuilder;
  24. import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
  25. import org.springframework.context.annotation.Configuration;
  26. import javax.annotation.Resource;
  27. import java.io.IOException;
  28. import java.util.ArrayList;
  29. import java.util.List;
  30. import java.util.Map;
  31. /**
  32. * @author fan
  33. */
  34. @Configuration
  35. public class ElasticSearchUtils {
  36. @Resource
  37. private RestHighLevelClient restHighLevelClient;
  38. // ================================新增========================================
  39. /**
  40. *  
  41. *  * @Description: 更新文档 
  42. *  * @param [index 索引, id 索引id, map 待更新内容] 
  43. *  * @return java.lang.String 
  44. *  * @author fan 
  45. *  * @date 2021/4/28 14:58  
  46. *  
  47. **/
  48. public String updateDoc(String index, String id, Map<String, Object> map) throws IOException {
  49. UpdateRequest updateRequest = new UpdateRequest(index, id);
  50. updateRequest.doc(map);
  51. UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
  52. return updateResponse.getResult().getLowercase();
  53. }
  54. /**
  55. *  
  56. *  * @Description: 新增文档 
  57. *  * @param [index 索引, id doc的 _id, map 待插入内容]  
  58. *  * @return java.lang.String 
  59. *  * @author fan 
  60. *  * @date 2021/4/28 14:59  
  61. *  
  62. **/
  63. public String addDoc(String index, Map<String, Object> map) throws IOException {
  64. IndexRequest indexRequest = new IndexRequest(index);
  65. indexRequest.source(map);
  66. indexRequest.id(IdUtil.simpleUUID());
  67. IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
  68. return indexResponse.getResult().getLowercase();
  69. }
  70. /** 
  71.  * @Description: 批量插入文档 
  72.  * @param [index 索引, id 索引id, list 待插入内容] 
  73.  * @return void 
  74.  * @author fan 
  75.  * @date 2021/4/28 14:59  
  76.  **/
  77. public void bulkInsertDoc(String index, String id, List<Map<String, Object>> list) throws IOException {
  78. if (StringUtils.isBlank(index) || StringUtils.isBlank(id) || CollectionUtils.isEmpty(list)) {
  79. return;
  80. }
  81. BulkRequest bulkRequest = new BulkRequest();
  82. list.forEach(map -> {
  83. IndexRequest indexRequest = new IndexRequest(index);
  84. indexRequest.source(map);
  85. indexRequest.id(map.get(id).toString());
  86. bulkRequest.add(indexRequest);
  87. });
  88. restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
  89. }
  90. // ======================删除======================================
  91. /** 
  92.  * @Description: 根据文档Id删除数据 
  93.  * @param [index 索引, id 文档id] 
  94.  * @return long 
  95.  * @author fan 
  96.  * @date 2021/4/29 14:19  
  97.  **/
  98. public long deleteDocById(String index, String id) throws IOException {
  99. if (StringUtils.isBlank(index) || StringUtils.isBlank(id)) {
  100. return -1;
  101. }
  102. return deleteDocByValue(index, "_id", id);
  103. }
  104. /** 
  105.  * @Description: 根据字段值 删除数据 
  106.  * @param [index 索引, fieldName 映射名, value 文档值] 
  107.  * @return long 
  108.  * @author fan 
  109.  * @date 2021/4/29 16:36  
  110.  **/
  111. public long deleteDocByValue(String index, String fieldName, String value) throws IOException {
  112. if (StringUtils.isBlank(index) || StringUtils.isBlank(fieldName) || StringUtils.isBlank(value)) {
  113. return -1;
  114. }
  115. DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(index);
  116. //设置版本冲突时继续执行
  117. deleteByQueryRequest.setConflicts("proceed");
  118. // 设置查询条件
  119. deleteByQueryRequest.setQuery(new TermQueryBuilder(fieldName, value));
  120. deleteByQueryRequest.setMaxDocs(60);
  121. deleteByQueryRequest.setBatchSize(1000);
  122. // 并行
  123. deleteByQueryRequest.setSlices(2);
  124. // 使用滚动参数来控制“搜索上下文”存活的时间
  125. deleteByQueryRequest.setScroll(TimeValue.timeValueMinutes(10));
  126. // 超时
  127. deleteByQueryRequest.setTimeout(TimeValue.timeValueMinutes(2));
  128. // 刷新索引
  129. deleteByQueryRequest.setRefresh(true);
  130. BulkByScrollResponse response = restHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
  131. return response.getStatus().getUpdated();
  132. }
  133. // ====================查询======================================
  134. /** 
  135.  * @Description: 多字段匹配查询 
  136.  * @param [index 索引, fieldMap 字段map] 
  137.  * @return java.lang.String 
  138.  * @author fan 
  139.  * @date 2021/4/29 16:39  
  140.  **/
  141. public String getByMultiFieldNames(String index, Map<String, Object> fieldMap) throws IOException {
  142. if (StringUtils.isBlank(index) || MapUtils.isEmpty(fieldMap)) {
  143. return null;
  144. }
  145. SearchRequest searchRequest = new SearchRequest(index);
  146. SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
  147. BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
  148. //循环传入搜索参数
  149. fieldMap.forEach((key, value) -> {
  150. boolQueryBuilder.must(QueryBuilders.termQuery(key, value));
  151. });
  152. sourceBuilder.query(boolQueryBuilder);
  153. searchRequest.source(sourceBuilder);
  154. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
  155. return handleSearchResponse2Json(searchResponse);
  156. }
  157. /** 
  158.  * @Description: 根据文档id 查询 
  159.  * @param [index 索引, id 文档id] 
  160.  * @return java.lang.String 
  161.  * @author fan 
  162.  * @date 2021/4/29 16:39  
  163.  **/
  164. public String getByIndexAndDocId(String index, String id) throws IOException {
  165. if (StringUtils.isBlank(index) || StringUtils.isBlank(id)) {
  166. return null;
  167. }
  168. GetRequest getRequest = new GetRequest(index, id);
  169. GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
  170. return getResponse.getSourceAsString();
  171. }
  172. /** 
  173.  * @Description: 根据索引分页查询 
  174.  * @param [index 索引, pageNum, pageSize] 
  175.  * @return java.lang.String 
  176.  * @author fan 
  177.  * @date 2021/4/29 16:41  
  178.  **/
  179. public String getByDoc(String index, int pageNum, int pageSize) throws IOException {
  180. if (StringUtils.isBlank(index)) {
  181. return null;
  182. }
  183. // 搜索请求
  184. SearchRequest searchRequest = new SearchRequest(index);
  185. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  186. // 分页
  187. if (pageNum >= 0 && pageSize >= 0) {
  188. searchSourceBuilder.from(pageSize * (pageNum - 1));
  189. searchSourceBuilder.size(pageSize);
  190. } else {
  191. // 如果不传分页参数 默认给20条数据
  192. searchSourceBuilder.from(0);
  193. searchSourceBuilder.size(19);
  194. }
  195. // 查询条件
  196. MatchAllQueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
  197. // 传入搜索条件
  198. searchSourceBuilder.query(queryBuilder);
  199. searchRequest.source(searchSourceBuilder);
  200. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
  201. return handleSearchResponse2Json(searchResponse);
  202. }
  203. /** 
  204.  * @Description: 根据映射查询 
  205.  * @param [index 索引, fileName 映射, value 映射值] 
  206.  * @return java.lang.String 
  207.  * @author fan 
  208.  * @date 2021/4/29 16:44  
  209.  **/
  210. public String getByFieldName(String index, String fileName, String value) throws IOException {
  211. if (StringUtils.isBlank(index) || StringUtils.isBlank(fileName) || StringUtils.isBlank(value)) {
  212. return null;
  213. }
  214. SearchRequest searchRequest = new SearchRequest(index);
  215. SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
  216. QueryBuilder queryBuilder = QueryBuilders.matchQuery(fileName, value);
  217. sourceBuilder.query(queryBuilder);
  218. searchRequest.source(sourceBuilder);
  219. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
  220. return handleSearchResponse2Json(searchResponse);
  221. }
  222. /** 
  223.  * @Description: 在多个映射中查找一个值 
  224.  * @param [index 索引, value 要查找的值, fieldNames 映射] 
  225.  * @return java.lang.String 
  226.  * @author fan 
  227.  * @date 2021/4/29 16:45  
  228.  **/
  229. public String multiFieldsMatchOneValue(String index, String value, String... fieldNames) throws IOException {
  230. if (StringUtils.isBlank(index) || StringUtils.isBlank(value) || null == fieldNames) {
  231. return null;
  232. }
  233. SearchRequest searchRequest = new SearchRequest(index);
  234. SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
  235. QueryBuilder queryBuilder = QueryBuilders.multiMatchQuery(value, fieldNames);
  236. sourceBuilder.query(queryBuilder);
  237. searchRequest.source(sourceBuilder);
  238. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
  239. return handleSearchResponse2Json(searchResponse);
  240. }
  241. /** 
  242.  * @Description: 字段模糊匹配 
  243.  * @param [index 索引, fieldName 映射, wildValueStr 模糊值] 
  244.  * @return java.lang.String 
  245.  * @author fan 
  246.  * @date 2021/4/29 16:48  
  247.  **/
  248. public String getByFieldNameWild(String index, String fieldName, String wildValueStr) throws IOException {
  249. if (StringUtils.isBlank(index) || StringUtils.isBlank(fieldName) || StringUtils.isBlank(wildValueStr)) {
  250. return null;
  251. }
  252. SearchRequest searchRequest = new SearchRequest(index);
  253. SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
  254. WildcardQueryBuilder queryBuilder = QueryBuilders.wildcardQuery(fieldName, wildValueStr);
  255. sourceBuilder.query(queryBuilder);
  256. searchRequest.source(sourceBuilder);
  257. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
  258. return handleSearchResponse2Json(searchResponse);
  259. }
  260. /** 
  261.  * @Description:  范围查询
  262.  * @param [index 索引, fieldName 映射, start 开始, includeUpperStart 是否包含开始, end 结束, includeUpperEnd 是否包含结束] 
  263.  * @return java.lang.String 
  264.  * @author fan 
  265.  * @date 2021/4/29 16:48  
  266.  **/
  267. public String getByFieldRange(String index, String fieldName, String start, boolean includeUpperStart, String end, boolean includeUpperEnd) throws IOException {
  268. if (StringUtils.isBlank(index) || StringUtils.isBlank(fieldName)) {
  269. return null;
  270. }
  271. SearchRequest searchRequest = new SearchRequest(index);
  272. RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(fieldName);
  273. rangeQueryBuilder.from(start, includeUpperStart);
  274. rangeQueryBuilder.to(end, includeUpperEnd);
  275. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  276. searchSourceBuilder.query(rangeQueryBuilder);
  277. searchRequest.source(searchSourceBuilder);
  278. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
  279. return handleSearchResponse2Json(searchResponse);
  280. }
  281. /** 
  282.  * @Description: 根据文档id判断文档是否存在 
  283.  * @param [index 索引, id 文档id] 
  284.  * @return boolean 
  285.  * @author fan 
  286.  * @date 2021/4/29 16:49  
  287.  **/
  288. public boolean isExists(String index, String id) throws IOException {
  289. if (StringUtils.isBlank(index) || StringUtils.isBlank(id)) {
  290. return Boolean.FALSE;
  291. }
  292. GetRequest getRequest = new GetRequest(index, id);
  293. //禁用fetching _source
  294. getRequest.fetchSourceContext(new FetchSourceContext(false));
  295. // 关闭 存储字段
  296. getRequest.storedFields("_none_");
  297. return restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
  298. }
  299. /** 
  300.  * @Description: 将SearchResponse 取出数据 转换成json 
  301.  * @param [searchResponse 查询结果] 
  302.  * @return java.lang.String 
  303.  * @author fan 
  304.  * @date 2021/4/29 16:50  
  305.  **/
  306. private String handleSearchResponse2Json(SearchResponse searchResponse) {
  307. SearchHit[] hits = searchResponse.getHits().getHits();
  308. if (hits.length == 0) {
  309. return null;
  310. }
  311. List<Map<String, Object>> dataList = new ArrayList<>(hits.length);
  312. for (int i = 0; i < hits.length; i++) {
  313. dataList.add(hits[i].getSourceAsMap());
  314. }
  315. return JSONObject.toJSONString(dataList);
  316. }
  317. }

3.编写ElasticSearchConfig

  1. package com.sinosoft.springbootplus.config;
  2. import org.apache.http.HttpHost;
  3. import org.elasticsearch.client.RestClient;
  4. import org.elasticsearch.client.RestClientBuilder;
  5. import org.elasticsearch.client.RestHighLevelClient;
  6. import org.springframework.beans.factory.annotation.Value;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import javax.annotation.PostConstruct;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. /**
  13. * @Description: ElasticSearch 配置类
  14. * @Author: karma
  15. * @Date: 2019/11/25 3:11 下午
  16. */
  17. @Configuration
  18. public class ElasticSearchConfig {
  19. /**
  20. * 集群地址,多个用,隔开
  21. **/
  22. @Value("${elasticSearch.hosts}")
  23. private String hosts;
  24. /**
  25. * 端口号
  26. **/
  27. @Value("${elasticSearch.port}")
  28. private int port;
  29. /**
  30. * 使用的协议
  31. **/
  32. @Value("${elasticSearch.schema}")
  33. private String schema;
  34. /**
  35. * 连接超时时间
  36. **/
  37. @Value("${elasticSearch.client.connectTimeOut}")
  38. private int connectTimeOut;
  39. /**
  40. * 连接超时时间
  41. **/
  42. @Value("${elasticSearch.client.socketTimeOut}")
  43. private int socketTimeOut;
  44. /**
  45. * 获取连接的超时时间
  46. **/
  47. @Value("${elasticSearch.client.connectionRequestTimeOut}")
  48. private static int connectionRequestTimeOut;
  49. /**
  50. * 最大连接数
  51. **/
  52. @Value("${elasticSearch.client.maxConnectNum}")
  53. private static int maxConnectNum;
  54. /**
  55. * 最大路由连接数
  56. **/
  57. @Value("${elasticSearch.client.maxConnectPerRoute}")
  58. private static int maxConnectPerRoute;
  59. private List<HttpHost> hostList = new ArrayList<>();
  60. @PostConstruct
  61. private void init(){
  62. hostList = new ArrayList<>();
  63. String[] hostArray = hosts.split(",");
  64. for (String host : hostArray) {
  65. hostList.add(new HttpHost(host, port, schema));
  66. }
  67. }
  68. @Bean
  69. public RestHighLevelClient getRestHighLevelClient() {
  70. RestClientBuilder builder = RestClient.builder(hostList.toArray(new HttpHost[0]));
  71. // 异步httpclient连接延时配置
  72. builder.setRequestConfigCallback(requestConfigBuilder -> {
  73. requestConfigBuilder.setConnectTimeout(connectTimeOut);
  74. requestConfigBuilder.setSocketTimeout(socketTimeOut);
  75. requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeOut);
  76. return requestConfigBuilder;
  77. });
  78. // 异步httpclient连接数配置
  79. builder.setHttpClientConfigCallback(httpClientBuilder -> {
  80. httpClientBuilder.setMaxConnTotal(maxConnectNum);
  81. httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
  82. return httpClientBuilder;
  83. });
  84. RestHighLevelClient client = new RestHighLevelClient(builder);
  85. return client;
  86. }
  87. }

三.通过Log4j2输出行为日志

1.编写行为日志接口

  1. package com.sinosoft.springbootplus.log4j2.service;
  2. import java.util.List;
  3. /**
  4. * <pre>
  5. * 系统行为日志
  6. * </pre>
  7. *
  8. * @author 波哥
  9. * @since 2020-02-23
  10. */
  11. public interface BaseLogService {
  12. /**
  13. * 单条保存行为日志
  14. */
  15. void save(String loginfoJson);
  16. /**
  17. * 批量保存行为日志
  18. */
  19. void saveBatch(List<String> loginfoJsons);
  20. }

2.实现行为日志接口(之前的项目是插入数据库的这个实现用来插入ES)

  1. package com.sinosoft.springbootplus.impl;
  2. import com.sinosoft.springbootplus.log4j2.service.BaseLogService;
  3. import com.sinosoft.springbootplus.entity.EsSaveInfo;
  4. import com.sinosoft.springbootplus.util.ElasticSearchUtils;
  5. import com.sinosoft.springbootplus.util.Jackson;
  6. import com.sinosoft.springbootplus.util.MapperUtils;
  7. import lombok.extern.slf4j.Slf4j;
  8. import org.springframework.stereotype.Component;
  9. import java.util.List;
  10. /**
  11. * @author fan
  12. * @Date 2021/5/14 10:36 <pre>
  13. *
  14. * </per>
  15. */
  16. @Slf4j
  17. @Component
  18. public class EsLogServiceImpl implements BaseLogService {
  19. ElasticSearchUtils elasticSearchUtils;
  20. public EsLogServiceImpl(ElasticSearchUtils elasticSearchUtils) {
  21. this.elasticSearchUtils = elasticSearchUtils;
  22. }
  23. @Override
  24. public void save(String logInfoJson) {
  25. log.info(Jackson.toJsonString(logInfoJson));
  26. try {
  27. saveSingle(logInfoJson);
  28. } catch (Exception e) {
  29. log.error("ES保存操作异常,【{}】",e.getMessage(),e);
  30. }
  31. }
  32. private void saveSingle(String logParamJson) throws Exception {
  33. EsSaveInfo esSaveInfo = MapperUtils.json2pojo(logParamJson, EsSaveInfo.class);
  34. elasticSearchUtils.addDoc(esSaveInfo.getIndex(), esSaveInfo.getEsSaveDetail());
  35. }
  36. @Override
  37. public void saveBatch(List<String> logInfoJsons) {
  38. log.info(Jackson.toJsonString(logInfoJsons));
  39. try {
  40. for (String logParamJson : logInfoJsons) {
  41. saveSingle(logParamJson);
  42. }
  43. } catch (Exception e) {
  44. log.error("ES批量保存操作异常,【{}】",e.getMessage(),e);
  45. }
  46. }
  47. }

3.创建ConsoleLogAppender用于Log4j2输出配置

  1. package com.sinosoft.springbootplus.log4j2;
  2. import cn.hutool.json.JSONUtil;
  3. import com.sinosoft.springbootplus.log4j2.service.BaseLogService;
  4. import com.sinosoft.springbootplus.util.SpringContextUtil;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.apache.logging.log4j.core.Filter;
  7. import org.apache.logging.log4j.core.Layout;
  8. import org.apache.logging.log4j.core.LogEvent;
  9. import org.apache.logging.log4j.core.appender.AbstractAppender;
  10. import org.apache.logging.log4j.core.config.Property;
  11. import org.apache.logging.log4j.core.config.plugins.Plugin;
  12. import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
  13. import org.apache.logging.log4j.core.config.plugins.PluginElement;
  14. import org.apache.logging.log4j.core.config.plugins.PluginFactory;
  15. import org.apache.logging.log4j.core.layout.PatternLayout;
  16. import java.io.Serializable;
  17. import java.util.ArrayList;
  18. import java.util.List;
  19. /**
  20. * @author 波哥
  21. * @Title:
  22. * @Description: 添加类的描述
  23. * @date 2020/2/22 18:05
  24. */
  25. @Slf4j
  26. @Plugin(name = "ConsoleLogAppender", category = "Core", elementType = "appender", printObject = true)
  27. public class ConsoleLogAppender extends AbstractAppender {
  28. /**
  29. * 日志出现延迟时间大于1秒时开启批量入库
  30. */
  31. private static final int DELAY_TIME = 1000;
  32. /**
  33. * 出现延迟后每次批量的插入的数量
  34. */
  35. private static final int BATCH_SIZE = 20;
  36. /**
  37. * 日志服务用于保存日志
  38. */
  39. private BaseLogService baseLogService;
  40. private List<String> list = new ArrayList<>();
  41. private ConsoleLogAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions) {
  42. super(name, filter, layout, ignoreExceptions, Property.EMPTY_ARRAY);
  43. }
  44. @Override
  45. public void append(LogEvent logEvent) {
  46. if (baseLogService == null) {
  47. baseLogService = SpringContextUtil.getBean(BaseLogService.class);
  48. }
  49. int size = list.size();
  50. long curr = System.currentTimeMillis();
  51. if ((curr - logEvent.getTimeMillis()) > DELAY_TIME && size < BATCH_SIZE) {
  52. list.add(logEvent.getMessage().getFormattedMessage());
  53. } else if (size == 0) {
  54. try {
  55. //baseLogService.save(JSONUtil.toBean(logEvent.getMessage().getFormattedMessage(), SysLog.class));
  56. baseLogService.save(logEvent.getMessage().getFormattedMessage());
  57. } catch (Exception e) {
  58. log.error("保存操作异常,【{}】,未保存日志【{}】",e.getMessage(),logEvent.getMessage().getFormattedMessage(),e);
  59. }
  60. } else {
  61. list.add(logEvent.getMessage().getFormattedMessage());
  62. try {
  63. baseLogService.saveBatch(new ArrayList<>(list));
  64. } catch (Exception e) {
  65. log.error("保存操作异常,【{}】,未保存日志【{}】",e.getMessage(),JSONUtil.toJsonStr(list),e);
  66. }
  67. list.clear();
  68. }
  69. }
  70. @PluginFactory
  71. public static ConsoleLogAppender createAppender(@PluginAttribute(value = "name", defaultString = "consoleLog") String name,
  72. @PluginElement("Filter") final Filter filter,
  73. @PluginElement("Layout") Layout<? extends Serializable> layout,
  74. @PluginAttribute(value = "ignoreExceptions") boolean ignoreExceptions
  75. ) {
  76. if (layout == null) {
  77. layout = PatternLayout.createDefaultLayout();
  78. }
  79. return new ConsoleLogAppender(name, filter, layout, ignoreExceptions);
  80. }
  81. }

四.编写AOP插入行为日志

1.编写日志类型枚举(老大之前写的,之前的日志是插入到数据库的.copy一下哈哈)

  1. package com.sinosoft.springbootplus.common.enums;
  2. /**
  3. * 枚举类型父接口
  4. *
  5. * @author 波哥
  6. */
  7. public interface BaseEnum {
  8. /**
  9. * 获取枚举索引
  10. *
  11. * @return
  12. */
  13. Integer getCode();
  14. /**
  15. * 获取描述
  16. *
  17. * @return
  18. */
  19. String getDesc();
  20. }
  1. package com.sinosoft.springbootplus.log4j2.enums;
  2. import com.sinosoft.springbootplus.common.enums.BaseEnum;
  3. /**
  4. * 日志类型枚举
  5. * @author 波哥
  6. * @date 2020/2/23 11:40
  7. */
  8. public enum LogTypeEnum implements BaseEnum {
  9. SEARCH(1, "查询"),
  10. UPDATE(2, "更新"),
  11. DELETE(3,"删除"),
  12. INSERT(4,"新增"),
  13. UPLOAD(5,"上传"),
  14. DOWNLOAD(6,"下载");
  15. private Integer code;
  16. private String desc;
  17. LogTypeEnum(Integer code, String desc) {
  18. this.code = code;
  19. this.desc = desc;
  20. }
  21. @Override
  22. public Integer getCode() {
  23. return this.code;
  24. }
  25. @Override
  26. public String getDesc() {
  27. return this.desc;
  28. }
  29. }

2.自定义Es日志注解

  1. import com.sinosoft.springbootplus.log4j2.enums.LogTypeEnum;
  2. import java.lang.annotation.*;
  3. /**
  4. *
  5. * ES日志注解
  6. * @author fan
  7. */
  8. @Target(ElementType.METHOD)
  9. @Retention(RetentionPolicy.RUNTIME)
  10. @Documented
  11. public @interface SysLogEs {
  12. String value() default "";
  13. LogTypeEnum type() default LogTypeEnum.SEARCH;
  14. String index() default "";
  15. }

3.编写AOP切面具体业务

  1. /*
  2. * Copyright 2019-2029 geekidea(https://github.com/geekidea)
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.sinosoft.springbootplus.log4j2.aop;
  17. import cn.hutool.json.JSONUtil;
  18. import com.alibaba.fastjson.JSONObject;
  19. import com.sinosoft.springbootplus.core.context.RequestContext;
  20. import com.sinosoft.springbootplus.log4j2.entity.SysLog;
  21. import com.sinosoft.springbootplus.log4j2.entity.SysLogEs;
  22. import com.sinosoft.springbootplus.util.ClientInfoUtil;
  23. import com.sinosoft.springbootplus.util.IpUtil;
  24. import com.sinosoft.springbootplus.util.vo.ClientInfo;
  25. import lombok.extern.slf4j.Slf4j;
  26. import org.aspectj.lang.ProceedingJoinPoint;
  27. import org.aspectj.lang.Signature;
  28. import org.aspectj.lang.annotation.Around;
  29. import org.aspectj.lang.annotation.Aspect;
  30. import org.aspectj.lang.annotation.Pointcut;
  31. import org.aspectj.lang.reflect.MethodSignature;
  32. import org.slf4j.Logger;
  33. import org.slf4j.LoggerFactory;
  34. import org.springframework.stereotype.Component;
  35. import org.springframework.web.bind.annotation.RequestBody;
  36. import org.springframework.web.context.request.RequestContextHolder;
  37. import org.springframework.web.context.request.ServletRequestAttributes;
  38. import org.springframework.web.multipart.MultipartFile;
  39. import org.springframework.web.servlet.ModelAndView;
  40. import javax.servlet.http.HttpServletRequest;
  41. import javax.servlet.http.HttpServletResponse;
  42. import java.lang.annotation.Annotation;
  43. import java.lang.reflect.Method;
  44. import java.util.ArrayList;
  45. import java.util.List;
  46. import java.util.Map;
  47. /**
  48. * 用于保存请求数据到Es
  49. *
  50. * @author fan
  51. */
  52. @Aspect
  53. @Component
  54. @Slf4j
  55. public class SysLogAopEs {
  56. private static final Logger logLogger = LoggerFactory.getLogger("sinosoft.consoleLog");
  57. /**
  58. * POST请求
  59. **/
  60. private static final String POST = "POST";
  61. @Pointcut("@annotation(com.sinosoft.springbootplus.log4j2.annotation.SysLogEs)")
  62. public void consoleLog() {
  63. }
  64. @Around("consoleLog()")
  65. public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {
  66. SysLogEs sysLogEs = new SysLogEs();
  67. SysLog sysLog = new SysLog();
  68. sysLogEs.setEsSaveMap(sysLog);
  69. // 获取请求相关信息
  70. try {
  71. // 获取当前的HttpServletRequest对象
  72. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  73. if (attributes == null) {
  74. throw new RuntimeException("获取ServletRequestAttributes为空");
  75. }
  76. HttpServletRequest request = attributes.getRequest();
  77. // 获取请求类名和方法名称
  78. Signature signature = joinPoint.getSignature();
  79. // 获取真实的方法对象
  80. MethodSignature methodSignature = (MethodSignature) signature;
  81. Method method = methodSignature.getMethod();
  82. //获取拦截额日志注解
  83. com.sinosoft.springbootplus.log4j2.annotation.SysLogEs sysLogAnnotation = method.getAnnotation(com.sinosoft.springbootplus.log4j2.annotation.SysLogEs.class);
  84. //获取注解中的信息,包括描述
  85. sysLog.setTitle(sysLogAnnotation.value());
  86. //获取操作类型
  87. sysLog.setType(sysLogAnnotation.type().getDesc());
  88. //获取索引
  89. sysLogEs.setIndex(sysLogAnnotation.index());
  90. // 请求全路径
  91. String url = request.getRequestURI();
  92. sysLog.setRequestUri(url);
  93. // IP地址
  94. String ip = IpUtil.getRequestIp();
  95. sysLog.setRemoteAddr(ip);
  96. ClientInfo clientInfo = ClientInfoUtil.get(request);
  97. sysLog.setBrowser(clientInfo.toString());
  98. //设置当前用户
  99. sysLog.setUsername(RequestContext.getLoginUserNickName());
  100. sysLog.setUserId(RequestContext.getLoginUserId());
  101. } catch (Exception e) {
  102. log.error("获取操作日志信息过程异常,【{}】", e.getMessage(), e);
  103. }
  104. long b = System.currentTimeMillis();
  105. // 执行目标方法,获得返回值
  106. try {
  107. Object result = joinPoint.proceed();
  108. long e = System.currentTimeMillis();
  109. sysLog.setUseTime(e - b);
  110. logLogger.info(JSONUtil.toJsonStr(sysLogEs));
  111. return result;
  112. } catch (Exception ep) {
  113. long e = System.currentTimeMillis();
  114. sysLog.setUseTime(e - b);
  115. sysLog.setException(ep.getMessage());
  116. logLogger.info(JSONUtil.toJsonStr(sysLogEs));
  117. throw ep;
  118. }
  119. }
  120. /**
  121. * 获取请求参数JSON字符串
  122. */
  123. private Object getRequestParamJsonString(ProceedingJoinPoint joinPoint, HttpServletRequest request, String requestMethod, boolean isRequestBody) {
  124. Object paramObject;
  125. if (POST.equals(requestMethod) && isRequestBody) {
  126. Object[] args = joinPoint.getArgs();
  127. paramObject = argsArrayToJsonString(args);
  128. } else {
  129. Map<String, String[]> paramsMap = request.getParameterMap();
  130. paramObject = getJsonForParamMap(paramsMap);
  131. }
  132. return paramObject;
  133. }
  134. /**
  135. * 判断控制器方法参数中是否有RequestBody注解
  136. */
  137. private boolean isRequestBody(Annotation[][] annotations) {
  138. boolean isRequestBody = false;
  139. for (Annotation[] annotationArray : annotations) {
  140. for (Annotation annotation : annotationArray) {
  141. if (annotation instanceof RequestBody) {
  142. isRequestBody = true;
  143. break;
  144. }
  145. }
  146. }
  147. return isRequestBody;
  148. }
  149. /**
  150. * 请求参数拼装
  151. */
  152. private Object argsArrayToJsonString(Object[] args) {
  153. if (args == null) {
  154. return null;
  155. }
  156. // 去掉HttpServletRequest和HttpServletResponse
  157. List<Object> realArgs = new ArrayList<>();
  158. for (Object arg : args) {
  159. if (arg instanceof HttpServletRequest) {
  160. continue;
  161. }
  162. if (arg instanceof HttpServletResponse) {
  163. continue;
  164. }
  165. if (arg instanceof MultipartFile) {
  166. continue;
  167. }
  168. if (arg instanceof ModelAndView) {
  169. continue;
  170. }
  171. realArgs.add(arg);
  172. }
  173. if (realArgs.size() == 1) {
  174. return realArgs.get(0);
  175. } else {
  176. return realArgs;
  177. }
  178. }
  179. /**
  180. * 获取参数Map的JSON字符串
  181. */
  182. private JSONObject getJsonForParamMap(Map<String, String[]> paramsMap) {
  183. if (paramsMap == null || paramsMap.size() == 0) {
  184. return null;
  185. }
  186. JSONObject jsonObject = new JSONObject();
  187. for (Map.Entry<String, String[]> kv : paramsMap.entrySet()) {
  188. String key = kv.getKey();
  189. String[] values = kv.getValue();
  190. // 没有值
  191. if (values == null) {
  192. jsonObject.put(key, null);
  193. // 一个值
  194. } else if (values.length == 1) {
  195. jsonObject.put(key, values[0]);
  196. // 多个值
  197. } else {
  198. jsonObject.put(key, values);
  199. }
  200. }
  201. return jsonObject;
  202. }
  203. }

五.log4j2.xml配置文件增加ConsoleLogAppender

  1. <Appenders>
  2. <ConsoleLogAppender >
  3. <PatternLayout pattern="%msg" charset="${CHARSET}"/>
  4. </ConsoleLogAppender>
  5. </Appenders>
  6. <Loggers>
  7. <AsyncLogger name="sinosoft.consoleLog" additivity="false">
  8. <AppenderRef ref="consoleLog" />
  9. </AsyncLogger>
  10. </Loggers>

六.使用

  1. package com.sinosoft.springbootplus;
  2. import com.sinosoft.springbootplus.log4j2.annotation.SysLogEs;
  3. import com.sinosoft.springbootplus.log4j2.enums.LogTypeEnum;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. /**
  9. * @Auther: fan
  10. * @Date 2021/5/20 16:11 <pre>
  11. *
  12. * </per>
  13. */
  14. @Slf4j
  15. @RestController
  16. @RequestMapping("/www")
  17. public class Controller {
  18. @GetMapping("/bbb")
  19. @SysLogEs(value = "testDDD",type= LogTypeEnum.DELETE,index = "user_action")
  20. public void addJwBjAllBaseinfo() {
  21. System.out.println("====");
  22. }
  23. }

七.创建Es索引和字段

  1. PUT user_action
  2. PUT user_action/_mapping
  3. {
  4. "properties": {
  5. "id": {
  6. "type": "keyword"
  7. },
  8. "type": {
  9. "type": "keyword"
  10. },
  11. "title": {
  12. "type": "keyword"
  13. },
  14. "remoteAddr": {
  15. "type": "ip"
  16. },
  17. "userId": {
  18. "type": "keyword"
  19. },
  20. "username": {
  21. "type": "text",
  22. "fielddata": true
  23. },
  24. "requestUri": {
  25. "type": "text"
  26. },
  27. "useTime": {
  28. "type": "integer"
  29. },
  30. "browser": {
  31. "type": "text"
  32. },
  33. "exception": {
  34. "type": "text"
  35. },
  36. "requestTime": {
  37. "type": "date"
  38. }
  39. }
  40. }

八.创建kibana索引

九.在kibana中创建仪表盘等(前提是ES有了一定量数据哈)

仿照kibana系统数据创建自己的仪表盘等

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/86824
推荐阅读
相关标签
  

闽ICP备14008679号