jsoup登录日志平台后调企业微信机器人自动发送错误日志告警

一、需求:错误日志Top10告警发送

二、需求分解

  1. jsoup实现登录,获取到cookie和token等用户鉴权信息
  2. 获取接口相应的key值
  3. 调用日志平台错误日志Top榜接口,查询到结果集
  4. 调用企业微信机器人发送消息接口
  5. 加上定时任务,可以实现定时发送错误日志告警的功能(后续加进定时任务里面去)

jsoup是java的爬虫框架,可以爬取网页数据,这里没有重点使用,只是做了个登录功能。后续可以专门它写一份爬虫的程序。。

  <dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.12.1</version></dependency>
package com.smy.cbs.task;import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.smy.cbs.util.RetryUtil;
import com.smy.framework.core.support.SpringTestCase;
import org.apache.commons.collections4.CollectionUtils;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.junit.Test;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import javax.annotation.Resource;
import javax.net.ssl.*;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;/*** @author youlu* @ClassName HttpTestt* @Date 2023/11/22 17:14* @Version V1.0**/
public class HttpTestt2 extends SpringTestCase {@Resourceprivate RetryUtil retryUtil;public static final String LOG_CENTER_BASE_URL = "https://logxxxxx.com/api/v1/";public static final String USER_NAME = "aaaaa";public static final String PASSWORD = "bbbbb";public static String X_Csrf_Token = "";public static String COOKIE = "";public static String id = "";public static int maxCount = Integer.MAX_VALUE;//达到阈值则告警public static List<String> filterContent = Lists.newArrayList();//过滤内容public static int SUB_CONTENT_LENGTH = 240;//截取报错内容字符串长度public static int SUB_TITLE_LENGTH = 20;//截取报错类型字符串长度public static int PERIOD_HOUR = 24*7;//24小时内的错误日志public static List<String> SEARCH_SYSTEM = Lists.newArrayList("cbs_core", "adv_core", "rls_core", "uts_core");//查询的系统//public static List<String> SEARCH_SYSTEM = Lists.newArrayList("adv_core");//查询的系统public static String WX_ROBOT_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=05e1f4a8-aaaaaa" /*+ ",https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=87140cb7-bbbbbb"*/;public static final String TEMPLATE = "{\"markdown\":{\"content\":\"%s\"},\"msgtype\":\"markdown\"}";public static final String QUERY_TEMPLATE = "{\n" +"  \"app\": \"system\",\n" +"  \"source\": \"other\",\n" +"  \"query\": \"repo=\\\"smy_%s\\\" origin=\\\"*\\\" AND \\\"ERROR\\\"\\n| where level=\\\"ERROR\\\"\\n| eval err_type=arr_index(split(arr_index(split(_raw, \\\" - \\\"), 1), \\\"\\\\d+\\\"), 0)\\n| stats count() as num by err_type\\n| sort 10 by num\\n| join type=inner err_type [\\n  repo=\\\"smy_%s\\\" origin=\\\"*\\\" AND \\\"ERROR\\\"\\n  | where level=\\\"ERROR\\\"\\n  | eval err_type=arr_index(split(arr_index(split(_raw, \\\" - \\\"), 1), \\\"\\\\d+\\\"), 0)\\n  | fields + err_type, _raw\\n  | dedup err_type\\n]\\n| rename _raw as 原始日志, num as 统计, err_type as 错误类型\",\n" +"  \"mode\": \"smart\",\n" +"  \"preview\": false,\n" +"  \"collectSize\": -1,\n" +"  \"timeout\": 1000,\n" +"  \"sorts\": []\n" +"}";public static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10, 0, TimeUnit.MINUTES,new LinkedBlockingQueue<Runnable>(100), new CustomizableThreadFactory("Log_Center-pool-"));@Testpublic void LogCenterData() throws IOException {//1.模拟登录 jsoupjsoupLogin();for (String systemName : SEARCH_SYSTEM) {try {Thread.sleep(1000);//2.获取keyString id = getKey(systemName);Thread.sleep(8000);List<Map<String, Object>> logCenterList = retryUtil.doRetry(4, () -> {//3.获取查询结果List<Map<String, Object>> contentList = getContentList(systemName, id);if (CollectionUtils.isEmpty(contentList)) {Thread.sleep(8000);throw new Exception(systemName + "未查询到数据,需要重试!");}return contentList;}, systemName + "获取日志数据");//4.调微信发送短信//if ("adv_core".equals(systemName) && CollectionUtils.isNotEmpty(logCenterList)) {//    List<List<Map<String, Object>>> partition = Lists.partition(logCenterList, 5);//    partition.stream().forEach(k -> sendWxMessage(systemName, id, k));//} else {sendWxMessage(systemName, id, logCenterList);//}} catch (Exception e) {e.printStackTrace();}}}public static String getKey(String systemName) {Date endDate = new Date();Date startDate = DateUtil.offsetHour(endDate, -PERIOD_HOUR);//请求参数JSONObject paramJson = JSON.parseObject(String.format(QUERY_TEMPLATE, systemName, systemName));paramJson.put("startTime",startDate.getTime());paramJson.put("endTime",endDate.getTime());HttpResponse execute = HttpRequest.post(LOG_CENTER_BASE_URL + "jobs")//设置请求头(可任意加).header("X-Csrf-Token", X_Csrf_Token).header("Cookie", COOKIE).header("Content-Type", "application/json").header("Connection","keep-alive")//请求参数.body(paramJson.toJSONString()).timeout(40000).execute();String body1 = execute.body();String id = JSON.parseObject(body1).getString("id");return id;}public static List<Map<String,Object>> getContentList(String systemName,String id)  {String url = LOG_CENTER_BASE_URL + "jobs/" + id + "/results";url += "?_=" + System.currentTimeMillis();HttpResponse execute = HttpRequest.get(url)//设置请求头(可任意加).header("X-Csrf-Token", X_Csrf_Token).header("Cookie", COOKIE).header("Content-Type", "application/json").header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36").header("Sec-Ch-Ua-Platform", "Windows").header("Connection","keep-alive").timeout(40000).execute();JSONArray rows = JSON.parseObject(execute.body()).getJSONArray("rows");System.err.println(rows.toString());List<Map<String, Object>> list = Lists.newArrayList();for (Object row : rows) {try {JSONArray jsonArray = JSON.parseArray(row.toString());String title = jsonArray.get(0).toString();Integer count = Integer.valueOf(jsonArray.get(1).toString());String content = jsonArray.get(2).toString();if (count >= maxCount) {continue;}if (filterContent.stream().anyMatch(k -> content.contains(k))) {continue;}int subContentLen = content.length() <= SUB_CONTENT_LENGTH ? content.length() : SUB_CONTENT_LENGTH;String subContent = content.substring(0, subContentLen);int subTitleLen = title.length() <= SUB_TITLE_LENGTH ? title.length() : SUB_TITLE_LENGTH;String subTitle = content.substring(0, subTitleLen);Map<String, Object> map = new HashMap<>();map.put("title", subTitle);map.put("count", count);map.put("content", subContent);list.add(map);} catch (Exception e) {e.printStackTrace();}}if (CollectionUtils.isEmpty(list)) {System.err.println(systemName + ":未获取到数据,请求链接:" + url + "   token:" + X_Csrf_Token + "   cookie:" + COOKIE);}return list;}public static void sendWxMessage(String systemName, String id, List<Map<String, Object>> contentList) {if (CollectionUtils.isEmpty(contentList)) {System.err.println(systemName + ":发送内容为空,不发送机器人微信消息,id:" + id);return;}String periodDesc = getPeriodDesc();final int[] topNum = {1};String StringContent = contentList.stream().map(k -> {String title = (String) k.get("title");Integer count = (Integer) k.get("count");String content = (String) k.get("content");//return String.format("> top-%d:%s\n> 出现次数:%d\n> 错误详情描述: %s", topNum[0]++, title, count, content.trim());return String.format("> top-%d:出现次数:%d\n> 错误详情描述: %s", topNum[0]++, count, content.trim());}).collect(Collectors.joining("\n\n"));String content = String.format("%s近%s内错误日志Top10\n%s", systemName, periodDesc, StringContent);String sendContent = String.format(TEMPLATE, content);////Map<String, String> contentMap = new HashMap<>();//contentMap.put("content", content);//Map<String, String> markdownMap = new HashMap<>();//markdownMap.put("markdown", JSON.toJSONString(contentMap));//markdownMap.put("msgtype", "markdown");//String sendText = JSON.toJSONString(markdownMap);////System.err.println(sendText);Arrays.stream(WX_ROBOT_URL.split(",")).forEach(wx -> {//String post = HttpUtil.post(wx + "&debug=1", sendText);//String post2 = HttpUtil.post(wx + "&debug=1", sendContent);String post2 = HttpUtil.post(wx, sendContent);System.err.println(systemName + "发送微信情况2" + post2);//System.err.println(systemName + "发送微信情况" + post);});}private static String getPeriodDesc() {if (PERIOD_HOUR <= 24) {return PERIOD_HOUR + "小时";}BigDecimal dayBigDecimal = BigDecimal.valueOf(PERIOD_HOUR).divide(BigDecimal.valueOf(24), 2, BigDecimal.ROUND_HALF_UP);String s = StrUtil.removeSuffix(StrUtil.removeSuffix(String.valueOf(dayBigDecimal), "00"), "0");String[] split = s.split("\\.");if (split.length == 1) {return split[0] + "天";}return s + "天";}/*** Jsoup 模拟登录 访问个人中心* 先构造登录请求参数,成功后获取到cookies* 设置request cookies,再次请求* @throws IOException*/public static void jsoupLogin() throws IOException {//Jsoup加这个,避免请求https报证书问题trustEveryone();// 构造登陆参数Map<String,String> data = new HashMap<>();data.put("username", USER_NAME);data.put("password", PASSWORD);Connection.Response response = Jsoup.connect(LOG_CENTER_BASE_URL + "account/ldap/login").ignoreContentType(true) // 忽略类型验证.ignoreHttpErrors(true).followRedirects(false) // 禁止重定向.postDataCharset("utf-8").header("Upgrade-Insecure-Requests","1").header("Accept","application/json").header("Content-Type","application/json").header("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36").requestBody(JSON.toJSONString(data)).method(Connection.Method.POST).execute();response.charset("UTF-8");// login 中已经获取到登录成功之后的cookies// 构造访问个人中心的请求Map<String, String> cookies = response.cookies();cookies.forEach((k, v) -> COOKIE = k + "=" + v);String body = response.body();X_Csrf_Token = JSON.parseObject(body).getString("X-Csrf-Token");System.err.println("COOKIE:" + COOKIE);System.err.println("X_Csrf_Token:" + X_Csrf_Token);}public static void jsoupHandle(String id) throws IOException {String url = LOG_CENTER_BASE_URL + "jobs/" + id + "/results";Document document = Jsoup.connect(url).header("X-Csrf-Token", X_Csrf_Token).header("Cookie", COOKIE).ignoreContentType(true) // 忽略类型验证.ignoreHttpErrors(true).method(Connection.Method.GET).get();System.err.println(url);String s = JSON.toJSONString(document);System.err.println(s);}public static void trustEveryone() {try {HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {public boolean verify(String hostname, SSLSession session) {return true;}});SSLContext context = SSLContext.getInstance("TLS");context.init(null, new X509TrustManager[] { new X509TrustManager() {public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}} }, new SecureRandom());HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());} catch (Exception e) {// e.printStackTrace();}}}

三、实现效果

企业微信机器狗开发者文档:群机器人配置说明 - 接口文档 - 企业微信开发者中心

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/175711.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java多线程-扩展知识二:线程的生命周期

1、生命周期 生命周期有广义与狭义之分&#xff0c;狭义为生命科学术语&#xff0c;指包括人类在内的一切动物由出生到死亡经历的生命全程。广义的生命周期泛指自然界与人类社会各种客观事物的阶段性变化及规律&#xff0c;如家庭生命周期、产品生命周期等。本义即狭义的生命周…

任务管理器怎么打开?4个方法快速打开!

“我想进入电脑任务管理器中对某些应用进行设置&#xff0c;但是我不知道应该怎么进入任务管理器中。有什么方法可以快速进入任务管理器吗&#xff1f;” 任务管理器是Windows操作系统中一个强大的工具&#xff0c;可以让你监控和管理计算机上运行的进程、应用程序和性能。当我…

FLASK博客系列5——模板之从天而降

我们啰啰嗦嗦讲了4篇&#xff0c;都是在调接口&#xff0c;啥时候能看到漂亮的页面呢&#xff1f;别急&#xff0c;今天我们就来实现。 来我们先来实现一个简单的页面。不多说&#xff0c;上代码。 app.route(/) def index():user {username: clannadhh}return <html>&…

解决方案:新版WPS-右键粘贴值到可见单元格没有了

旧版WPS&#xff0c;右键就能出现 但是新版WPS不是在这里&#xff08;方法1&#xff09; 新版WPS&#xff08;方法2&#xff09; 视频详细教程链接&#xff1a;解决方案&#xff1a;新版WPS-右键粘贴值到可见单元格没有了 -- 筛选后复制公式粘贴为数值_哔哩哔哩_bilibili

智能优化算法应用:基于飞蛾扑火算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于飞蛾扑火算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于飞蛾扑火算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.飞蛾扑火算法4.实验参数设定5.算法结果6.参考…

数智融合 开启金融数据治理新时代

11月24日&#xff0c;由上海罗盘信息科技有限公司&#xff08;罗盘科技&#xff09;主办&#xff0c;北京酷克数据科技有限公司&#xff08;酷克数据&#xff09;支持协办的“博学近思 切问治理”数据治理分享会在上海成功举行。 本次会议深度聚焦金融行业数智化转型&#xff…

FPGA模块——DA转换模块(AD9708类)

FPGA模块——DA转换模块&#xff08;AD9708类&#xff09; AD9708/3PD9708代码 AD9708/3PD9708 由于电路接了反相器&#xff0c;所以对应就不一样了。 电路图&#xff1a; 代码 在ROM中存入要输出的波形数据&#xff1a; 用软件生成各个对应的点。 给DA转换器一个时钟&…

网络篇---第四篇

系列文章目录 文章目录 系列文章目录前言一、TCP 如何保证可靠性二、OSI 的七层模型都有哪些?三、浏览器中输入:“www.woaijava.com”之后都发生了什么?请详细阐述前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站…

关于Unity中字典在Inspector的显示

字典在Inspector的显示 方法一&#xff1a;实现ISerializationCallbackReceiver接口 《unity3D游戏开发第二版》记录 在编辑面板中可以利用序列化监听接口特性对字典进行序列化。 主要继承ISerializationCallbackReceiver接口 实现OnAfterDeserialize() OnBeforeSerialize() …

C# WPF上位机开发(第一个应用)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 万事开头难&#xff0c;很多事情都是难在第一步。走出了这第一步&#xff0c;回过头看以前走的每一步&#xff0c;发现其实也不难。用c# wpf编写界…

VSD Viewer for Mac(Visio绘图文件阅读器)

VSD Viewer for Mac版是mac上一款非常强大的Visio绘图文件阅读器&#xff0c;它为打开和打印Visio文件提供了简单的解决方案。可以显示隐藏的图层&#xff0c;查看对象的形状数据&#xff0c;预览超链接。还可以将Visio转换为包含图层&#xff0c;形状数据和超链接的PDF文档。 …

【C/PTA —— 12.指针1(课外实践)】

C/PTA —— 12.指针1&#xff08;课外实践&#xff09; 一.函数题6-1 删除字符串中数字字符6-2 找最大值及其下标6-3 求两数平方根之和6-4 求一组数中的最大值、最小值和平均值6-5 两个4位正整数的后两位互换6-6 判断回文字符串 二.程序题7-1 求矩阵每行元素的和 一.函数题 6-…

5.golang字符串的拆解和拼接

字符串是 Go 中的字节切片。可以通过将一组字符括在双引号中来创建字符串" "。Go 中的字符串是兼容Unicode编码的&#xff0c;并且是UTF-8编码的。 访问字符串的单个字节或字符 由于字符串是字节切片&#xff0c;因此可以访问字符串的每个字节。 func printStr(s …

用python实现kindle文件转换pdf

上一篇文章讲了下用工具转换相关的格式&#xff1a;https://blog.csdn.net/weixin_42019349/article/details/134654695 今天来分享一个python库实现上述功能&#xff0c;实现文件转换自由 ^_^ 主角就是pypandoc库 # 安装方式 pip install pypandoc# pypandoc主要有三个函数…

MacOS 系统 Flutter开发Android 环境配置

上节我们已经把 开发工具准备齐全&#xff0c;并可以进行Flutter的web开发&#xff0c;本节将做安卓开发环境进行详细说明 接上节这里先说下&#xff0c;系统环境 MacOS14 &#xff08;Sonoma&#xff09; 芯片 Apple M3 执行命令&#xff1a;flutter doctor 提示如下&#…

好用的样式动画库集合(css、js)

文章目录 前言一、Animate.css二、Anime.js三、CSShake四、Hover.css五、AniJS六、Animista七、Tachyons-animate八、Sequence.js九、Infinite十、OBNOXIOUS.CSS十一、MOTION UI十二、Keyframes.app十三、AnimXYZ十四、Whirl十五、Hamburgers十六、Vivify十七、Magic Animation…

ESP32-Web-Server编程- JS 基础 3

ESP32-Web-Server编程- JS 基础 3 概述 本示例演示通过 button 控件的 onclick 内联属性&#xff0c;实现在网页上点击按钮&#xff0c;切换 LED 灯图标的转变。 示例解析 前端设计 前端代码建立了一个 id 为 “imageLamp” 的图片对象。并建立两个按钮&#xff0c;设计两…

Linux系统安装Docker-根据官方教程教程(以Ubuntu为例)

Linux系统安装Docker-根据官方教程教程&#xff08;以Ubuntu为例&#xff09; 1. 背景介绍2. 环境配置2.1 软件环境要求2.2 软件下载2.3 文档地址2.3 必备命令工具下载 3. 安装Docker3.1 使用root用户操作后续命令3.2 卸载可能存在的旧版本 4. 安装Docker4.1 更新依赖包4.4 配置…

lack——主页前后端开发优化(精华:java多线程实现数据插入)

lack——主页前后端开发优化 前端开发主页 最容易的方式&#xff1a;list列表<template><van-cardv-for"user in props.userList":desc"user.profile":title"${user.username} (${user.planetCode})":thumb"user.avatarUrl"…

SAP Smartforms自定义纸张格式

在写SMARTFORM或SAPScript的时候首先要定义纸张格式&#xff0c;如DIN4 A4, DIN5等&#xff0c;但当系统标准的纸张格式并不符合特定的纸张&#xff0c;就需要自定义一种纸张格式了。 总共三个步骤&#xff1a; 定义页格式(横打/竖打&#xff0c; 宽x高)定义格式类型(用于ABA…