springboot全局异常消息捕获+钉钉自定义消息推送

SpringBoot全局异常处理与钉钉消息推送实践
本文介绍了如何在SpringBoot项目中配置全局异常捕获,并结合钉钉机器人进行异常消息推送。详细讲解了创建钉钉机器人、设置IP白名单、处理HTTP请求以发送钉钉消息的过程,以及在项目中遇到的问题和解决方案。此外,还分享了相关配置代码和自定义工具类的使用。

因项目需要,需要配置spring的全局异常消息捕获,并进行钉钉消息推送,使用钉钉的机器人进行消息群推送,以下是钉钉机器人的配置使用

关于钉钉机器人异常消息推送流程备忘录_bigbig_bug的博客-优快云博客开发的速卖通对接奇门自定义接口,数据安全出塔项目,因为项目需要部署到聚石塔中,但异常信息查看比较麻烦,不利于排查错误信息,所以考虑使用钉钉的机器人进行异常消息推送。首先,你需要一个钉钉账号,其次你需要拉一个群比如我选择创建一个项目群创建完后有一条消息推送,这个是钉钉的机器人,点击它的头像,选择更多机器人我选择的是自定义选择ip地址段,这里的设置相当于ip白名单,只有地址段的ip才被允许推送消息完成配置后会给一个链接地址,请复制此地址进行存储,这个就是...https://blog.youkuaiyun.com/bigbig_bug/article/details/118958687?spm=1001.2014.3001.5501

在进行钉钉消息推送的时候,本地测试消息推送无异常,但项目已启动进行异常消息推送的时候,总是报错:

com.dingtalk.api.DingTalkClient

查询了很多资料,排查了很多地方,并没有找到具体原因,本身使用的是钉钉的sdk,已经加载到项目中了,使用的方式按照以下文章进行的配置:

springboot加载第三方jar包淘宝sdk进行打包编译_bigbig_bug的博客-优快云博客开发工具ide本地开发springBoot电商项目,需要加载淘宝的sdk,以下有几种方式加载https://blog.youkuaiyun.com/bigbig_bug/article/details/117689524?spm=1001.2014.3001.5501

因为项目本身加载了众多淘宝系的sdk,经推测可能是众多com.top包下的类及方法重复,使程序并不明白具体的指向,但仅仅是推测并未找到具体原因,因项目进度问题,无法继续深入研究,所以在查看了钉钉sdk的底层代码后,具体使用自定义的方法进行钉钉的消息推送

钉钉的消息推送是post请求,前面是关于钉钉的请求源码分析,后面是我仿照其源码编写的请求工具类,可直接使用

上面这行代码在 

package com.dingtalk.api;包下的 DefaultDingTalkClient类中

红色框中圈起来的是重点代码,

fullUrl:这个参数是请求的url地址,这个是你配置钉钉机器人给的,url需要是全的后面带token
jsonParams:这个是里面是请求的参数和具体内容,具体可以debug走一遍,注意是要转为json的格式
connectTimeout readTimeout 这两个参数具体值类中有配置,固定参数

 上面这个方法的作用其实是拼接了请求的格式,即
ctype== "application/json;charset=UTF-8" ;

然后把请求的参数和具体内容转换为字节数组,然后调用dopost方法

从这段代码中可以看出来,进行了钉钉的消息推送并获取返回值,以上方法就是钉钉消息推送的具体代码内容,仿照其内容进行编写即可实现

 从上面的代码看出,使用的httpclient进行的请求和响应,所以首先要加载其jar包

<dependency>
			<groupId>commons-httpclient</groupId>
			<artifactId>commons-httpclient</artifactId>
			<version>3.1</version>
</dependency>

 使用pom进行配置

spring或者springboot的全局异常捕获,可以使用注解的方式进行捕获,但spring需要记得配置包扫描

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class ExceptionHandle {


    @ExceptionHandler(value=Exception.class)
    public Boolean exceptionHandler(Exception e){
        //全局捕获异常,并进行钉钉异常消息推送
        DingTalkUtils.dingTalkSendException(e);
        return false;
    }
}

 DingTalkUtils是我自定义的工具类:自定义关键词可更换,具体在钉钉机器人上设置.钉钉的消息可以分为多种,支持text,支持markdown,支持url,这里我使用的是最简单text,方便,只是为了推送个异常消息,如果有需要可以自行更改





import org.apache.commons.lang.StringUtils;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;

/**
 * @description: 钉钉机器人
 * @author:
 * @date: 2021-09-09 10:03
 * @version: 1.0
 */
public class DingTalkUtils {
    private static boolean ignoreSSLCheck = true;
    private static boolean ignoreHostCheck = true;
    private static String URL="替换自己的url";
    private static String Ctype = "application/json;charset=UTF-8";

    /**
     * @description:钉钉机器人异常信息推送
     * @attention:监控报警是我自定义的关键词,使用此关键词钉钉才会推送,这个是钉钉机器人的自定义配置
     * @date: 2021/7/15 12:19
     * @param: e
     * @return: void
     */
    public static void dingTalkSendException(Exception e){
        //得到异常棧的首个元素
        StackTraceElement  stackTrace = e.getStackTrace()[0];
        StringBuilder sb=new StringBuilder();
        sb.append("监控报警报错信息:"+e.getMessage()+"\n")
                .append("报错文件:"+stackTrace.getFileName()+"\n")
                .append("报错行号:"+stackTrace.getLineNumber()+"\n")
                .append("报错方法:"+stackTrace.getMethodName()+"\n")
                .append("报错Class:"+stackTrace.getClass()+"\n")
                .append("报错ClassName:"+stackTrace.getClassName());

        Map<String, Object> jsonParams = new HashMap();
        Map<String,Boolean> atMap=new HashMap<>();
        atMap.put("isAtAll",true);
        jsonParams.put("at",atMap);
        Map<String,String> contextMap=new HashMap<>();
        contextMap.put("content",sb.toString());
        jsonParams.put("text",contextMap);
        jsonParams.put("msgtype","text");
        String body = JsonUtils.objectToJson(jsonParams);
        byte[] content = body.getBytes(StandardCharsets.UTF_8);
        try {
            HttpResponseData httpResponseData = _doPost(URL, Ctype, content, 15000, 30000, (Map) null, (Proxy) null);
            System.out.println("钉钉机器人异常通知返回信息:"+JsonUtils.objectToJson(httpResponseData.getBody()));
        }catch (Exception exception){
            exception.printStackTrace();
        }

        // System.out.println("钉钉机器人异常通知返回信息:"+response.getBody());
    }


    public static  HttpResponseData _doPost(String url, String ctype, byte[] content, int connectTimeout, int readTimeout, Map<String, String> headerMap, Proxy proxy) throws Exception {
        HttpURLConnection conn = null;
        OutputStream out = null;
        String rsp = null;
        HttpResponseData data = new HttpResponseData();
        try {
            conn = getConnection(new URL(url), "POST", ctype, headerMap, proxy);
            conn.setConnectTimeout(connectTimeout);
            conn.setReadTimeout(readTimeout);
            out = conn.getOutputStream();
            out.write(content);
            rsp = getResponseAsString(conn);
            data.setBody(rsp);
            Map<String, List<String>> headers = conn.getHeaderFields();
            data.setHeaders(headers);
        } finally {
            if (out != null) {
                out.close();
            }

            if (conn != null) {
                conn.disconnect();
            }

        }

        return data;
    }
    private static HttpURLConnection getConnection(URL url, String method, String ctype, Map<String, String> headerMap, Proxy proxy) throws IOException {
        HttpURLConnection conn = null;
        if (proxy == null) {
            conn = (HttpURLConnection)url.openConnection();
        } else {
            conn = (HttpURLConnection)url.openConnection(proxy);
        }

        if (conn instanceof HttpsURLConnection) {
            HttpsURLConnection connHttps = (HttpsURLConnection)conn;
            if (ignoreSSLCheck) {
                try {
                    SSLContext ctx = SSLContext.getInstance("TLS");
                    ctx.init((KeyManager[])null, new TrustManager[]{new WebV2Utils.TrustAllTrustManager()}, new SecureRandom());
                    connHttps.setSSLSocketFactory(ctx.getSocketFactory());
                    connHttps.setHostnameVerifier(new HostnameVerifier() {
                        public boolean verify(String hostname, SSLSession session) {
                            return true;
                        }
                    });
                } catch (Exception var8) {
                    throw new IOException(var8.toString());
                }
            } else if (ignoreHostCheck) {
                connHttps.setHostnameVerifier(new HostnameVerifier() {
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                });
            }

            conn = connHttps;
        }

        ((HttpURLConnection)conn).setRequestMethod(method);
        ((HttpURLConnection)conn).setDoInput(true);
        ((HttpURLConnection)conn).setDoOutput(true);
        if (headerMap != null && headerMap.get("TOP_HTTP_DNS_HOST") != null) {
            ((HttpURLConnection)conn).setRequestProperty("Host", (String)headerMap.get("TOP_HTTP_DNS_HOST"));
        } else {
            ((HttpURLConnection)conn).setRequestProperty("Host", url.getHost());
        }

        ((HttpURLConnection)conn).setRequestProperty("Accept", "text/xml,text/javascript");
        ((HttpURLConnection)conn).setRequestProperty("User-Agent", "top-sdk-java");
        ((HttpURLConnection)conn).setRequestProperty("Content-Type", ctype);
        if (headerMap != null) {
            Iterator var9 = headerMap.entrySet().iterator();

            while(var9.hasNext()) {
                Map.Entry<String, String> entry = (Map.Entry)var9.next();
                if (!"TOP_HTTP_DNS_HOST".equals(entry.getKey())) {
                    ((HttpURLConnection)conn).setRequestProperty((String)entry.getKey(), (String)entry.getValue());
                }
            }
        }

        return (HttpURLConnection)conn;
    }

    protected static String getResponseAsString(HttpURLConnection conn) throws IOException {
        String charset = getResponseCharset(conn.getContentType());
        if (conn.getResponseCode() < 400) {
            String contentEncoding = conn.getContentEncoding();
            return "gzip".equalsIgnoreCase(contentEncoding) ? getStreamAsString(new GZIPInputStream(conn.getInputStream()), charset) : getStreamAsString(conn.getInputStream(), charset);
        } else {
            if (conn.getResponseCode() == 400) {
                InputStream error = conn.getErrorStream();
                if (error != null) {
                    return getStreamAsString(error, charset);
                }
            }

            throw new IOException(conn.getResponseCode() + " " + conn.getResponseMessage());
        }
    }

    public static String getStreamAsString(InputStream stream, String charset) throws IOException {
        try {
            Reader reader = new InputStreamReader(stream, charset);
            StringBuilder response = new StringBuilder();
            char[] buff = new char[1024];
            boolean var5 = false;

            int read;
            while((read = reader.read(buff)) > 0) {
                response.append(buff, 0, read);
            }

            String var6 = response.toString();
            return var6;
        } finally {
            if (stream != null) {
                stream.close();
            }

        }
    }
    public static String getResponseCharset(String ctype) {
        String charset = "UTF-8";
        if (!StringUtils.isEmpty(ctype)) {
            String[] params = ctype.split(";");
            String[] var3 = params;
            int var4 = params.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                String param = var3[var5];
                param = param.trim();
                if (param.startsWith("charset")) {
                    String[] pair = param.split("=", 2);
                    if (pair.length == 2 && !StringUtils.isEmpty(pair[1])) {
                        charset = pair[1].trim();
                    }
                    break;
                }
            }
        }

        return charset;
    }

}

这个是相应的实体,如果不需要返回请求是否成功,具体看情况是否配置

 
import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * @description:
 * @author:  
 * @date: 2021-09-08 10:37
 * @version: 1.0
 */
public class HttpResponseData implements Serializable {

    private static final long serialVersionUID = -6975537945058755923L;
    private String body;
    private Map<String, List<String>> headers;

    public HttpResponseData() {
    }

    public String getBody() {
        return this.body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public Map<String, List<String>> getHeaders() {
        return this.headers;
    }

    public void setHeaders(Map<String, List<String>> headers) {
        this.headers = headers;
    }

}

这个是上面请求会使用到工具类i,其实可以和上面的请求工具类合并到一起,但是我懒,就没改。。。。。。

package com.irs.util;


import javax.net.ssl.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * @description:
 * @author:  
 * @date: 2021-09-09 14:52
 * @version: 1.0
 */
public class WebV2Utils {


    private WebV2Utils() {
    }

    public static class TrustAllTrustManager implements X509TrustManager {
        public TrustAllTrustManager() {
        }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }
    }
}

以上就可以实现,项目的全局异常捕获,比较适合项目上线了,有错误可以及时通知到相关人员

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值