您好,欢迎访问一九零五行业门户网

JAVA基于Slack怎么实现异常日志报警

一、功能介绍实现逻辑:一般情况下,代码中都会对会出现异常的地方,进行处理,最基本的就是打印日志,本文将实现在打印日志时,同时将异常信息发送到slack频道中,开发或运维人员创建slack账号,加入频道,便可实时收到异常信息的告警。
二、slack介绍slack 它是一种基于web的实时通信工具,可作为台式机/笔记本电脑、移动设备的单个应用程序以及web应用程序使用。基本上,它是您的私人聊天和协作室。对于许多公司而言,它已取代电子邮件/私人论坛/聊天室成为主要的内部基于文本的沟通渠道。
可以理解为它是聊天群组 + 大规模工具集成 + 文件整合 + 统一搜索。截至2014年底,slack 已经整合了电子邮件、短信、google drives、twitter、trello、asana、github 等 65 种工具和服务,可以把各种碎片化的企业沟通和协作集中到一起。几个重要的概念:
工作区:相当去工作空间,用户可以加入或者创建不同的工作区,很多时候,工作区的名称和url将是公司名称。
频道:频道可以区分为不同的团队或者主题,也可以理解成相当于微信,频道中的成员共享频道中的信息。
三、前期准备slack配置创建账号,登录,可以使用app或者用浏览器登录网页版
创建自己的工作区,还可以邀请其他人加入工作区。
创建频道,邀请同事加入,此时可以往频道中发信息,加入频道的人都可以看到信息
工作区添加应用incoming webhook,选择频道,保存webhook url,后面将通过webhook实现程序往频道中发消息。
pom.xml<dependencies> <dependency> <groupid>org.apache.httpcomponents</groupid> <artifactid>httpclient</artifactid> <version>4.5.2</version> </dependency> <dependency> <groupid>com.alibaba</groupid> <artifactid>fastjson</artifactid> <version>1.2.83</version> </dependency> <dependency> <groupid>commons-configuration</groupid> <artifactid>commons-configuration</artifactid> <version>1.10</version> </dependency> <dependency> <groupid>junit</groupid> <artifactid>junit</artifactid> <version>4.10</version> <scope>test</scope> </dependency></dependencies>
四、具体实现1.实现slack发送消息slackutil 给slack发消息工具类package com.yy.operation;import com.yy.common.commonthreadfactory;import com.yy.common.connutil;import org.apache.commons.lang.stringutils;import java.text.messageformat;import java.text.simpledateformat;import java.util.concurrent.*;import java.util.logging.level;import java.util.logging.logger;/** * @author :max * @date :created in 2022/8/26 下午12:54 * @description: */public class slackutil { private static final logger logger = logger.getlogger(slackutil.class.getcanonicalname()); private static simpledateformat sdf = new simpledateformat("yyyy-mm-dd hh:mm:ss"); private static final string send_user_name ="运维机器人"; private static int max_retry =3; /** * 线程池 抛弃策略discardpolicy:这种策略,会默默的把新来的这个任务给丢弃;不会得到通知 */ private static executorservice executor = new threadpoolexecutor(10,30,60,timeunit.milliseconds,new arrayblockingqueue<runnable>(200),new commonthreadfactory("slack"), new threadpoolexecutor.discardpolicy()); private static string msg_format ="payload='{'"channel": "{0}", "username": "{1}", "text": "{2}", "icon_emoji": ":ghost:"'}'" ; /** * 保存的webhook url ,需要初始化 */ private static string webhook_url ; private static boolean slack_able; public static void setslackconfig(string webhookurl){ webhook_url = webhookurl; slack_able = true; } /** * slack异步发消息,保证不能影响到主功能 * @param channel * @param msg */ public static void send(final string channel, final string msg){ if(!slack_able){ return; } if(stringutils.isblank(msg)){ return; } executor.submit(new runnable() { @override public void run() { try { slackutil.send(channel,sdf.format(system.currenttimemillis())+" "+msg,max_retry); } catch (exception e) { logger.log(level.severe, e.getmessage(), e); } } }); } /** * 如果slask发消息失败,会最多尝试发三次,三次都失败,会打印异常信息 * @param channel * @param msg * @param retry * @throws exception */ public static void send(string channel, string msg, int retry) throws exception { if(msg.indexof(""")>=0 ||msg.indexof("{")>=0 ||msg.indexof("}")>=0){ msg =msg.replace(""","'").replace("{","[").replace("}","]"); } string payload = messageformat.format(msg_format, channel,send_user_name,msg); string result = connutil.getcontentbypostwithurlencode(webhook_url,payload); logger.info("result:"+result); if(stringutils.isempty(result) ||!result.startswith("ok")){ --retry; if(retry>0){ try { timeunit.seconds.sleep(retry*5); } catch (interruptedexception e) { e.printstacktrace(); } send(channel,msg,retry); }else{ throw new exception("fail to send slack:"+result+"\nmsg:"+msg); } } }}
向 webhook发起请求通过urlencodepackage com.yy.common;import org.apache.http.httpentity;import org.apache.http.httpresponse;import org.apache.http.client.httpclient;import org.apache.http.client.methods.httppost;import org.apache.http.entity.stringentity;import org.apache.http.impl.client.httpclientbuilder;import java.io.bufferedreader;import java.io.inputstream;import java.io.inputstreamreader;import java.util.logging.level;import java.util.logging.logger;/** * @author :max * @date :created in 2022/8/26 下午1:44 * @description: */public class connutil { private static final logger logger = logger.getlogger(connutil.class.getcanonicalname()); public static string getcontentbypostwithurlencode(string url,string msg){ stringentity entity = new stringentity(msg, "utf-8"); entity.setcontentencoding("utf-8"); entity.setcontenttype(" application/x-www-form-urlencoded"); httpclient httpclient = httpclientbuilder.create().build(); httppost request = new httppost(url); request.setentity(entity); httpresponse response = null; try { response = httpclient.execute(request); httpentity responseentity = response.getentity(); if (responseentity != null) { inputstream instream = responseentity.getcontent(); bufferedreader reader = new bufferedreader(new inputstreamreader(instream)); stringbuffer contents = new stringbuffer(); string line = null; while ((line = reader.readline()) != null) { contents.append(line); contents.append("\n"); } return contents.tostring(); } } catch (exception ex) { logger.log(level.severe, ex.getmessage(), ex); } return null; }}
slackutil测试package com.yy.test;import com.yy.common.slackchannelenum;import com.yy.operation.slackutil;import org.junit.assert;import org.junit.test;import java.util.concurrent.timeunit;/** * @author :max * @date :created in 2022/8/28 下午2:37 * @description: */public class slacktest { static { slackutil.setslackconfig("https://hooks.slack.com/services/*******"); } @test public void test(){ slackutil.send(slackchannelenum.exception.channel,"test ~"); try { timeunit.minutes.sleep(1); } catch (interruptedexception e) { e.printstacktrace(); } assert.asserttrue(true); }}
发送成功,可以在频道中看到信息
2.重写打印日志类常见异常打日志处理public class loggertest { private static final logger logger = logger.getlogger(loggertest.class.getcanonicalname()); @test public void test() { try { int i = 1 / 0; } catch (exception e) { logger.log(level.severe, e.getmessage(), e); } }}

重写封装打印日志的方法package com.yy.operation;import com.yy.common.slackchannelenum;import org.apache.commons.lang.stringutils;import java.io.printwriter;import java.io.stringwriter;import java.net.inet4address;import java.net.inetaddress;import java.text.messageformat;import java.util.logging.level;import java.util.logging.logrecord;import java.util.logging.logger;/** * @author max * @date :created in 2022/8/4 下午5:14 * @description: */public class commonlogger { private logger logger; private commonlogger(string classname) { logger = logger.getlogger(classname); } private static string server; private static string exception_alarm_format = "exception 发生异常!\n环境 :{0}\n信息 :{1}\n详情 :{2}"; private static string warning_alarm_format = "warning 发生告警!\n环境 :{0}\n信息 :{1}"; private static string severe_alarm_format = "severe 发生告警!\n环境 :{0}\n信息 :{1}"; private static string log_alarm_format = "log 发生告警!\n环境 :{0}\n信息 :{1}"; private static string user_behavior_format = "customer \n环境 :{0}\n信息 :{1}"; static { try{ inetaddress ip4 = inet4address.getlocalhost(); server = ip4.gethostaddress(); }catch (exception e){ server ="undefined server"; } } public static commonlogger getlogger(string name) { return new commonlogger(name); } /** * print exception information, send slack * * @param level * @param msg * @param e */ public void log(level level, string msg, throwable e) { if(stringutils.isblank(msg)){ return; } msg =dolog(level,msg, e); msg = messageformat.format(exception_alarm_format, server, formatmsg(msg), geterrmessage(e)); slackutil.send(slackchannelenum.exception.channel, msg); } /** * print user behavior information, send slack * * @param msg */ public void userbehaviorinfo(string msg) { if(stringutils.isblank(msg)){ return; } msg =dolog(level.info,msg); msg = messageformat.format(user_behavior_format, server, formatmsg(msg)); slackutil.send(slackchannelenum.exception.channel, msg); } public string formatmsg(string msg){ stringbuilder source =new stringbuilder(logger.getname()); msg=transfermsgsource(source,msg); return source.tostring()+" "+msg; } /** * print warning severe information, send slack * * @param msg */ public void severe(string msg) { if(stringutils.isblank(msg)){ return; } msg = dolog(level.severe,msg); msg = messageformat.format(severe_alarm_format, server, formatmsg(msg)); slackutil.send(slackchannelenum.exception.channel, msg); } /** * print warning severe information, send slack * * @param msg */ public void warning(string msg) { if(stringutils.isblank(msg)){ return; } msg = dolog(level.warning,msg); msg = messageformat.format(warning_alarm_format, server, formatmsg(msg)); slackutil.send(slackchannelenum.exception.channel, msg); } /** * print warning log information, send slack * * @param msg */ public void log(level severe, string msg) { if(stringutils.isblank(msg)){ return; } msg =dolog(severe,msg); msg = messageformat.format(log_alarm_format, server, formatmsg(msg)); slackutil.send(slackchannelenum.exception.channel, msg); } public static string geterrmessage(throwable t) { return getthrowable(t); } public void info(string msg) { dolog(level.info,msg); } public void fine(string msg) { logger.fine(msg); } public void setlevel(level level) { logger.setlevel(level); } public string dolog(level level, string msg) { return dolog(level,msg,null); } /** * * @param level * @param msg * @param thrown * @return msg="["+currentthread.getname()+"] "+a.getmethodname()+" "+msg; */ public string dolog(level level, string msg, throwable thrown) { logrecord lr = new logrecord(level, msg); lr.setlevel(level); if(thrown!=null){ lr.setthrown(thrown); } thread currentthread = thread.currentthread(); stacktraceelement[] temp=currentthread.getstacktrace(); stacktraceelement a=(stacktraceelement)temp[3]; lr.setthreadid((int) currentthread.getid()); lr.setsourceclassname(logger.getname()); lr.setsourcemethodname(a.getmethodname()); lr.setloggername(logger.getname()); logger.log(lr); return "["+currentthread.getname()+"] "+a.getmethodname()+" "+msg; } public static string getthrowable(throwable e) { string throwable = ""; if (e != null) { stringwriter sw = new stringwriter(); printwriter pw = new printwriter(sw); pw.println(); e.printstacktrace(pw); pw.close(); throwable = sw.tostring(); } return throwable; } public static string transfermsgsource(stringbuilder source,string msg){ if(msg.indexof(" ")>0){ string threadname = msg.substring(0,msg.indexof(" "))+ " "; msg=msg.substring(threadname.length()); source.insert(0,threadname); if(msg.indexof(" ")>0) { string method = msg.substring(0, msg.indexof(" ")); source.append( "." + method); msg = msg.substring(method.length()+1); } } return msg; }}
package com.yy.operation;import java.text.messageformat;import java.util.concurrent.concurrenthashmap;import java.util.logging.level;import java.util.logging.logger;public class loggerutil { private static logger curlogger = logger.getlogger(loggerutil.class.getcanonicalname()); private static concurrenthashmap<string, commonlogger> loggers = new concurrenthashmap<string, commonlogger>(); public static commonlogger getlogger(class<?> clazz) { string classname = clazz.getcanonicalname(); commonlogger logger = loggers.get(classname); if (logger == null) { logger = commonlogger.getlogger(classname); curlogger.fine(messageformat.format("register logger for {0}", classname)); loggers.put(classname, logger); } return logger; }}
测试日志类定义日志类时发生改变,调用出的代码无需更改,以较小的代价,集成异常报警功能
public class loggertest { private static final logger logger = logger.getlogger(loggertest.class.getcanonicalname()); @test public void test() { try { int i = 1 / 0; } catch (exception e) { logger.log(level.severe, e.getmessage(), e); } }}

测试结果,频道中出现打印的异常信息,方便开发运维人员定位
五、优化扩展想法可以不仅仅实现打印异常日志,也可以打印用户的一些关键行为,如充值等,频道可以设置多个,发送不同主题的消息
可以优化线程池
如果开发人员不能及时查看slack,也可以集成电子邮件,slack中可以添加mailclark应用(单独收费),经过配置后,发动频道中的信息,可以自动邮件发送给任意邮箱,接受者无需创建slack账号。具体配置可参考链接。
其他代码package com.yy.common;import java.util.concurrent.threadfactory;import java.util.concurrent.atomic.atomicinteger;/** * @author :max * @date :created in 2022/8/26 下午1:51 * @description: */public class commonthreadfactory implements threadfactory { private static final atomicinteger poolnumber = new atomicinteger(1); private final threadgroup group; private final atomicinteger threadnumber = new atomicinteger(1); private final string threadnameprefix; private final string namespecific; private final boolean isdaemon; public commonthreadfactory(string namespecific) { this(namespecifihttps://juejin.cn/post/7136858841756467230#heading-4c, false); } public commonthreadfactory(string namespecific, boolean isdaemon) { securitymanager s = system.getsecuritymanager(); this.group = (s != null) ? s.getthreadgroup() : thread.currentthread().getthreadgroup(); this.threadnameprefix = "eg-pool-" + poolnumber.getandincrement() + "-thread"; this.namespecific = namespecific; this.isdaemon = isdaemon; } @override public thread newthread(runnable r) { thread t = new thread(group, r, string.format("%s-%d-%s", this.threadnameprefix, threadnumber.getandincrement(), this.namespecific), 0); t.setdaemon(isdaemon); t.setpriority(thread.norm_priority); return t; }}
public enum slackchannelenum { exception("#test-example"); public string channel; slackchannelenum(string channel) { this.channel = channel; }}
以上就是java基于slack怎么实现异常日志报警的详细内容。
其它类似信息

推荐信息