구현 논리: 일반적인 상황에서 코드는 예외가 발생한 예외를 처리합니다. 이 기사에서는 로그를 인쇄할 때, 또한 비정상적인 정보는 Slack 채널로 전송됩니다. 개발 또는 운영 및 유지 관리 담당자는 Slack 계정을 생성하고 채널에 가입하여 실시간으로 비정상적인 정보에 대한 알림을 받을 수 있습니다.
Slack 데스크톱/노트북 컴퓨터, 모바일 기기에서 단일 애플리케이션으로, 웹 애플리케이션으로 사용할 수 있는 웹 기반 실시간 커뮤니케이션 도구입니다. 기본적으로 이곳은 개인 채팅 및 협업 공간입니다. 많은 회사에서 이메일/비공개 포럼/채팅룸을 기본 내부 텍스트 기반 커뮤니케이션 채널로 대체했습니다.
채팅 그룹 + 대규모 도구 통합 + 파일 통합 + 통합 검색으로 이해될 수 있습니다. 2014년 말 현재 Slack은 이메일, 문자 메시지, Google 드라이브, Twitter, Trello, Asana, GitHub 등 65개의 도구와 서비스를 통합하여 다양한 단편화된 기업 커뮤니케이션과 협업을 통합할 수 있습니다. 몇 가지 중요한 개념:
Workspace: 꽤 작업 공간이므로 사용자는 다양한 작업 공간에 참여하거나 만들 수 있습니다. 작업 공간의 이름과 URL은 회사 이름이 됩니다.
Channel: 채널은 서로 다른 팀이나 주제로 나눌 수 있으며, 채널 내 구성원이 채널에서 정보를 공유하는 WeChat과 동일하게 이해될 수도 있습니다.
계정을 만들고 로그인하세요. 앱을 사용하거나 브라우저로 웹 버전에 로그인할 수 있습니다.
나만의 워크스페이스를 만들고, 초대도 가능합니다. 다른 사람들이 작업 공간에 참여하도록 합니다.
채널을 만들고 동료를 초대하여 채널에 메시지를 보낼 수 있으며, 채널에 참여하는 모든 사람이 메시지를 볼 수 있습니다.
작업 공간에 애플리케이션 추가 Incoming WebHook을 선택하세요. 채널을 등록하고 Webhook URL을 저장한 후 팔로우하세요. 프로그램은 Webhook을 통해 구현되어 채널에 메시지를 보냅니다.
<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>
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); } } } }
공통 예외 로깅 처리
package 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; } }
인쇄 로그 캡슐화 방법 재작성
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); } }
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; } }
5. 최적화 및 확장 아이디어
인쇄만 가능하지 않습니다 예외 로그를 인쇄할 뿐만 아니라 재충전 등과 같은 사용자의 일부 주요 동작도 인쇄합니다. 여러 채널을 설정하여 다양한 테마의 메시지를 보낼 수 있습니다.
예 스레드 풀을 최적화하세요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을 기반으로 JAVA에서 이상 로그 알람을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!