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

Sentry

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

基本介绍

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

简单使用

首先打开Sentry 的官网sentry官网,并且进行一系列的注册,创建一个组织,这里我创建一个组织,命名叫做ah
微服务架构统一异常监控Sentry_第1张图片

创建项目
微服务架构统一异常监控Sentry_第2张图片
微服务架构统一异常监控Sentry_第3张图片
获取dsn

微服务架构统一异常监控Sentry_第4张图片

Java项目应用

 
            io.sentry
            sentry
            5.7.3
        

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

配置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!";
    }
}

错误如下
微服务架构统一异常监控Sentry_第5张图片
在这里插入图片描述

全局拦截器捕获(建议)

全局异常拦截类

@ControllerAdvice
@RestController
public class GlobalExceptionHandler {

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

    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public ResultModel 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 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 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 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 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 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 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  ResultModel error(ErrorCode errorCode){
        return new ResultModel<>(errorCode);
    }
    public static  ResultModel  error(long errorCode,String msg){
        return new ResultModel<>(errorCode,msg);
    }
    public static  ResultModel error(long errorCode, String msg, T data){
        return new ResultModel<>(errorCode,data,msg);
    }
    public static  ResultModel fail(){
        return new ResultModel<>(ErrorCode.FAIL);
    }
    public static  ResultModel succ(){
        return new ResultModel<>(ErrorCode.SUCCESS);
    }
    public static  ResultModel 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显示如下
微服务架构统一异常监控Sentry_第6张图片

Lockback.xml 配置(建议)

xml配置


    
    
        
            %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
        
    

    
    
        
            WARN
        
    

    
    
        
        
    

代码使用

   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!";
    }

微服务架构统一异常监控Sentry_第7张图片

小结

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

你可能感兴趣的:(公共,Sentry)