JAVA implementa una alarma de registro anormal basada en Slack

1. Introducción a la función

En nuestro desarrollo diario, si hay una anomalía en el entorno en línea del sistema, el desarrollador no puede saber a tiempo para repararlo, lo que puede causar grandes pérdidas. Por lo tanto, es muy necesario agregar la función de alarma anormal en la parte posterior. -Finalizar el servicio y desarrollar una función Un servicio integral de alarmas anormales puede tomar un largo período de tiempo. Hoy, les traeré un método para realizar alarmas de registros anormales basado en Slack.

Lógica de implementación: en circunstancias normales, el código se ocupará del lugar anormal. Lo más básico es imprimir el registro. Este artículo se dará cuenta de que cuando se imprime el registro, la información anormal se enviará al canal de Slack al mismo tiempo. Personal de desarrollo u operación y mantenimiento Cree una cuenta de Slack, únase a un canal y reciba alertas en tiempo real de información anómala.

2. Introducción a Slack

Slack es una herramienta de comunicación en tiempo real basada en la web disponible como una aplicación única para computadoras de escritorio/portátiles, dispositivos móviles y una aplicación web. Básicamente, es su sala privada de chat y colaboración. Para muchas empresas, ha reemplazado el correo electrónico, los foros privados y las salas de chat como el principal canal interno de comunicación basado en texto. Se puede entender que es un grupo de chat + integración de herramientas a gran escala + integración de archivos + búsqueda unificada . A finales de 2014, Slack ha integrado 65 herramientas y servicios como correo electrónico, SMS, Google Drive, Twitter, Trello, Asana, GitHub, etc., que pueden reunir varias comunicaciones y colaboraciones empresariales fragmentadas. Varios conceptos importantes:

Espacios de trabajo : en lugar de ir a los espacios de trabajo, los usuarios pueden unirse o crear diferentes espacios de trabajo, muchas veces el nombre y la URL del espacio de trabajo será el nombre de la empresa.

Canales : los canales se pueden dividir en diferentes equipos o temas, y también se pueden entender como equivalentes a WeChat, donde los miembros de un canal comparten información en el canal.

3. Preparación preliminar

configuración de holgura

  1. Cree una cuenta, inicie sesión, puede usar la aplicación o iniciar sesión en la versión web con un navegador
  2. Cree su propio espacio de trabajo e invite a otros a su espacio de trabajo.
  3. Cree un canal, invite a colegas a unirse, puede enviar información al canal en este momento, y todos los que se unen al canal pueden ver la información
  4. Agregue la aplicación WebHook entrante al espacio de trabajo , seleccione el canal, guarde la URL del Webhook y luego envíe mensajes al canal a través del programa de implementación del Webhook.imagen.png

imagen.png

imagen.png

imagen.png

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>
复制代码

Cuarto, la implementación específica

1. Implementar Slack para enviar mensajes

Clase de herramienta de mensajes SlackUtil para 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);
            }
        }
    }


}
复制代码

Hacer una solicitud al webhook a través de Urlencode

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;
    }

}
复制代码

Prueba de 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);
    }
}
复制代码

Envio con exito, puedes ver la informacion en el canal

imagen.png

2. Vuelva a escribir la clase de registro de impresión

Procesamiento de registros de excepciones comunes

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);
        }
    }
}
复制代码

Anular el método de encapsular el registro de impresión

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;
   }
}
复制代码

clase de registro de prueba

Cuando se cambia la clase de registro, no es necesario cambiar el código llamado y la función de alarma anormal se integra a un costo pequeño.

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);
        }
    }
}
复制代码

Resultados de la prueba, información anormal impresa en el canal, que es conveniente para el personal de desarrollo, operación y mantenimiento para localizar

imagen.png

Quinto, optimizar la idea de expansión

  1. No solo puede imprimir registros anormales, sino también algunos comportamientos clave de los usuarios, como recargas, etc. Se pueden configurar múltiples canales para enviar mensajes con diferentes temas.
  2. El grupo de subprocesos se puede optimizar
  3. Si los desarrolladores no pueden ver Slack a tiempo, también pueden integrar el correo electrónico. La aplicación Mailclark (que se cobra por separado) se puede agregar a Slack. Después de la configuración, la información en el canal de activación se puede enviar automáticamente a cualquier buzón y el destinatario no necesita crear una cuenta de slack. Para una configuración específica, consulte el enlace .

¡Todos son bienvenidos a dar sugerencias de optimización e intercambiar mucho!

otro codigo

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(nameSpecific, 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;
    }
}
复制代码

Supongo que te gusta

Origin juejin.im/post/7136858841756467230
Recomendado
Clasificación