1、自定义appender,先在logback.xml中配置
<appender name="SendErrorMsgAppender"
class="com.xxx.xxx.xxx.common.SendErrorMsgAppender"></appender>
2、配置Appender引用,这步不能少,少了Appender不启作用
<logger name="root" level="info" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC"/>
<appender-ref ref="SendErrorMsgAppender"/>
</logger>
<logger name="com.xxx" level="debug"
additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC"/>
<appender-ref ref="SendErrorMsgAppender"/>
</logger>
3、extends UnsynchronizedAppenderBase 用于异步处理,不阻塞主线程;例如自定义发送日志到消息队列。
需要在钉钉群创建机器人,并在电脑客户端获取access_token
@Slf4j
public class SendErrorMsgAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
private static final String url = "https://oapi.dingtalk.com/robot/send?access_token=%s";
private HttpHeaders headers;
public SendErrorMsgAppender() {
headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
}
@Override
public void append(ILoggingEvent event) {
if (event.getLevel() == Level.ERROR) {
SysProperties sysProperties = getApplicationContext().getBean("sysProperties", SysProperties.class);
//没有启用
if (!sysProperties.getWaringDingdingEnable()) {
return;
}
IThrowableProxy iThrowableProxy = event.getThrowableProxy();
StringBuilder sb = new StringBuilder();
if (iThrowableProxy != null && iThrowableProxy instanceof ThrowableProxy) {
ThrowableProxy throwableProxy = (ThrowableProxy) iThrowableProxy;
Throwable throwable = throwableProxy.getThrowable();
String throwableMsg = throwable.getMessage();
StackTraceElementProxy[] stackTraceElementProxy = iThrowableProxy.getStackTraceElementProxyArray();
//获取服务器Ip,告知哪台服务器抛异常
String ip = Context.getContext().getIp();
if (null != ip) {
sb.append("服务器:").append(ip).append("\n");
}
sb.append(event.getMessage()).append("\n");
if (StringUtils.isNotEmpty(throwableMsg)) {
sb.append(throwableMsg).append("\n");
}
int i = 0;
for (StackTraceElementProxy proxy : stackTraceElementProxy) {
//只打印40行的堆栈
if (i < 40) {
sb.append(proxy.getSTEAsString()).append("\n");
} else {
break;
}
i++;
}
} else {
sb.append(event.getMessage());
}
String msg = sb.toString();
if (StringUtils.isNotEmpty(msg)) {
sendMsgToDingDing(msg);
}
}
}
/**
* @param msg
* @return
*/
private Map<String, Object> sendMsgToDingDing(String msg) {
RestTemplate restTemplate = getApplicationContext().getBean("restTemplate", RestTemplate.class);
SysProperties sysProperties = getApplicationContext().getBean("sysProperties", SysProperties.class);
ObjectMapper objectMapper = getApplicationContext().getBean("jacksonObjectMapper", ObjectMapper.class);
Text text = new Text();
text.setContent(msg);
DdMsgBody msgBody = DdMsgBody.builder().msgtype("text").text(text).build();
String json = null;
try {
json = objectMapper.writeValueAsString(msgBody);
} catch (JsonProcessingException e) {
//不记录日志,有可能死循环
//log.error("error", e);
}
if (null == json) {
return null;
}
HttpEntity<String> formEntity = new HttpEntity<>(json, headers);
String formatUrl = String.format(url, sysProperties.getWaringDingdingToken());
Map<String, Object> result = restTemplate.postForObject(formatUrl, formEntity, Map.class);
return result;
}
private ApplicationContext getApplicationContext() {
return ApplicationContextUtils.getApplicationContext();
}
@Builder
@Data
private static class DdMsgBody {
private String msgtype;
private Text text;
}
@Data
private class Text {
private String content;
}
}