一、項目背景
代碼異常的增多(error級別日志增多)也是系統異常的一種,對于這種情況,收到報警消息之后,我們開發同學一般需要登錄到線上機器,查看錯誤日志來排查具體的原因。這種情況下,如果報警消息中能夠包括出現異常的上下文以及異常堆棧,不僅能第一時間發現問題,這樣的話還能夠一定程度上提高問題的排查修復效率。以及我們也要關注線上的異常日志,進而針對異常日志反應的程序問題來相應的優化改造代碼。
所以我們就基于企業微信和logback日志系統來實現error級別異常日志發送企業微信群消息報警功能,消息中包括了異常上下文以及異常堆棧消息。
二、技術選型
針對日志監控,通常的手段有以下幾種:
1.在日志輸出時,并在寫到磁盤前,就可以通過攔截手段拿到日志并分析日志的級別,或者在異常處理時就觸發日志警告(比如告警系統Cat),也可以通過Java agent的手段(如skywalking使用探針技術收集),當然也可以在日志寫到磁盤上,由ELK采集日志并分析日志,并將異常結果推送到用戶,比如可以將日志采集并存儲到阿里云的SLS,并由SLS分析并觸發日志告警。
2.Logback是由log4j創始人設計的又一個開源日志組件。logback當前分成三個模塊:logback-core,logback- classic和logback-access。logback-core是其它兩個模塊的基礎模塊。logback-classic是log4j的一個 改良版本。此外logback-classic完整實現SLF4J API使你可以很方便地更換成其它日志系統如log4j或JDK14 Logging。logback-access訪問模塊與Servlet容器集成提供通過Http來訪問日志的功能。
三.為什么要使用Logback
其他的告警,都需要搭建一套完整的告警系統,如Cat,skywalking等都需要搭建獨立的系統。因此,我們就實現由logback集成企信機器人來實現告警功能。通過logback,我們是通過appender將日志分析并寫入到磁盤上,那么我們可以在appender獲取到日志內容,并判斷是否需要發送告警推送。
由于我們的系統中已經使用了Logback,我們就可以很方便的管理我們的日志。Logback也提供了一些可實現的日志處理供我們自定義實現來處理日志操作。
Logback詳細介紹及其日志輸出打印格式:https://www.cnblogs.com/origalom/p/11120423.html
四.接入思路及實現
企業微信群機器人配置:
企業微信給所有的企業微信群提供了機器人功能,通過群機器人,可以提供一些自定義的消息推送。簡單來說就是直接通過http調用webhook接口來發送群消息。
群機器人配置官方鏈接:https://developer.work.weixin.qq.com/document/path/91770
首先需要明確,微信報警消息中需要發送哪些數據。
新建TextMessage類,來定義微信報警中需要發送文本數據及設置需提醒的人或手機號。
新建Message類,來定義微信報警中需要發送哪些數據格式。
新建MessageManager類,實現sendMessage()方法來發送消息提醒。
具體實現:
public static boolean sendMessage(Message message, String webHookAddress) {OkHttpClient client = new OkHttpClient.Builder()// 設置連接超時時間.connectTimeout(10, TimeUnit.SECONDS)// 設置讀取超時時間.readTimeout(20, TimeUnit.SECONDS).build();MediaType contentType = MediaType.parse("application/json; charset=utf-8");RequestBody body = RequestBody.create(contentType, JSONObject.toJSONString(message));Request request = new Request.Builder().url(webHookAddress).post(body).addHeader("cache-control", "no-cache").build();try {log.info("===開始調用企業微信發送消息!");Response response = client.newCall(request).execute();byte[] datas = response.body().bytes();String respMsg = new String(datas);JSONObject resultJSON = JSONObject.parseObject(respMsg);if (resultJSON.getIntValue("errcode") == 0) {log.info("消息發送成功!");return true;}log.info("消息發送失敗, 錯誤信息如下: {}", resultJSON.getString("errmsg"));return false;} catch (IOException e) {
// log.info("消息發送成功!");log.info("消息發送失敗, 異常信息如下: {}", e.getMessage());return false;}}
logback攔截error級別的日志:
在logback中,要自定義Appeder,只需要繼承 AppenderBase類實現append()方法即可。
首先定義一個抽象類AbstractAlarmAppender,該類繼承自 AppenderBase類,并實現了append()方法:
具體實現代碼:
public abstract class AbstractAlarmAppender extends AppenderBase<LoggingEvent> {private static final String template_offline = "服務名: %s \n當前環境: %s \n日志等級: %s \n異常時間: %s \n異常描述: %s \n異常詳細信息: %s";@Overrideprotected void append(LoggingEvent eventObject) {try {Level level = eventObject.getLevel();if (Level.ERROR != level) {// 只處理error級別的報錯return;}// 獲取用戶在日志中輸出的語句,一般涵蓋異常上下文LoggerContextVO loggerContextVO = eventObject.getLoggerContextVO();IThrowableProxy proxy = eventObject.getThrowableProxy();Map<String, String> propertyMap = loggerContextVO.getPropertyMap();String active = propertyMap.get("ACTIVE");if (StringUtil.isEmpty(active) || !active.equals("prod")) {
// 只處理prod線上環境return;}// 獲取異常堆棧Throwable t = ((ThrowableProxy) proxy).getThrowable();String name = propertyMap.get("APPLICATION_NAME");String messageText = String.format(template_offline,StringUtil.isBlank(name) ? loggerContextVO.getName() : name,propertyMap,eventObject.getLevel(),DateTimeUtil.formatTime(new Date(), DateTimeUtil.DEFAULT_FORMAT_DATE_TIME),t.toString().length() <= 1000 ? t : t.toString().substring(0, 1000),Arrays.toString(t.getStackTrace()).length() <= 1888 ? Arrays.toString(t.getStackTrace()) : Arrays.toString(t.getStackTrace()).substring(0, 1888));System.out.println("-----" + messageText);monitor(messageText);} catch (Exception e) {addError("日志報警異常,異常原因:{}", e);}}protected abstract void monitor(String messageText);
}
append()方法中的 LoggingEvent參數對應的數據內容 如下圖:
我們首先定義一個WechatAlarm類,該類實現自 AlarmService類,并實現了 alarm()方法異步組裝參數并調用發送消息的接口:
具體實現代碼:
public class WechatAlarm implements WxAlarmService {private ExecutorService executorService;private String webHookUrl;public WechatAlarm() {}public WechatAlarm(String webHookUrl) {this.webHookUrl = webHookUrl;}private Message buildParam(String messageText) {Message message = new Message();//發送文本消息message.setMsgtype(MessageTypeEnum.text);TextMessage textMessage = new TextMessage();textMessage.setContent(messageText);//是否@所有人
// textMessage.setMentioned_list(Arrays.asList("@all"));JSONObject text = JSONObject.parseObject(JSONObject.toJSONString(textMessage));message.setText(text);return message;}@Overridepublic boolean alarm(String messageText) {ThreadPoolExecutorUtils.getCacheExecutorService().execute(() ->{try {// 在線程池中調用http接口發送微信消息Message message = buildParam(messageText);MessageManager.sendMessage(message, webHookUrl);} catch (Exception e) {e.printStackTrace();}});return true;}
}
日志打印格式的編寫如下圖:(也可以在logback的配置文件中配置)
此時我們需要讀取spring 中的配置文件,會有一個問題:
logback.xml的配置加載優先于spring的 .yml .properties 相關配置文件,如果我們需要讀取spring配置的相關變量配置信息就會加載不到。
所以我們需要通過logback的配置文件中配置來獲取spring里配置的相關變量信息。如下圖:
加載對應的參數項,如下圖:
log.error()方法的打印方式 如下:
log.error("異常描述的信息:大括弧 ", 異常的簡要信息, 整個異常);
log.error("數組越界異常:{} ", e.getMessage(), e);
如何添加企業微信機器人,如下圖:
總結
以上是生活随笔為你收集整理的springBoot 整合 logback异常告警 发送企业微信的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。