微服务架构统一异常监控Sentry

news2024/11/25 6:59:45

Sentry

  • 基本介绍
  • 简单使用
  • Java项目应用
    • 代码侵入(不建议,耦合性大)
    • 全局拦截器捕获(建议)
    • Lockback.xml 配置(建议)

基本介绍

Sentry 是一个开源的实时错误报告工具,支持 web 前后端、移动应用以及游戏,支持 Python、OC、Java、Go、Node、Django、RoR 等主流编程语言和框架 ,还提供了 GitHub、Slack、Trello 等常见开发工具的集成。
Senty是专门用来干异常日志监控的,它的核心就是围绕异常日志来建模和设计的,它有很多的异常日志监控特性,包括智能错误分析,归类汇总,自动分配告警到相关团队等等,这些虽然理论上ELK也能实现,但是实现成本比较高。
Sentry是一个应用监控系统,可以用于前后端各种技术栈的线上监控和错误分析。

简单使用

首先打开Sentry 的官网sentry官网,并且进行一系列的注册,创建一个组织,这里我创建一个组织,命名叫做ah
在这里插入图片描述

创建项目
在这里插入图片描述
在这里插入图片描述
获取dsn

在这里插入图片描述

Java项目应用

 <dependency>
            <groupId>io.sentry</groupId>
            <artifactId>sentry</artifactId>
            <version>5.7.3</version>
        </dependency>

代码侵入(不建议,耦合性大)

配置Sentry

@SpringBootApplication
public class ThriftRpcApplication {


    public static void main(String[] args) {
        SpringApplication.run(ThriftRpcApplication.class, args);
        Sentry.init(options -> {
            options.setDsn("https://90cd056919fxxxxxxxxxxxxxxxxx954c6186db4@o1207430.ingest.sentry.io/6340908");
        });

    }
}

@RestController
@Api(value = "测试", tags = "测试")
@RequestMapping("/tt")
public class Controller {

    /**
     * 方法会发生500错误
     *
     * @return
     */
    @GetMapping("/helloworld")
    @ApiOperation("ceshi")
    public String helloworld() {
        try {
            int x = 1 / 0;
        } catch (Exception e) {
            Sentry.captureException(e);
        }
        return "Hello World!";
    }

    @GetMapping("/test")
    @ApiOperation("tt")
    public String test() {
        try {
            System.out.println("测试代码");
            throw  new Exception("测试错误。。。。");
        } catch (Exception e) {
            Sentry.captureException(e);
        }
        return "Hello World!";
    }
}

错误如下
在这里插入图片描述
在这里插入图片描述

全局拦截器捕获(建议)

全局异常拦截类

@ControllerAdvice
@RestController
public class GlobalExceptionHandler {

    private static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public ResultModel<Boolean> businessExceptionHandler(BusinessException e, ServletRequest request) {
        Sentry.captureException(e);
        ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
        logger.error("error in \nurl :{} \ncode:{} \nmsg:{} \nparams:{}\n body:{}", ((ContentCachingRequestWrapper) request).getRequestURI(), e.getErrorCode(), e.getMsg(), JSON.toJSONString(request.getParameterMap()), StringUtils.toEncodedString(wrapper.getContentAsByteArray(), Charset.forName(wrapper.getCharacterEncoding())));
        return ResultModel.error(e.getErrorCode(), e.getMsg());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResultModel<Boolean> methodArgumentNotValidHandler(MethodArgumentNotValidException e, ServletRequest request) {
        ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
        String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
        logger.error("error in \nurl :{} \nmsg:{} \nparams:{}\n body:{}", ((ContentCachingRequestWrapper) request).getRequestURI(), message, JSON.toJSONString(request.getParameterMap()), StringUtils.toEncodedString(wrapper.getContentAsByteArray(), Charset.forName(wrapper.getCharacterEncoding())));
        Sentry.captureException(e);
        return ResultModel.error(ErrorCode.FAIL.getCode(), message);
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    public ResultModel<Boolean> methodArgumentNotValidHandler(MissingServletRequestParameterException e, ServletRequest request) {
        ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
        String message = e.getMessage();
        logger.error("error in \nurl :{} \nmsg:{} \nparams:{}\n body:{}", ((ContentCachingRequestWrapper) request).getRequestURI(), message, JSON.toJSONString(request.getParameterMap()), StringUtils.toEncodedString(wrapper.getContentAsByteArray(), Charset.forName(wrapper.getCharacterEncoding())));
        Sentry.captureException(e);
        return ResultModel.error(ErrorCode.FAIL.getCode(), "必填参数为空");
    }

    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    public ResultModel<Boolean> illegalArgumentHandler(IllegalArgumentException e, ServletRequest request) {
        ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
        logger.error("error in \nurl :{} \nmsg:{} \nparams:{}\n body:{}", ((ContentCachingRequestWrapper) request).getRequestURI(), e.getMessage(), JSON.toJSONString(request.getParameterMap()), StringUtils.toEncodedString(wrapper.getContentAsByteArray(), Charset.forName(wrapper.getCharacterEncoding())));
        Sentry.captureException(e);
        return ResultModel.error(ErrorCode.FAIL.getCode(), e.getMessage()
        );
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResultModel<Boolean> exceptionHandler(Exception e, ServletRequest request) {

        ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
        logger.error("error in \nurl :{} \nparams:{}\nbody:{}", ((ContentCachingRequestWrapper) request).getRequestURI(), JSON.toJSONString(request.getParameterMap()), StringUtils.toEncodedString(wrapper.getContentAsByteArray(), Charset.forName(wrapper.getCharacterEncoding())));
        logger.error("程序运行出现异常!", e);
        Sentry.captureException(e);
        return ResultModel.error(ErrorCode.UNDEFINED);

    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    public ResultModel<Boolean> exceptionHandler(HttpMessageNotReadableException e, ServletRequest request) {

        ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
        logger.error("error in \nurl :{} \nparams:{}\nbody:{}", ((ContentCachingRequestWrapper) request).getRequestURI(), JSON.toJSONString(request.getParameterMap()), StringUtils.toEncodedString(wrapper.getContentAsByteArray(), Charset.forName(wrapper.getCharacterEncoding())));
        logger.error("程序运行出现异常!", e);
        Sentry.captureException(e);
        return ResultModel.error(ErrorCode.PARAM_ERROR.getCode(), "json格式错误:" + e.getLocalizedMessage());

    }


}

错误码接口

public interface IErrorCode {
    long getCode();

    String getMessage();
}

错误码类

public enum ErrorCode implements IErrorCode {
    /**
     * 成功
     */
    SUCCESS(0L, "成功"),
    /**
     * 失败
     */
    FAIL(1L, "失败"),
    /**
     * 参数异常
     */
    PARAM_ERROR(2L, "参数异常"),
    /**
     * 服务连接异常
     */
    CONNECTION_ERROR(3L, "服务连接异常"),
    /**
     * 未明确定义名称异常
     */
    UNDEFINED(10001, "未明确定义名称异常"),
    ;


    private long code;
    private String message;

    ErrorCode(long code, String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public long getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }
}

返回对象封装

@Data
public class ResultModel<T> implements Serializable {

    /**
     * 返回错误码
     */
    @ApiModelProperty(value="返回错误码数")
    private long code = ErrorCode.SUCCESS.getCode();

    /**
     * 返回错误信息
     */
    @ApiModelProperty(value="返回错误信息")
    private String message = ErrorCode.SUCCESS.getMessage();

    /**
     * 数据
     */
    @ApiModelProperty(value="数据")
    private T data;

    @ApiModelProperty(value="时间戳")
    private long timestamp = System.currentTimeMillis();


    /**是否加密**/
    @ApiModelProperty(value="是否加密")
    private boolean encryption = false;

    /**加密数据**/
    @ApiModelProperty(value="加密数据")
    private String ciphertext;


    public ResultModel(long code, T data, String message) {
        super();
        this.code = code;
        this.message = message;
        this.data = data;
    }
    public ResultModel(long code, String message) {
        super();
        this.code = code;
        this.message = message;
    }
    public ResultModel(ErrorCode errorCode) {
        super();
        this.code = errorCode.getCode();
        this.message = errorCode.getMessage();
    }
    public ResultModel() {
    }

    public ResultModel(T data) {
        this.data = data;
    }

    public static <T> ResultModel<T> error(ErrorCode errorCode){
        return new ResultModel<>(errorCode);
    }
    public static <T> ResultModel<T>  error(long errorCode,String msg){
        return new ResultModel<>(errorCode,msg);
    }
    public static <T> ResultModel<T> error(long errorCode, String msg, T data){
        return new ResultModel<>(errorCode,data,msg);
    }
    public static <T> ResultModel<T> fail(){
        return new ResultModel<>(ErrorCode.FAIL);
    }
    public static <T> ResultModel<T> succ(){
        return new ResultModel<>(ErrorCode.SUCCESS);
    }
    public static <T> ResultModel<T> succ(T data){
        return new ResultModel<>(data);
    }
}

业务异常类


import lombok.Getter;
import lombok.Setter;
import lombok.ToString;


@Getter
@Setter
@ToString
public class BusinessException extends RuntimeException {
    /** 异常码 */
    private Long errorCode = ErrorCode.UNDEFINED.getCode();
    /** 对用户友好的错误信息 */
    private String msg;

    public BusinessException(Long errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
        this.msg = message;
    }

    public BusinessException(String message) {
        super(message);
        this.msg = message;
    }

    public BusinessException(IErrorCode errorCode) {
        super(errorCode.getMessage());
        this.errorCode = errorCode.getCode();
        this.msg = errorCode.getMessage();
    }


}

项目使用

/**
     * 方法会发生500错误
     *
     * @return
     */
    @GetMapping("/helloworld")
    @ApiOperation("ceshi")
    public String helloworld() {
        try {
            int x = 1 / 0;
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        }
        return "Hello World!";
    }

Sentry显示如下
在这里插入图片描述

Lockback.xml 配置(建议)

xml配置

<configuration>
    <!-- Configure the Console appender -->
    <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Configure the Sentry appender, overriding the logging threshold to the WARN level -->
    <appender name="Sentry" class="io.sentry.logback.SentryAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>WARN</level>
        </filter>
    </appender>

    <!-- Enable the Console and Sentry appenders, Console is provided as an example
 of a non-Sentry logger that is set to a different logging threshold -->
    <root level="INFO">
        <appender-ref ref="Console" />
        <appender-ref ref="Sentry" />
    </root>
</configuration>

代码使用

   private final Logger LOGGER = LoggerFactory.getLogger(getClass());
    @GetMapping("/log")
    @ApiOperation("logback")
    public String log() {
        System.out.println("测试日志级别");
        if (true) {
            LOGGER.error("测试erro日志....你有个错误哦");
        }
        return "Hello World!";
    }

在这里插入图片描述

小结

建议 全局捕获异常和lockback配合使用,效果更好

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

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

相关文章

《从零开始读懂Web3》读后感 之 Web 3.0的前世今生

介绍什么是 Web 3.0 &#xff08;Web3&#xff09;&#xff1f;网络的演变为什么 Web 3.0 很重要&#xff1f;Web 3.0 将如何运作&#xff1f;Web 3.0 的主要功能和技术Web 3.0 用例和应用程序Web 3.0 的潜在好处是什么&#xff1f;Web 3.0 的潜在挑战是什么&#xff1f;Web 3.…

【3】使用YOLOv8训练自己的目标检测数据集-【收集数据集】-【标注数据集】-【划分数据集】-【配置训练环境】-【训练模型】-【评估模型】-【导出模型】

在自定义数据上训练 YOLOv8 目标检测模型的步骤可以总结如下 6 步&#xff1a; &#x1f31f;收集数据集&#x1f31f;标注数据集&#x1f31f;划分数据集&#x1f31f;配置训练环境&#x1f31f;训练模型&#x1f31f;评估模型 1. 收集数据集 随着深度学习技术在计算机视觉领…

Windows安装mysql详细步骤(通俗易懂,简单上手)

文章目录 【确认本地是否安装mysql】【下载mysql安装包】【添加配置文件并安装mysql】【修改mysql密码】【配置环境变量】【总结】 前期在windows电脑尝试安装mysql&#xff0c;经历诸多不顺&#xff0c;特把安装详细步骤以及遇见的问题记录下来&#xff0c;提供给有需者使用。…

Java-Web前后端交互实现登陆注册(附源码)

1.完成用户登录功能。 2.完成注册功能。 3.主体利用Maven导入java中的jar包&#xff0c;使用Servlet实现前后端交互&#xff0c;使用mybatis以及注解&#xff0c;mysql进行数据保存&#xff0c;Tomcat服务器进行开发。 效果图 项目结构 代码 mapper(相对应注解) package com.it…

前端搭建砸地鼠游戏(内附源码)

The sand accumulates to form a pagoda ✨ 写在前面✨ 功能介绍✨ 页面搭建✨ 样式设置✨ 逻辑部分✨ 完整代码 ✨ 写在前面 上周我们实通过前端基础实现了打字通&#xff0c;当然很多伙伴再评论区提出了想法&#xff0c;后续我们会考虑实现的&#xff0c;今天还是继续按照我…

微服务---Redis实用篇-黑马头条项目-优惠卷秒杀功能(使用redis的消息队列对秒杀进行异步优化)

微服务—Redis实用篇-黑马头条项目-优惠卷秒杀功能(使用redis的消息队列对秒杀进行异步优化) 1、Redis消息队列 1.1 Redis消息队列-认识消息队列 什么是消息队列&#xff1a;字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色&#xff1a; 消息队列&#xff1a…

零基础学模拟电路--2.运算放大器的虚短和虚断

零基础学模拟电路–2.运算放大器的虚短和虚断 虚短&#xff1a; 虚短指在理想情况下&#xff0c;两个输入端的电位相等&#xff0c;就好像两个输入端短接在一起&#xff0c;但事实上并没有短接&#xff0c;称为“虚短”。 由虚短可得出正负输入端点位相等的结论。 虚断&…

如何在 VS Code 中编写、运行C语言程序 教程

本篇目录 前言 1.下载、安装VS Code 2.安装VS code中2个插件 3.下载minGW64 4.配置系统的环境变量 5.C语言配置 6.编写一个测试程序 7.可能存在的问题 总结 前言 折腾了好久&#xff0c;终于成功地实现了在VS Code中写C语言程序&#xff0c;于是发文分享一下我的经验。 要想…

FPGA实现AD采集

1 理论学习&#xff08;废话篇&#xff09; ADC 模拟数字转换器&#xff08;额谈到这个&#xff0c;真的很荣幸在ADI实习的时光&#xff0c;打住不扯了&#xff09;&#xff0c;凡是涉及到模拟信号转数字信号的时候&#xff0c;都会用到ADC。   ADC的种类很多&#xff0c;有积…

解决el-checkbox点击文字也会选中

最近要做一个 多选框嵌套下拉框的一个功能&#xff0c;在点击下拉框时&#xff0c;多选框一直会被选中或者取消&#xff0c;这里做一下解决记录 首先展示一下要做的功能 出现原因&#xff1a; el 的checkbox的组件整个是由lable包裹的&#xff0c;所以重写el-checkbox就可以了…

〖大前端 - 基础入门三大核心之CSS篇㉓〗- 过渡的缓动效果

当前子专栏 基础入门三大核心篇 是免费开放阶段。推荐他人订阅&#xff0c;可获取扣除平台费用后的35%收益&#xff0c;文末名片加V&#xff01;说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费开放&#xff0c;购买任意白宝书体系化专栏可加入TFS…

〖大前端 - 基础入门三大核心之CSS篇㉑〗- 3D变形 与空间移动

当前子专栏 基础入门三大核心篇 也是免费开放阶段。推荐他人订阅&#xff0c;可获取扣除平台费用后的35%收益。说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费开放&#xff0c;购买任意白宝书体系化专栏可加入TFS-CLUB 私域社区。福利&#xff1a…

Java去除字符串中空格、制表符、回车换行的方法

\t 是制表符\r\n 回车换行 注意&#xff1a;\r,\n的顺序是不能够对换的&#xff0c;否则不能实现换行的效果&#xff0e;操作系统的不同&#xff0c;换行符操也不同&#xff1a;\r&#xff1a; return 到当前行的最左边。\n&#xff1a; newline 向下移动一行&#xff0c;并不移…

Reddit NFT爆火,全球最大社区论坛成为Web3大规模应用前哨站

这是白话区块链的第1804期原创 作者 | 火火出品&#xff5c;白话区块链&#xff08;ID&#xff1a;hellobtc&#xff09; 据Dune Analytics最新数据显示&#xff0c;Reddit于Polygon网络发行的NFT系列Reddit Collectible Avatar销售总量已突破9万笔&#xff0c;在12月7日达到94…

火灾报警电路设计

火灾报警电路设计 设计一个火灾报警电路&#xff1a;有一火灾报警系统&#xff0c;设有烟感、温感和紫外线 光感3种类型的火灾探测器。为了防止误报警&#xff0c;只有当其中有两种或两种以 上类型的探测器发出火灾检测信号时&#xff0c;报警系统才产生报警控制信号。设计一个…

Linux驱动device_create创建字符设备文件

在Linux中有两种创建字符设备的方法&#xff0c;一种是通过mknod手动进行设备文件创建&#xff0c;第二种是通过device_create函数进行设备文件创建。在驱动开发中常用第二种方式进行设备文件的创建。 class_create和device_create 先来了解一下跟设备文件创建相关的两个函数。…

window 以zip的方式 安装mysql5.7或mysql8,或者两个一起安装Mysql5.7和Mysql8、或其他的版本也可以

window 以zip的方式 安装mysql5.7或mysql8&#xff0c;或者两个一起安装Mysql5.7和Mysql8、或其他的版本也可以 注意不能同一个端口。需要创建个my.ini ,配置内容在网上查下即可 比如说 mysql8的配置文件或mysql5.7的配置&#xff0c;当然内容差别不大&#xff0c;只是需要看自…

数据库系统课程设计(高校成绩管理数据库系统的设计与实现)

目录 1、需求分析 1 1.1 数据需求描述 1 1.2 系统功能需求 3 1.3 其他性能需求 4 2、概念结构设计 4 2.1 局部E-R图 4 2.2 全局E-R图 5 2.3 优化E-R图 6 3、逻辑结构设计 6 3.1 关系模式设计 6 3.2 数据类型定义 6 3.3 关系模式的优化 8 4、物理结构设计 9 4.1 聚…

【AIGC】论文阅读神器 SciSpace 注册与测试

欢迎关注【youcans的 AIGC 学习笔记】原创作品 【AIGC】论文阅读神器 SciSpace 注册与测试 1. 【SciSpace】网址与用户注册1.1 官网地址&#xff1a;[【SciSpace官网】https://typeset.io](https://typeset.io)1.2 官网注册 2. 【SciSpace】实战解说2.1 导入论文2.2 论文分析2.…

基于Python-sqlparse的SQL表血缘追踪解析实现

目录 前言 一、主线任务 1.数据治理 2.血缘追踪 3.SQL表血缘 二、实现过程 1.目标效果 2.代码实现 1.功能函数识别 2.SQL标准格式 3.解析AST树 4.最终效果&#xff1a; 点关注&#xff0c;防走丢&#xff0c;如有纰漏之处&#xff0c;请留言指教&#xff0c;非常感…