Intercept the application error log and send it to the nail group | Java development practice

Posted by JasperBosch on Fri, 28 Jan 2022 18:36:00 +0100

Opening

Now all applications need to monitor or alarm the log. Now the common practice is to collect the log by EKL, and then display the content and alarm strategy by Grafana. If the project architecture is relatively simple (single application) and you don't want to rely on so many middleware, here is a simple way to do it~

Inherit UnsynchronizedAppenderBase

Springboot integrates logback by default, so customizing the Appender is very simple. Just inherit the AppenderBase class.

Let's take a look at the implementation of Appender on AppenderBase

UnsynchronizedAppenderBase. It can be seen from the name that it is asynchronous, ordinary and unlocked. It is similar to AppenderBase, except that the derived Appender implementation class needs to handle thread synchronization by itself.

demonstration

Define a SendErrorMsgAppender

This class should inherit UnsynchronizedAppenderBase and implement its abstract methods. It doesn't need to make its threads synchronous and asynchronous manually, so as to avoid business operation rollback caused by errors.

/**
 * UnsynchronizedAppenderBase It is used for asynchronous processing. It does not block the main thread, intercepts the error log, and sends it to the nail group
 */
@Getter
@Setter
public class SendErrorMsgAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
    // ILoggingEvent contains the contents of the log
    Layout<ILoggingEvent> layout;

    //Custom configuration
    String printString;

    @Override
    public void start() {
        //Here you can make some initialization judgments. For example, layout cannot be null,
        if (layout == null) {
            addWarn("Layout was not defined");
        }
        //Or the initialization connection when writing to the database or redis, etc
        super.start();
    }

    @Override
    public void stop() {
        //Release relevant resources, such as database connection, redis thread pool, etc
        if (!isStarted()) {
            return;
        }
        super.stop();
    }

    @Override
    public void append(ILoggingEvent event) {
        if (event.getLevel() == Level.ERROR) {
            try {
                var isEnableSendLog = "true".equals(SpringUtil.environment(PropertiesConstant.IS_ENABLE_SEND_LOG));
                if (isEnableSendLog) {
                    //Get the server Ip and tell which server throws an exception
                    var ip = InetAddress.getLocalHost().getHostAddress();
                    var message = ip + "  " + layout.doLayout(event);
                    sendMsgToDingDing(message);
                }
            } catch (UnknownHostException ignored) {
                addWarn("Get server ip fail");
            }
        }
    }
    /**
    * Logic for sending log messages to pegs
    **/
    private void sendMsgToDingDing(String msg) {
        Text text = new Text();
        text.setContent(msg);
        DdMsgBody msgBody = DdMsgBody.builder().msgtype("text").text(text).build();

        Long timestamp = System.currentTimeMillis();
        String secret = SpringUtil.environment(PropertiesConstant.OAPI_DINGTALK_SECRET);
        var charsetName = "UTF-8";
        // Take the timestamp+"\n" + key as the signature string
        String stringToSign = timestamp + "\n" + secret;
        try {
            // The HmacSHA256 algorithm is used to calculate the signature
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(secret.getBytes(charsetName), "HmacSHA256"));
            byte[] signData = mac.doFinal(stringToSign.getBytes());
            // Finally, perform urlEncode on the signature parameters to obtain the final signature (UTF-8 character set is required)
            String sign = URLEncoder.encode(Base64.getEncoder().encodeToString(signData), charsetName);
            var paramMap = Map.of("timestamp", timestamp, "sign", sign);
            var sendUrl = RestTemplateUtils.buildGetUrlByMap(SpringUtil.environment(PropertiesConstant.OAPI_DINGTALK_URL), paramMap);
            RestTemplateUtils.executeHttpPost(sendUrl, msgBody);
        } catch (Exception e) {
        }
    }

    @Builder
    @Data
    private static class DdMsgBody {
        private String msgtype;
        private Text text;
        private Markdown markdown;
    }

    @Accessors(chain = true)
    @Data
    private class Markdown {
        private String title;
    }

    @Accessors(chain = true)
    @Data
    private class Text {
        private String content;
    }
}

Additional configuration

The name of the < appender > tag can be set at will, but < appender ref > needs to reference the name. The value of the class attribute is the class that implements UnsynchronizedAppenderBase just defined above

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="sendErrorMsg" class="com.mty.jls.config.SendErrorMsgAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <!--Format output:%d Indicates the date,%thread Represents the thread name,%-5level: The level is displayed 5 characters wide from the left%msg: Log messages,%n Is a newline character -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
        </layout>
    </appender>
    <root level="info">
        <appender-ref ref="stdout"/>
        <appender-ref ref="sendErrorMsg"/>
    </root>
</configuration>

The effect is as follows:


If I think the article is helpful to you, please praise, comment and collect. Your support is my biggest motivation!!!

Finally, Xiaobian sorted out some learning materials during the learning process, which can be shared with Java engineers and friends to exchange and learn from each other, If necessary, you can join my learning exchange group 323432957 to obtain Java architecture learning materials for free (including architecture materials of multiple knowledge points such as high availability, high concurrency, high performance and distribution, Jvm performance tuning, Spring source code, MyBatis, Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx, etc.)

Author: dingyu002

Source: dinyu002

The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Topics: Java Android Database Multithreading Distribution