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 += "×tamp=" + 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的专栏-优快云博客