Spring Boot Admin2 自定义异常监控

news2025/7/15 6:46:24

其他相关文章:

  1. Spring Boot Admin 参考指南
  2. SpringBoot Admin服务离线、不显示健康信息的问题
  3. Spring Boot Admin2 @EnableAdminServer的加载
  4. Spring Boot Admin2 AdminServerAutoConfiguration详解
  5. Spring Boot Admin2 实例状态监控详解
  6. Spring Boot Admin2 自定义JVM监控通知

有时候客户往往会先于我们发现产品的异常,经常是他们跟我们反馈,为了改变这种状况,我们需要监控服务发生的各种异常。而正好SBA2 提供了HTTP请求相关的异常统计指标。那么我们经过适当的设计就可以达到每次有异常发生的时候就发送通知,实现原理就是:查询的最新异常累计值大于原有值时,表示新发生异常

看下提醒效果:

在这里插入图片描述

同样的马上代码:

NotifierAutoConfiguration.exceptionAlarm

@Bean(initMethod = "start", destroyMethod = "stop")
@ConditionalOnProperty(prefix = "spring.boot.admin.notify.exception", name = "enabled", havingValue = "true")
@ConfigurationProperties("spring.boot.admin.notify.exception")
@Lazy(false)
public ExceptionAlarm exceptionAlarm(InstanceRepository repository, AlarmMessage alarmMessage) {
    return new ExceptionAlarm(repository, alarmMessage);
}

ExceptionAlarm

@Slf4j
public class ExceptionAlarm {

    private final RestTemplate restTemplate = new RestTemplate();

    private Scheduler scheduler;

    private Disposable subscription;

    private InstanceRepository repository;


    private AlarmMessage alarmMessage;

    /**
     * 开关
     */
    private boolean enabled = true;

    /**
     * 检测频率,秒
     */
    private long interval = 10;

    /**
     * 排除异常
     */
    private String exclude = "None,BizException";

    /**
     * 排除实例
     */
    private String excludeInstances = "";

    /**
     * 提醒模版
     */
    private final String ALARM_TPL = "服务实例【%s】,发生异常【%s】";

    /**
     * 最后一次检测时的异常次数
     */
    private final Map<String, Map<String, Integer>> instanceCount = new HashMap<>();

    public ExceptionAlarm(InstanceRepository repository, AlarmMessage alarmMessage) {
        this.repository = repository;
        this.alarmMessage = alarmMessage;
    }

    private void checkFn(Long aLong) {
        if (!enabled) {
            return;
        }
        log.debug("check exception for all instances");
        //检查所有实例
        repository.findAll().filter(instance -> !excludeInstances.contains(instance.getRegistration().getName())).map(instance -> {
            String instanceName = instance.getRegistration().getName();
            List<String> exceptionList = getExceptionTag(instance);
            for (String exception : exceptionList) {
                Integer value = getValue(instance, exception);
                Integer lastValue = instanceCount.getOrDefault(instanceName, new HashMap<>()).get(exception);
                //查询的异常统计值大于原有值时,表示新发生异常
                if (lastValue != null && value > lastValue) {
                    String content = String.format(ALARM_TPL, instanceName, exception);
                    alarmMessage.sendData(content);
                    instanceCount.get(instanceName).put(exception, value);
                } else {
                    Map<String, Integer> map = instanceCount.getOrDefault(instanceName, new HashMap<>());
                    map.put(exception, value);
                    instanceCount.put(instanceName, map);
                }
            }
            return Mono.just(0d);
        }).subscribe();
    }

    private Integer getValue(Instance instance, String tags) {
        String reqUrl = instance.getRegistration().getManagementUrl() + "/metrics/http.server.requests?tag=exception:" + tags;
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(reqUrl, String.class);
        String body = responseEntity.getBody();
        JSONObject bodyObject = JSON.parseObject(body);
        JSONArray measurementsArray = bodyObject.getJSONArray("measurements");
        if (measurementsArray != null && !measurementsArray.isEmpty()) {
            return measurementsArray.getJSONObject(0).getInteger("value");
        }
        return 0;
    }

    private List<String> getExceptionTag(Instance instance) {
        try {
            String reqUrl = instance.getRegistration().getManagementUrl() + "/metrics/http.server.requests";
            log.debug("check jvm {},uri {}", instance.getRegistration().getName(), reqUrl);
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(reqUrl, String.class);
            String body = responseEntity.getBody();
            JSONObject bodyObject = JSON.parseObject(body);
            JSONArray tagsArray = bodyObject.getJSONArray("availableTags");
            if (tagsArray != null && !tagsArray.isEmpty()) {
                for (Object tag : tagsArray) {
                    JSONObject tagObject = (JSONObject) tag;
                    if ("exception".equals(tagObject.getString("tag"))) {
                        List<String> valuesList = tagObject.getJSONArray("values").toJavaList(String.class);
                        return valuesList.stream().filter(s -> !exclude.contains(s)).collect(Collectors.toList());
                    }
                }
            }
        } catch (Exception ex) {
            log.error(ex.getMessage());
        }
        return Collections.emptyList();
    }

    public long getInterval() {
        return interval;
    }

    public void setInterval(long interval) {
        this.interval = interval;
    }

    public String getExclude() {
        return exclude;
    }

    public void setExclude(String exclude) {
        this.exclude = exclude;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public String getExcludeInstances() {
        return excludeInstances;
    }

    public void setExcludeInstances(String excludeInstances) {
        this.excludeInstances = excludeInstances;
    }

    private void start() {
        this.scheduler = Schedulers.newSingle("exception-check");
        this.subscription = Flux.interval(Duration.ofSeconds(this.interval)).subscribeOn(this.scheduler).subscribe(this::checkFn);
        initInstanceCount();
    }

    private void initInstanceCount() {
        repository.findAll().map(instance -> {
            String instanceName = instance.getRegistration().getName();
            List<String> exceptionList = getExceptionTag(instance);
            for (String exception : exceptionList) {
                Integer value = getValue(instance, exception);
                Map<String, Integer> map = instanceCount.getOrDefault(instanceName, new HashMap<>());
                map.put(exception, value);
                instanceCount.put(instanceName, map);
            }
            return Mono.just(0d);
        }).subscribe();
    }

    private void stop() {
        if (this.subscription != null) {
            this.subscription.dispose();
            this.subscription = null;
        }
        if (this.scheduler != null) {
            this.scheduler.dispose();
            this.scheduler = null;
        }
    }
}

其中

/**
 * 排除异常
 */
private String exclude = "None,BizException";

该处代码用来排除不告警的异常,因为我们的很多业务异常也会被SBA2记录,此类异常不太具有参考价值,排除。

private void start() {
    this.scheduler = Schedulers.newSingle("exception-check");
    this.subscription = Flux.interval(Duration.ofSeconds(this.interval)).subscribeOn(this.scheduler).subscribe(this::checkFn);
    initInstanceCount();
}

private void initInstanceCount() {
    repository.findAll().map(instance -> {
        String instanceName = instance.getRegistration().getName();
        List<String> exceptionList = getExceptionTag(instance);
        for (String exception : exceptionList) {
            Integer value = getValue(instance, exception);
            Map<String, Integer> map = instanceCount.getOrDefault(instanceName, new HashMap<>());
            map.put(exception, value);
            instanceCount.put(instanceName, map);
        }
        return Mono.just(0d);
    }).subscribe();
}

该处代码用于在SBA2启动的时候重新价值对应服务的异常到内存中,由于SBA2默认都是将异常保存在内存中,每次重启就会丢失。
其他同JVM监控,不再叙述。

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

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

相关文章

Java项目:JSP旅游产品销售管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为前后台&#xff0c;分为管理员与普通用户两种角色&#xff0c;管理员登录后台&#xff0c;普通用户登录前台&#xff1b; 管理员角色…

Docker-CentOS开启防火墙firewalled映射Docker端口

开启docker的Tomcat容器后&#xff0c;启动 docker run -d -p 8080:8080 tomcat 访问不了Tomcat 查看防火墙所有开放的端口 firewall-cmd --zonepublic --list-ports 一、需要防火墙开启8080 端口 1、通过systemctl status firewalld查看firewalld状态&#xff0c;发现当前…

03.OpenWrt-系统固件烧录

03.OpenWrt-系统固件烧录 3.1 tft软件烧录 tftp是运行在windows的软件,是将windows主机作为服务端,OpenWrt开发板作为客户端,通过tftp协议将数据发送到开发板进行数据升级. 3.1.1 tftp升级的连接方式 tftp烧录有两种连接方式: windows主机通过有线或者无线的方式连接到路由…

IDEA 代码提交前流程及提交日志模板化

前言 在开发大型项目时&#xff0c;通常都是由团队来进行开发。此时&#xff0c;每个人有每个人的代码编写风格和提交习惯&#xff0c;如果放任自由发挥&#xff0c;那么代码质量和代码提交日志就难免风格各异&#xff0c;导致项目代码质量难以保持统一。针对这一问题&#xf…

Flutter高仿微信-第44篇-群聊

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 实现代码&#xff1a; group_chat_main.dart /*** Author : wangning* Email …

Unity VR 开发教程: Oculus 一体机开发 (一) 环境配置(基于 Oculus Integration v46)

文章目录&#x1f4d5;教程说明&#x1f4d5;安装 Unity 时需要添加的模块&#x1f4d5;设置 Unity 的 Build Settings&#x1f4d5;导入 Oculus Integration&#x1f4d5;设置 Project Settings⭐通用设置⭐Rendering 设置⭐Identification 设置⭐Configuration 设置⭐XR Plug…

信号类型(雷达)——雷达波形认识(一)

系列文章目录 《信号类型&#xff08;雷达通信&#xff09;》 文章目录 前言 简述 总结 前言 本文将结合个人研究经验&#xff0c;从雷达波形简单谈谈我对雷达的认识。之后将对常见的雷达波形进行简单分析。 简述 雷达的波形决定了信号处理的手段以及对应的雷达功能&#x…

关于WEB端实现电子海图研究二GeoServer

记笔记&#xff0c;免忘记&#xff01; 接前面思路篇。本文章主要是讲&#xff0c;利用GeoServer对shp文件进行数据样式调整 &#xff0c;数据拼接&#xff0c;shp文件发布&#xff0c;矢量切片。 Geoserver官网&#xff1a;GeoServer 可以使用最新版本&#xff0c;我使用的…

边缘计算那些事儿--网络切片技术(1)

0 背景 边缘计算可以支持就近的计算卸载&#xff0c;让数据在靠近数据源的设备上处理。对于边缘计算低时延的应用场景来说&#xff0c;网络性能的好坏&#xff0c;直接影响着卸载算法的整体耗时&#xff0c;决定着整个算法模型的可行性。因此如果想实现低时延卸载算法&#xff…

Go语言入门【7】指针

指针 在go语言中&#xff0c;每一个变量在运行时都会拥有一个地址&#xff0c;这个地址代表的就是变量在内存中的位置&#xff0c;而指针就是指向这个地址的变量。使用go语言中的指针很简单&#xff0c;只需要记住两个关键字符&#xff0c;&&#xff08;取地址符&#xff…

[附源码]java毕业设计在线二手车交易信息管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【App自动化测试】(九)移动端复杂测试环境模拟——来电、短信、网络切换

目录1. 发送短信2. GSM电话3. 设置模拟信号强弱4. 设置网速5. 设置网络连接类型前言&#xff1a; 本文为在霍格沃兹测试开发学社中学习到的一些技术写出来分享给大家&#xff0c;希望有志同道合的小伙伴可以一起交流技术&#xff0c;一起进步~ &#x1f618; 当我们使用模拟器来…

C#重启 --- 类和对象

​​​​​​ 1.类是抽象的&#xff0c;对象是具体的 2.类中有数据成员和方法成员&#xff08;数据成员是名词性的&#xff0c;方法成员是动词性的&#xff09; 1.类的关键字是class &#xff08;变量&#xff1a;首字母小写&#xff0c;方法&#xff1a;首字母大写&#x…

【毕业设计】机器学习的员工离职模型研究-python

目录 前言 课题背景和意义 实现技术思路 变量分析 数据导入 构建机器学习模型 1. 1 复制数据删除不需要的变量 1.2 列变量属性分类 实现效果图样例 前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设…

Charles证书安装windows11抓包 Https协议

1、Charles 的CA证书 如果需要截取分析 Https 协议相关的内容&#xff0c;那么需要安装 Charles 的 CA 证书&#xff0c;需要在 win11 电脑上安装证书。 2、证书安装路径 点击 Charles 的顶部菜单&#xff0c;选择 “Help” –> “SSL Proxying” –> “Install Charl…

Revit修改:网格角度,体量轮廓,梁随斜板

一、Revit中使幕墙系统网格改变角度 绘制幕墙系统时&#xff0c;若幕墙系统出现如下图情况&#xff1a; 若想改变该网格的角度&#xff0c;使其与该幕墙上下边界平行或垂直则选中该幕墙&#xff0c;修改属性栏的中的网格角度。如下图所示&#xff1a; 修改完所需角度后&#xf…

web网页设计期末课程大作业——汉中印象旅游景点介绍网页设计与实现19页面HTML+CSS+JavaScript

&#x1f468;‍&#x1f393;学生HTML静态网页基础水平制作&#x1f469;‍&#x1f393;&#xff0c;页面排版干净简洁。使用HTMLCSS页面布局设计,web大学生网页设计作业源码&#xff0c;这是一个不错的旅游网页制作&#xff0c;画面精明&#xff0c;排版整洁&#xff0c;内容…

基于FPGA的Hamiton方程--辛几何算法实现(全网唯一)

1、本文实验基于冯康院士的《哈密尔顿系统的辛几何算法》开展&#xff0c;链接&#xff1a;https://pan.baidu.com/s/1GM0Px7SLWBWzh4sXmAdcwg 提取码&#xff1a;fmkt 2、虽然题目写的是基于FPGA的求解&#xff0c;但实际上采用的是VHLS来实现的&#xff0c;最近根本不想写v…

m基于rbf神经网络和遗传算法优化的MIMO-OFDM系统信道估计算法matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法描述 MIMO-OFDM的信道估计&#xff1a;时&#xff0c;频&#xff0c;空三个域都要考虑&#xff0c;尤其是在空域&#xff0c;不同天线发射的导频序列需要相互正交&#xff0c;否则在接收端无法…

使用星凸随机超曲面模型对扩展对象和分组目标进行形状跟踪(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…