logback错误日志告警推送

logback错误日志告警推送

背景

  项目上线后,对于线上问题及关键业务未能及时推送给相关人员。如果没有基础的监控服务,可以自己针对异常日志进行消息推送。

技术方案

 目前主流的技术方案有以下几种:
 1.ELK采集分析,将异常结果发送给用户
 2.接入Cat告警系统,(在日志写到磁盘前,就可以通过拦截手段拿到日志并分析日志的级别,或者在异常处理时就触发日志警告)
 3.通过javaAgent skywaking使用探针技术收集
 这3项告警都需要搭建一套完整的告警系统,实现成本较高,因此我们选用logback对接基础服务平台实现消息推送。(企业微信或者其他OA系统)

实现方案

1.继承Appender

 logback 通过appender将日志分析并写入到磁盘上,那么我们可以在appender获取到日志内容,并判断是否需要发送告警推送。
 Appender 两个基类  UnsynchronizedAppenderBase、AppenderBase
 在Cat告警系统中也对应实现了具体的Appender CatLogbackAppender 
public class CatLogbackAppender extends AppenderBase<ILoggingEvent> {

	@Override
	protected void append(ILoggingEvent event) {
		try {
			boolean isTraceMode = Cat.getManager().isTraceMode();
			Level level = event.getLevel();
			if (level.isGreaterOrEqual(Level.ERROR)) {
				logError(event);
			} else if (isTraceMode) {
				logTrace(event);
			}
		} catch (Exception ex) {
			throw new LogbackException(event.getFormattedMessage(), ex);
		}
	}

	private void logError(ILoggingEvent event) {
		ThrowableProxy info = (ThrowableProxy) event.getThrowableProxy();
		if (info != null) {
			Throwable exception = info.getThrowable();

			Object message = event.getFormattedMessage();
			if (message != null) {
				Cat.logError(String.valueOf(message), exception);
			} else {
				Cat.logError(exception);
			}
		}
	}

	private void logTrace(ILoggingEvent event) {
		String type = "Logback";
		String name = event.getLevel().toString();
		Object message = event.getFormattedMessage();
		String data;
		if (message instanceof Throwable) {
			data = buildExceptionStack((Throwable) message);
		} else {
			data = event.getFormattedMessage().toString();
		}

		ThrowableProxy info = (ThrowableProxy) event.getThrowableProxy();
		if (info != null) {
			data = data + '\n' + buildExceptionStack(info.getThrowable());
		}

		Cat.logTrace(type, name, "0", data);
	}

	private String buildExceptionStack(Throwable exception) {
		if (exception != null) {
			StringWriter writer = new StringWriter(2048);
			exception.printStackTrace(new PrintWriter(writer));
			return writer.toString();
		} else {
			return "";
		}
	}

}
@Slf4j
public abstract class AlarmSendAppender extends UnsynchronizedAppenderBase<LoggingEvent> {


    private static final String template_offline = "服务名: %s \n日志等级: %s \n异常时间: %s \n异常描述: %s \n异常详细信息: %s";

    @Override
    protected void append(LoggingEvent loggingEvent) {
        List<Filter<LoggingEvent>> copyOfAttachedFiltersList = getCopyOfAttachedFiltersList();

        Boolean contains = false;

        for (Filter<LoggingEvent> filter : copyOfAttachedFiltersList) {
            FilterReply filterReply = filter.decide(loggingEvent);
            if (filterReply == FilterReply.ACCEPT) {
                contains = true;
                break;
            }
        }
        if (contains) {
            // 获取用户在日志中输出的语句,一般涵盖异常上下文
            LoggerContextVO loggerContextVO = loggingEvent.getLoggerContextVO();
            Map<String, String> propertyMap = loggerContextVO.getPropertyMap();
            Boolean alarmOpen = Boolean.valueOf(propertyMap.get("alarmOpen"));
            //开关关闭不推送告警
            if (!alarmOpen) {
                log.info("错误日志告警消息已关不进行推送");
                return;
            }
            String alarmReceivers = propertyMap.get("msgAlarmReceivers");
            IThrowableProxy proxy = loggingEvent.getThrowableProxy();

            // 获取异常堆栈
            Throwable t = ((ThrowableProxy) proxy).getThrowable();
//           原文链接:https://blog.csdn.net/weixin_45423451/article/details/123203736
            //发送第三放告警消息
            String messageText = String.format(template_offline,
                    loggerContextVO.getName(),
                    loggingEvent.getLevel(),
                    DateUtils.format(new Date(),"yyyy-MM-dd HH:mm:sss"),
                    t.toString().length() <= 1000 ? t : t.toString().substring(0, 1000),
                    Arrays.toString(t.getStackTrace()).length() <= 2000 ? Arrays.toString(t.getStackTrace()) : Arrays.toString(t.getStackTrace()).substring(0, 1888));
            System.out.println("-----" + messageText);
            String[] receivers = alarmReceivers.split(",");
            //进行参数组装
            AlarmMessage alarmMessage = new AlarmMessage();
            alarmMessage.setMessageText(messageText);
            alarmMessage.setPropertyMap(propertyMap);
            alarmMessage.setReceiverList(Arrays.asList(receivers));
            //发送消息告警
            sendAlarmMessage(alarmMessage);


        }

    }


    /**
     * 模板设计方法,可以对接各种平台进行抽象方法推送告警消息
     * @param alarmMessage
     */
    protected abstract void  sendAlarmMessage(AlarmMessage alarmMessage);

}
/**
 * <p>
 *    告警信息
 * </p>
 *
 * @author: 
 * @date: 2023-01-17
 * @desc:
 */
@Data
public class AlarmMessage  {


    /**
     * 告警标题
     */
    private String title;

    /**
     * 告警内容
     */
    private String messageText;

    /**
     * 接收者
     */
    private List<String> receiverList;

    /**
     * 变量map
     */
    private Map<String, String> propertyMap;


    /**
     * 消息类型 1文本消息
     */
    private String messageType;

}

2.具体对接服务

可以对接各类消息或者企业微信实现推送,logback的SMTPAppender已经邮件推送。

/**
 * <p>
 *  对接消息中心告警
 * </p>
 *
 * @author: 
 * @date: 2023-01-17
 * @desc:
 */
@Slf4j
public class MsgCenterAlarmSendAppender extends AlarmSendAppender{


    @Override
    protected void sendAlarmMessage(AlarmMessage alarmMessage) {
        //日志比较频繁,使用多线程处理
         ExecutorService executorService = Executors.newCachedThreadPool();
         executorService.execute(new Runnable() {
            @Override
            public void run() {
                  //todo 具体处理
                   System.out.println("=========告警信息发送结果:" + result);
                }
       }
       });
        }
    }
}

3.logback配置

   1.  logback-spring.xml的加载顺序早于springboot的application.yml 配置文件当然读不到application.yml文件中的值了。通过springProperty标签来引用。
   2. 配置具体的appender
   3. 在root中引入

logback-spring,xml

接收错误日志进行告警
引入appender

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值