logback 钉钉机器人通知 LogDingDingAppender

本文详细介绍了一种利用钉钉机器人进行日志报警的配置方法,通过自定义Logback Appender,实现了根据日志级别向钉钉群发送警报信息的功能。文章涵盖了配置文件的编写、异常过滤、消息格式化及HTTPS请求的发送过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package com.wjj.application.config;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;

/**
 * 钉钉logback appender
 *
 * @author hank
 * @apiNote
 *          <!--注入环境参数 源码参考: SpringBootJoranConfigurator-->
<springProperty scope="context" name="springProfilesActive" source="spring.profiles.active"/>
<!--类路径-->
<appender name="LogDingDing"  class="com.wjj.application.config.LogDingDingAppender">
    <!--输出格式 %ex{12}:指的是输出12行异常栈-->
    <pattern>${springProfilesActive:-${env:-null}} %level\n%m\n%logger\n%ex{12}</pattern>
    <!--钉钉机器人token-->
    <dingdingToken>xxxxxxxxxxx</dingdingToken>
    <!--机器人签名密钥 新版需要签名推送-->
    <secret>xxxxxxxxxxx</secret>
    <!--排除的异常类,用逗号分隔-->
    <excludeThrowableClasses>com.wjj.application.exception.CommonException</excludeThrowableClasses>
    <!--机器人的推送级别-->
    <level>WARN</level>
</appender>

<!-- 日志输出级别 -->
<root level="info">
    <!--指定环境开启(LogDingDing) dev,test,uat,local(dev|test|uat|local) 或者 !prod(如果要实现!prod && !local 使用两层springProfile) 源码参考: SpringBootJoranConfigurator -->
    <springProfile name="dev,test,uat,local">
        <appender-ref ref="LogDingDing" />
    </springProfile>
    <appender-ref ref="FILE" />
    <!-- 生产环境将请stdout,testfile去掉 -->
    <appender-ref ref="STDOUT" />
</root>
 */
public class LogDingDingAppender extends UnsynchronizedAppenderBase<LoggingEvent> {
    /**
     * 默认的输出格式, %ex{12}:可以通过后面的数字指定异常栈输出的行数
     */
    private String pattern = "%level\n%m\n%logger\n%ex";
    private PatternLayout layout;
    private String profilesActive = "unknown";
    private String dingdingToken;
    private Level nowLevel = Level.ERROR;
    private String secret;
    /**
     * 排除的类集合用逗号分隔
     */
    private String excludeThrowableClasses;

    @Override
    protected void append(LoggingEvent eventObject) {
        try {
//            System.out.println("dingding nowLevel:"+nowLevel + ", profilesActive:"+profilesActive);
            if(eventObject.getLevel().isGreaterOrEqual(nowLevel)) {
                if(excludeThrowableClasses != null && eventObject.getThrowableProxy() != null){
                    if(excludeThrowableClasses.contains(eventObject.getThrowableProxy().getClassName())){
                        return;
                    }
                }
                String toMsg = layout.doLayout(eventObject);
/*                if (null != eventObject.getThrowableProxy() && null != eventObject.getThrowableProxy().getStackTraceElementProxyArray() && eventObject.getThrowableProxy().getStackTraceElementProxyArray().length > 0) {
                    if(null != eventObject.getThrowableProxy().getMessage()){
                        toMsg += "EMsg: "+eventObject.getThrowableProxy().getMessage() + "\n\n";
                    }
                    toMsg += eventObject.getThrowableProxy().getStackTraceElementProxyArray()[0].getSTEAsString();
                }*/
                toDingDing("https://oapi.dingtalk.com/robot/send?access_token="+getDingdingToken(), toMsg);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void start() {
        PatternLayout patternLayout = new PatternLayout();
        patternLayout.setContext(context);
        patternLayout.setPattern(getPattern());
//        patternLayout.setOutputPatternAsHeader(outputPatternAsHeader);
        patternLayout.start();
        this.layout = patternLayout;
        super.start();
    }

    private void toDingDing(String webHookToken, String contentMsg) throws Exception{

        String textMsg="{\"msgtype\": \"text\",\"text\": {\"content\": " + JSON.toJSONString(contentMsg) + "}}";
        //加签
        if(secret != null){
            Long timestamp = System.currentTimeMillis();
            String stringToSign = timestamp + "\n" + secret;
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
            byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
            String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)),"UTF-8");
            webHookToken += "&timestamp=" + timestamp + "&sign=" + sign;
        }

        try(CloseableHttpClient httpclient = HttpClients.createDefault()) {
            HttpPost httppost = new HttpPost(webHookToken);
            httppost.addHeader("Content-Type", "application/json; charset=utf-8");

            StringEntity se = new StringEntity(textMsg, "utf-8");
            httppost.setEntity(se);
            //            System.out.println("dingding req:"+JSON.toJSONString(httppost));
            CloseableHttpResponse closeableHttpResponse = httpclient.execute(httppost);
//            System.out.println("dingding res:"+JSON.toJSONString(closeableHttpResponse));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String getProfilesActive() {
        return profilesActive;
    }

    public void setProfilesActive(String profilesActive) {
        System.out.println("dingding setProfilesActive:"+profilesActive);
        this.profilesActive = profilesActive;
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }

    public String getPattern() {
        return pattern;
    }

    public PatternLayout getLayout() {
        return layout;
    }

    public void setLayout(PatternLayout layout) {
        this.layout = layout;
    }

    public String getDingdingToken() {
        return dingdingToken;
    }

    public void setDingdingToken(String dingdingToken) {
        this.dingdingToken = dingdingToken;
    }
    public void setLevel(String level) {
        this.nowLevel = Level.toLevel(level, Level.ERROR);
    }

    public void setExcludeThrowableClasses(String excludeThrowableClasses) {
        this.excludeThrowableClasses = excludeThrowableClasses;
    }

    public String getSecret() {
        return secret;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }
}

再在logbac-spring.xml加入配置:

<!--注入环境参数-->
    <springProperty scope="context" name="springProfile" source="spring.profiles.active"/>
    <!--类路径-->
    <appender name="LogDingDing"  class="com.wjj.application.config.LogDingDingAppender">
        <!--输出格式 %ex{12}:指的是输出12行异常栈-->
        <pattern>${springProfilesActive:-${env:-null}} %level\n%m\n%logger\n%ex{12}</pattern>
        <!--钉钉机器人token-->
        <dingdingToken>xxxxxxxxxxx</dingdingToken>
        <!--机器人签名密钥 新版需要签名推送-->
        <secret>xxxxxxxxxxx</secret>
        <!--排除的异常类,用逗号分隔-->
        <excludeThrowableClasses>com.wjj.application.exception.CommonException</excludeThrowableClasses>
        <!--机器人的推送级别-->
        <level>WARN</level>
    </appender>

<!-- 日志输出级别 -->
<root level="info">
    <!--指定环境开启(LogDingDing) dev,test,uat,local(dev|test|uat|local) 或者 !prod(如果要实现!prod && !local 使用两层springProfile) 源码参考: SpringBootJoranConfigurator -->
    <springProfile name="dev,test,uat,local">
        <appender-ref ref="LogDingDing" />
    </springProfile>
</root>

关于配置pattern可以参考 logback pattern配置格式全面 内部 PatternLayout 源码_huang007guo的专栏-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值