微服务架构统一异常监控Sentry
Sentry
- 基本介绍
- 简单使用
- Java项目应用
-
- 简单demo
- 项目实战
基本介绍
Sentry 是一个开源的实时错误报告工具,支持 web 前后端、移动应用以及游戏,支持 Python、OC、Java、Go、Node、Django、RoR 等主流编程语言和框架 ,还提供了 GitHub、Slack、Trello 等常见开发工具的集成。
Senty是专门用来干异常日志监控的,它的核心就是围绕异常日志来建模和设计的,它有很多的异常日志监控特性,包括智能错误分析,归类汇总,自动分配告警到相关团队等等,这些虽然理论上ELK也能实现,但是实现成本比较高。
Sentry是一个应用监控系统,可以用于前后端各种技术栈的线上监控和错误分析。
简单使用
首先打开Sentry 的官网sentry官网,并且进行一系列的注册,创建一个组织,这里我创建一个组织,命名叫做ah
创建项目
获取dsn
Java项目应用
io.sentry sentry 5.7.3
简单demo
配置Sentry
@SpringBootApplicationpublic 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@RestControllerpublic 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; }}
返回对象封装
@Datapublic 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@ToStringpublic 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显示如下