就在2026年7月28日的早上,也即是刚才,群里有很多的告警信息,但是我淡定的很,因为核心的业务,并没有告警。那我就知道,可以让团队的人上班后再看了。
这就是核心业务都有监控的好处,只要核心模块没有告警信息,你就可以放心了。
因为其他的业务模块有告警,不会出大事的,顶多是小问题。
我这边是在早期就做了一个规定,核心模块一定要监控好,一出事必须马上知道。
而我采用的方式就是,一个核心模块一个钉钉监控群。比如:
- 订单的一个告警群;
- 库存的一个告警群;
并通过规范化日志打印,方便监控。我这边的日志,必须是规范化的。因此我提供了一个结构化的日志打印工具,代码超级短:
importcom.alibaba.fastjson2.JSON;importlombok.extern.slf4j.Slf4j;importorg.slf4j.Logger;importjava.util.LinkedHashMap;importjava.util.Map;/** * 结构化日志工具类 * * 使用示例: * // 方式1:传入logger(推荐,类名正确) * StructuredLog.info(log) * .userId("123") * .shopId(456) * .log(); * * // 方式2:不传logger(简便,类名为StructuredLog) * StructuredLog.info() * .userId("123") * .shopId(456) * .log(); * * @author linsongbin */@Slf4jpublicclassStructuredLog{privatefinalMap<String,Object>fields=newLinkedHashMap<>();privatefinalLogLevellevel;privatefinalLoggerlogger;privateStringmessage;privateThrowablethrowable;privateenumLogLevel{INFO,WARN,ERROR,DEBUG}privateStructuredLog(LogLevellevel,Loggerlogger){this.level=level;this.logger=logger;}/** * 创建INFO级别日志(使用调用方logger) */publicstaticStructuredLoginfo(Loggerlogger){returnnewStructuredLog(LogLevel.INFO,logger);}/** * 创建INFO级别日志(使用StructuredLog内置logger) */publicstaticStructuredLoginfo(){returnnewStructuredLog(LogLevel.INFO,log);}/** * 创建WARN级别日志(使用调用方logger) */publicstaticStructuredLogwarn(Loggerlogger){returnnewStructuredLog(LogLevel.WARN,logger);}/** * 创建WARN级别日志(使用StructuredLog内置logger) */publicstaticStructuredLogwarn(){returnnewStructuredLog(LogLevel.WARN,log);}/** * 创建ERROR级别日志(使用调用方logger) */publicstaticStructuredLogerror(Loggerlogger){returnnewStructuredLog(LogLevel.ERROR,logger);}/** * 创建ERROR级别日志(使用StructuredLog内置logger) */publicstaticStructuredLogerror(){returnnewStructuredLog(LogLevel.ERROR,log);}/** * 创建DEBUG级别日志(使用调用方logger) */publicstaticStructuredLogdebug(Loggerlogger){returnnewStructuredLog(LogLevel.DEBUG,logger);}/** * 创建DEBUG级别日志(使用StructuredLog内置logger) */publicstaticStructuredLogdebug(){returnnewStructuredLog(LogLevel.DEBUG,log);}/** * 设置日志消息 */publicStructuredLogmessage(Stringmessage){this.message=message;returnthis;}/** * 设置异常信息 */publicStructuredLogexception(Throwablethrowable){this.throwable=throwable;returnthis;}/** * 通用的键值对设置方法 */publicStructuredLogput(Stringkey,Objectvalue){if(key!=null&&value!=null){fields.put(key,value);}returnthis;}// ============ 业务字段快捷方法 ============/** * 用户ID */publicStructuredLoguserId(StringuserId){returnput("userId",userId);}/** * 用户名称 */publicStructuredLoguserName(StringuserName){returnput("userName",userName);}/** * 门店ID */publicStructuredLogshopId(IntegershopId){returnput("shopId",shopId);}/** * 门店名称 */publicStructuredLogshopName(StringshopName){returnput("shopName",shopName);}/** * 单据ID */publicStructuredLogdocsId(LongdocsId){returnput("docsId",docsId);}/** * 单据编号 */publicStructuredLogdocsNo(StringdocsNo){returnput("docsNo",docsNo);}/** * 模块编码 */publicStructuredLogmoduleCode(StringmoduleCode){returnput("moduleCode",moduleCode);}/** * 模块名称 */publicStructuredLogmoduleName(StringmoduleName){returnput("moduleName",moduleName);}/** * 事件编码 */publicStructuredLogeventCode(StringeventCode){returnput("eventCode",eventCode);}/** * 事件名称 */publicStructuredLogeventName(StringeventName){returnput("eventName",eventName);}/** * 业务场景 */publicStructuredLogscenario(Stringscenario){returnput("scenario",scenario);}/** * 状态(如: success/fail/processing) */publicStructuredLogstatus(Stringstatus){returnput("status",status);}/** * 数量(如: 记录数/处理数/失败数) */publicStructuredLogcount(Integercount){returnput("count",count);}/** * 耗时(单位: 毫秒) */publicStructuredLogcostTime(LongcostTime){returnput("costTime",costTime);}/** * 输出日志 */publicvoidlog(){StringlogContent=buildLogContent();switch(level){caseINFO:if(throwable!=null){logger.info(logContent,throwable);}else{logger.info(logContent);}break;caseWARN:if(throwable!=null){logger.warn(logContent,throwable);}else{logger.warn(logContent);}break;caseERROR:if(throwable!=null){logger.error(logContent,throwable);}else{logger.error(logContent);}break;caseDEBUG:if(throwable!=null){logger.debug(logContent,throwable);}else{logger.debug(logContent);}break;}}/** * 构建日志内容 */privateStringbuildLogContent(){StringBuildersb=newStringBuilder();if(message!=null&&!message.isEmpty()){sb.append(message).append(" || ");}// 使用JSON格式输出,便于日志平台解析sb.append(JSON.toJSONString(fields));returnsb.toString();}}使用的时候,也很简单:
StructuredLog.error(log).message("门店过时未下单").exception(e).scenario("系统自动下单").moduleCode("order").moduleName("订货单").eventCode("autoCreateOrder").eventName("系统自动下单").shopName("xxx门店").docsNo(docsNo).put("shopId",1).log();这个结构化工具是有模块编码的,专门用于监控的。一旦某个关键字模块出现了error日志。我这边对应的钉钉群,就会有告警信息。
这个日志结构化工具类,你可以直接拿去用,我已经用了它,用了三年了,很实用的。
在这里我想告诉大家几件事:
第一,如果你是技术组长/技术经理/技术负责人的,一定要做到一件事情,核心模块实时监控到位,有问题,马上处理,降低损失,否则,你可能位置不保;
第二,日志打印一定要结构化,不要让团队的人随意乱打;
第三,不要使用一个统一的监控大群,而是分模块。也即是,一个核心模块,一个告警群;
程序员保护自己,从实时监控开始。