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.