最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JAVA基于Slack實現(xiàn)異常日志報警詳解

 更新時間:2022年08月30日 14:06:58   作者:大樹Max  
這篇文章主要為大家介紹了JAVA基于Slack實現(xiàn)異常日志報警詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

一、功能介紹

在我們?nèi)粘i_發(fā)中,如果系統(tǒng)在線上環(huán)境上,發(fā)生異常,開發(fā)人員不能及時知曉來修復(fù),可能會造成重大的損失,因此后端服務(wù)中加入異常報警的功能是十分必要的,而開發(fā)一個功能全面的異常報警服務(wù),可能會花費較長的周期,今天給大家?guī)硪环N基于Slack實現(xiàn)異常日志報警的辦法。

實現(xiàn)邏輯:一般情況下,代碼中都會對會出現(xiàn)異常的地方,進行處理,最基本的就是打印日志,本文將實現(xiàn)在打印日志時,同時將異常信息發(fā)送到Slack頻道中,開發(fā)或運維人員創(chuàng)建Slack賬號,加入頻道,便可實時收到異常信息的告警。

二、Slack介紹

Slack 它是一種基于Web的實時通信工具,可作為臺式機/筆記本電腦、移動設(shè)備的單個應(yīng)用程序以及Web應(yīng)用程序使用。基本上,它是您的私人聊天和協(xié)作室。對于許多公司而言,它已取代電子郵件/私人論壇/聊天室成為主要的內(nèi)部基于文本的溝通渠道。

可以理解為它是聊天群組 + 大規(guī)模工具集成 + 文件整合 + 統(tǒng)一搜索。截至2014年底,Slack 已經(jīng)整合了電子郵件、短信、Google Drives、Twitter、Trello、Asana、GitHub 等 65 種工具和服務(wù),可以把各種碎片化的企業(yè)溝通和協(xié)作集中到一起。幾個重要的概念:

工作區(qū):相當去工作空間,用戶可以加入或者創(chuàng)建不同的工作區(qū),很多時候,工作區(qū)的名稱和URL將是公司名稱。

頻道:頻道可以區(qū)分為不同的團隊或者主題,也可以理解成相當于微信,頻道中的成員共享頻道中的信息。

三、前期準備

slack配置

  • 創(chuàng)建賬號,登錄,可以使用app或者用瀏覽器登錄網(wǎng)頁版
  • 創(chuàng)建自己的工作區(qū),還可以邀請其他人加入工作區(qū)。
  • 創(chuàng)建頻道,邀請同事加入,此時可以往頻道中發(fā)信息,加入頻道的人都可以看到信息

工作區(qū)添加應(yīng)用Incoming WebHook,選擇頻道,保存Webhook URL,后面將通過Webhook實現(xiàn)程序往頻道中發(fā)消息。

pom.xml

<dependencies>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.2</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.83</version>
    </dependency>
    <dependency>
        <groupId>commons-configuration</groupId>
        <artifactId>commons-configuration</artifactId>
        <version>1.10</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.10</version>
        <scope>test</scope>
    </dependency>
</dependencies>

四、具體實現(xiàn)

1.實現(xiàn)Slack發(fā)送消息

SlackUtil 給Slack發(fā)消息工具類

package com.yy.operation;
import com.yy.common.CommonThreadFactory;
import com.yy.common.ConnUtil;
import org.apache.commons.lang.StringUtils;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 * @author :Max
 * @date :Created in 2022/8/26 下午12:54
 * @description:
 */
public class SlackUtil {
    private static final Logger logger = Logger.getLogger(SlackUtil.class.getCanonicalName());
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private static final String SEND_USER_NAME ="運維機器人";
    private static int MAX_RETRY =3;
    /**
     * 線程池 拋棄策略DiscardPolicy:這種策略,會默默的把新來的這個任務(wù)給丟棄;不會得到通知
      */
    private static ExecutorService executor = new ThreadPoolExecutor(10,30,60,TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(200),new CommonThreadFactory("Slack"), new ThreadPoolExecutor.DiscardPolicy());
    private static String MSG_FORMAT ="payload='{'"channel": "{0}", "username": "{1}", "text": "{2}", "icon_emoji": ":ghost:"'}'" ;
    /**
     * 保存的Webhook URL ,需要初始化
     */
    private static String WEBHOOK_URL ;
    private static boolean SLACK_ABLE;
    public static void setSlackConfig(String webhookUrl){
        WEBHOOK_URL = webhookUrl;
        SLACK_ABLE = true;
    }
    /**
     * slack異步發(fā)消息,保證不能影響到主功能
      * @param channel
     * @param msg
     */
    public static void send(final String channel, final String msg){
        if(!SLACK_ABLE){
            return;
        }
        if(StringUtils.isBlank(msg)){
            return;
        }
        executor.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    SlackUtil.send(channel,sdf.format(System.currentTimeMillis())+"   "+msg,MAX_RETRY);
                } catch (Exception e) {
                    logger.log(Level.SEVERE, e.getMessage(), e);
                }
            }
        });
    }
    /**
     * 如果slask發(fā)消息失敗,會最多嘗試發(fā)三次,三次都失敗,會打印異常信息
      * @param channel
     * @param msg
     * @param retry
     * @throws Exception
     */
    public static void send(String channel, String msg, int retry) throws Exception {
        if(msg.indexOf(""")>=0 ||msg.indexOf("{")>=0 ||msg.indexOf("}")>=0){
            msg =msg.replace(""","'").replace("{","[").replace("}","]");
        }
        String payload = MessageFormat.format(MSG_FORMAT, channel,SEND_USER_NAME,msg);
        String result = ConnUtil.getContentByPostWithUrlencode(WEBHOOK_URL,payload);
        logger.info("result:"+result);
        if(StringUtils.isEmpty(result) ||!result.startsWith("ok")){
            --retry;
            if(retry>0){
                try {
                    TimeUnit.SECONDS.sleep(retry*5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                send(channel,msg,retry);
            }else{
                throw new Exception("Fail to send slack:"+result+"\nmsg:"+msg);
            }
        }
    }
}

向 webhook發(fā)起請求通過Urlencode

package com.yy.common;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 * @author :Max
 * @date :Created in 2022/8/26 下午1:44
 * @description:
 */
public class ConnUtil {
    private static final Logger logger = Logger.getLogger(ConnUtil.class.getCanonicalName());
    public static String getContentByPostWithUrlencode(String url,String msg){
        StringEntity entity = new StringEntity(msg, "UTF-8");
        entity.setContentEncoding("UTF-8");
        entity.setContentType(" application/x-www-form-urlencoded");
        HttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost request = new HttpPost(url);
        request.setEntity(entity);
        HttpResponse response = null;
        try {
            response = httpClient.execute(request);
            HttpEntity responseEntity = response.getEntity();
            if (responseEntity != null) {
                InputStream instream = responseEntity.getContent();
                BufferedReader reader = new BufferedReader(new InputStreamReader(instream));
                StringBuffer contents = new StringBuffer();
                String line = null;
                while ((line = reader.readLine()) != null) {
                    contents.append(line);
                    contents.append("\n");
                }
                return contents.toString();
            }
        } catch (Exception ex) {
            logger.log(Level.SEVERE, ex.getMessage(), ex);
        }
        return null;
    }
}

SlackUtil測試

package com.yy.test;
import com.yy.common.SlackChannelEnum;
import com.yy.operation.SlackUtil;
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
/**
 * @author :Max
 * @date :Created in 2022/8/28 下午2:37
 * @description:
 */
public class SlackTest {
    static {
        SlackUtil.setSlackConfig("https://hooks.slack.com/services/*******");
    }
    @Test
    public void test(){
        SlackUtil.send(SlackChannelEnum.EXCEPTION.channel,"test ~");
        try {
            TimeUnit.MINUTES.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Assert.assertTrue(true);
    }
}

發(fā)送成功,可以在頻道中看到信息

2.重寫打印日志類

常見異常打日志處理

public class LoggerTest {
    private static final Logger logger = Logger.getLogger(LoggerTest.class.getCanonicalName());
    @Test
    public void test() {
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            logger.log(Level.SEVERE, e.getMessage(), e);
        }
    }
}

重寫封裝打印日志的方法

package com.yy.operation;
import com.yy.common.SlackChannelEnum;
import org.apache.commons.lang.StringUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.text.MessageFormat;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
 * @author  Max
 * @date :Created in 2022/8/4 下午5:14
 * @description:
 */
public class CommonLogger {
    private Logger logger;
    private CommonLogger(String className) {
        logger = Logger.getLogger(className);
    }
    private static String SERVER;
    private static String EXCEPTION_ALARM_FORMAT = "EXCEPTION 發(fā)生異常!\n環(huán)境 :{0}\n信息 :{1}\n詳情 :{2}";
    private static String WARNING_ALARM_FORMAT = "WARNING 發(fā)生告警!\n環(huán)境 :{0}\n信息 :{1}";
    private static String SEVERE_ALARM_FORMAT = "SEVERE 發(fā)生告警!\n環(huán)境 :{0}\n信息 :{1}";
    private static String LOG_ALARM_FORMAT = "LOG 發(fā)生告警!\n環(huán)境 :{0}\n信息 :{1}";
    private static String USER_BEHAVIOR_FORMAT = "CUSTOMER \n環(huán)境 :{0}\n信息 :{1}";
    static {
        try{
            InetAddress ip4 = Inet4Address.getLocalHost();
            SERVER = ip4.getHostAddress();
        }catch (Exception e){
            SERVER ="undefined server";
        }
    }
    public static CommonLogger getLogger(String name) {
        return new CommonLogger(name);
    }
    /**
     * Print exception information, send slack
     *
     * @param level
     * @param msg
     * @param e
     */
    public void log(Level level, String msg, Throwable e) {
        if(StringUtils.isBlank(msg)){
            return;
        }
        msg =dolog(level,msg, e);
        msg = MessageFormat.format(EXCEPTION_ALARM_FORMAT, SERVER, formatMsg(msg), getErrmessage(e));
        SlackUtil.send(SlackChannelEnum.EXCEPTION.channel, msg);
    }
    /**
     * Print user behavior information, send slack
     *
     * @param msg
     */
    public void userBehaviorInfo(String msg) {
        if(StringUtils.isBlank(msg)){
            return;
        }
        msg =dolog(Level.INFO,msg);
        msg = MessageFormat.format(USER_BEHAVIOR_FORMAT, SERVER, formatMsg(msg));
        SlackUtil.send(SlackChannelEnum.EXCEPTION.channel, msg);
    }
    public String formatMsg(String msg){
        StringBuilder source =new StringBuilder(logger.getName());
        msg=transferMsgSource(source,msg);
        return source.toString()+" "+msg;
    }
    /**
     * Print warning severe information, send slack
     *
     * @param msg
     */
    public void severe(String msg) {
        if(StringUtils.isBlank(msg)){
            return;
        }
        msg = dolog(Level.SEVERE,msg);
        msg = MessageFormat.format(SEVERE_ALARM_FORMAT, SERVER, formatMsg(msg));
        SlackUtil.send(SlackChannelEnum.EXCEPTION.channel, msg);
    }
    /**
     * Print warning severe information, send slack
     *
     * @param msg
     */
    public void warning(String msg) { 
        if(StringUtils.isBlank(msg)){
            return;
         }
        msg = dolog(Level.WARNING,msg);
        msg = MessageFormat.format(WARNING_ALARM_FORMAT, SERVER, formatMsg(msg));
        SlackUtil.send(SlackChannelEnum.EXCEPTION.channel, msg);
    }
    /**
     * Print warning log information, send slack
     *
     * @param msg
     */
    public void log(Level severe, String msg) {
        if(StringUtils.isBlank(msg)){
            return;
        }
        msg =dolog(severe,msg);
        msg = MessageFormat.format(LOG_ALARM_FORMAT, SERVER, formatMsg(msg));
        SlackUtil.send(SlackChannelEnum.EXCEPTION.channel, msg);
    }
    public static String getErrmessage(Throwable t) {
        return getThrowable(t);
    }
    public void info(String msg) {
        dolog(Level.INFO,msg);
    }
    public void fine(String msg) {
        logger.fine(msg);
    }
    public void setLevel(Level level) {
        logger.setLevel(level);
    }
    public String dolog(Level level, String msg) {
        return dolog(level,msg,null);
    }
    /**
     *
      * @param level
     * @param msg
     * @param thrown
     * @return msg="["+currentThread.getName()+"] "+a.getMethodName()+" "+msg;
     */
    public String dolog(Level level, String msg, Throwable thrown) {
        LogRecord lr = new LogRecord(level, msg);
        lr.setLevel(level);
        if(thrown!=null){
            lr.setThrown(thrown);
        }
        Thread currentThread = Thread.currentThread();
        StackTraceElement[] temp=currentThread.getStackTrace();
        StackTraceElement a=(StackTraceElement)temp[3];
        lr.setThreadID((int) currentThread.getId());
        lr.setSourceClassName(logger.getName());
        lr.setSourceMethodName(a.getMethodName());
        lr.setLoggerName(logger.getName());
        logger.log(lr);
        return "["+currentThread.getName()+"] "+a.getMethodName()+" "+msg;
    }
    public static String getThrowable(Throwable e) {
        String throwable = "";
        if (e != null) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            pw.println();
            e.printStackTrace(pw);
            pw.close();
            throwable = sw.toString();
        }
        return throwable;
    }
    public static String transferMsgSource(StringBuilder source,String msg){
        if(msg.indexOf(" ")>0){
            String threadName = msg.substring(0,msg.indexOf(" "))+ " ";
            msg=msg.substring(threadName.length());
            source.insert(0,threadName);
            if(msg.indexOf(" ")>0) {
                String method = msg.substring(0, msg.indexOf(" "));
                source.append( "." + method);
                msg = msg.substring(method.length()+1);
            }
        }
        return msg;
    }
}
package com.yy.operation;
import java.text.MessageFormat;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggerUtil {
   private static Logger curLogger = Logger.getLogger(LoggerUtil.class.getCanonicalName());
   private static ConcurrentHashMap<String, CommonLogger> loggers = new ConcurrentHashMap<String, CommonLogger>();
   public static CommonLogger getLogger(Class<?> clazz) {
      String className = clazz.getCanonicalName();
      CommonLogger logger = loggers.get(className);
      if (logger == null) {
         logger = CommonLogger.getLogger(className);
         curLogger.fine(MessageFormat.format("Register logger for {0}", className));
         loggers.put(className, logger);
      }
      return logger;
   }
}

測試日志類

定義日志類時發(fā)生改變,調(diào)用出的代碼無需更改,以較小的代價,集成異常報警功能

public class LoggerTest {
    private static final Logger logger = Logger.getLogger(LoggerTest.class.getCanonicalName());
    @Test
    public void test() {
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            logger.log(Level.SEVERE, e.getMessage(), e);
        }
    }
}

測試結(jié)果,頻道中出現(xiàn)打印的異常信息,方便開發(fā)運維人員定位

五、優(yōu)化擴展想法

  • 可以不僅僅實現(xiàn)打印異常日志,也可以打印用戶的一些關(guān)鍵行為,如充值等,頻道可以設(shè)置多個,發(fā)送不同主題的消息
  • 可以優(yōu)化線程池
  • 如果開發(fā)人員不能及時查看slack,也可以集成電子郵件,Slack中可以添加mailclark應(yīng)用(單獨收費),經(jīng)過配置后,發(fā)動頻道中的信息,可以自動郵件發(fā)送給任意郵箱,接受者無需創(chuàng)建slack賬號。具體配置可參考鏈接。

其他代碼

package com.yy.common;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * @author :Max
 * @date :Created in 2022/8/26 下午1:51
 * @description:
 */
public class CommonThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String threadNamePrefix;
    private final String nameSpecific;
    private final boolean isDaemon;
    public CommonThreadFactory(String nameSpecific) {
        this(nameSpecifihttps://juejin.cn/post/7136858841756467230#heading-4c, false);
    }
    public CommonThreadFactory(String nameSpecific, boolean isDaemon) {
        SecurityManager s = System.getSecurityManager();
        this.group = (s != null) ? s.getThreadGroup() :
                Thread.currentThread().getThreadGroup();
        this.threadNamePrefix = "eg-pool-" + poolNumber.getAndIncrement() + "-thread";
        this.nameSpecific = nameSpecific;
        this.isDaemon = isDaemon;
    }
    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r, String.format("%s-%d-%s",
                this.threadNamePrefix, threadNumber.getAndIncrement(), this.nameSpecific), 0);
        t.setDaemon(isDaemon);
        t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}
public enum SlackChannelEnum {
    EXCEPTION("#test-example");
    public String channel;
    SlackChannelEnum(String channel) {
        this.channel = channel;
    }
}

以上就是JAVA基于Slack實現(xiàn)異常日志報警詳解的詳細內(nèi)容,更多關(guān)于JAVA Slack異常日志報警的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot整合BootStrap實戰(zhàn)

    SpringBoot整合BootStrap實戰(zhàn)

    這篇文章主要介紹了SpringBoot整合BootStrap實戰(zhàn),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • MyBatis 中  type-aliases-package的作用(簡化類型映射)

    MyBatis 中  type-aliases-package的作用(簡化類型映射)

    MyBatis中type-aliases-package的作用是簡化類型映射,通過指定包路徑,MyBatis會自動掃描該包下的所有類并為這些類生成類型別名,減少XML配置文件中的冗長代碼,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • SpringBoot獲取Request和Response方法代碼解析

    SpringBoot獲取Request和Response方法代碼解析

    這篇文章主要介紹了SpringBoot獲取Request和Response方法代碼解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-11-11
  • java無鎖hashmap原理與實現(xiàn)詳解

    java無鎖hashmap原理與實現(xiàn)詳解

    本文主要介紹了java無鎖hashmap原理與實現(xiàn),大家參考使用吧
    2014-01-01
  • java應(yīng)用cpu占用過高問題分析及解決方法

    java應(yīng)用cpu占用過高問題分析及解決方法

    這篇文章主要介紹了java應(yīng)用cpu占用過高問題分析及解決方法,具有一定參考價值,需要的朋友可以參考下。
    2017-09-09
  • Spring Boot中集成各種日志框架Logback、Log4j2和Java Util Logging的步驟和示例代碼

    Spring Boot中集成各種日志框架Logback、Log4j2和Java Util 

    這篇文章主要介紹了Spring Boot中集成各種日志框架Logback、Log4j2和Java Util Logging,通過實例代碼介紹了集成Logback、Log4j2和Java Util Logging的基本步驟,你可以根據(jù)自己的需求進行配置和擴展,以滿足更復(fù)雜的日志需求,需要的朋友可以參考下
    2023-11-11
  • java 讀取文件方法的總結(jié)

    java 讀取文件方法的總結(jié)

    這篇文章主要介紹了java 讀取文件方法的總結(jié)的相關(guān)資料,這里提供文件讀取5種方法并附實例,需要的朋友可以參考下
    2017-08-08
  • Java  隊列 Queue 用法實例詳解

    Java 隊列 Queue 用法實例詳解

    本文實例講述了Java內(nèi)置隊列類Queue用法,分享給大家供大家參考
    2017-04-04
  • jpa使用manyToOne(opntional=true)踩過的坑及解決

    jpa使用manyToOne(opntional=true)踩過的坑及解決

    這篇文章主要介紹了jpa使用manyToOne(opntional=true)踩過的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 五分鐘帶你快速學(xué)習(xí)Spring?IOC

    五分鐘帶你快速學(xué)習(xí)Spring?IOC

    這篇文章主要給大家介紹了關(guān)于如何通過五分鐘快速學(xué)習(xí)Spring?IOC的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2022-03-03

最新評論

乌什县| 光山县| 鄯善县| 杭锦旗| 佳木斯市| 滦平县| 南澳县| 嵊泗县| 宣化县| 腾冲县| 恩施市| 琼海市| 盐津县| 德江县| 酒泉市| 桦甸市| 洪洞县| 库伦旗| 台东市| 太谷县| 疏附县| 仁布县| 安平县| 秀山| 饶阳县| 普洱| 仁怀市| 佛山市| 庆安县| 克山县| 中宁县| 南川市| 井冈山市| 德清县| 淳安县| 集安市| 崇信县| 广汉市| 吴桥县| 津市市| 中阳县|