• 基于log4j的消息流的实现之二消息传递


    在“基于log4j的消息流的实现之一消息获取”中获取日志消息的部分,修改如下:

    import org.apache.commons.collections.map.HashedMap;
    import org.apache.log4j.AppenderSkeleton;
    import org.apache.log4j.spi.LoggingEvent;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.ArrayDeque;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * AdapterAppender requires the layout as "%X - %m%n", and the message will be stored by identifier.
     * getMessageQueueByKey can be used to get the message by identifier
     * */
    public class AdapterAppender extends AppenderSkeleton {
        private static final Logger LOG = LoggerFactory.getLogger(AdapterAppender.class);
        //to store message got from log4j by key
        private static ConcurrentHashMap<String, ArrayDeque<String>> messageHM = new ConcurrentHashMap<>();
        //to store the first time the  message got by key
        private Map<String,Long> messageTimestamp = new HashedMap();
        //last time we do the remove method
        private long lastCheckTimestamp = System.currentTimeMillis();
        //will do the remove by one minute
        private final int checkInterval = 60000;
        //messages which has lived more than 10 minute will be removed
        private final int liveDuration = 600000;
    
        /**
         * format the log message as <identifier - information>, store the <identifier,information>
         *
         * @param loggingEvent
         *         log message sent from log4j rootLogger
         * */
        @Override
        protected void append(LoggingEvent loggingEvent) {
            String message = this.layout.format(loggingEvent);
            if (message.contains("-")) {
                String key = message.substring(0, message.indexOf("-") - 1).trim();
                String value = message.substring(message.indexOf("-") + 1).trim();
                if (messageHM.containsKey(key)) {
                    messageHM.get(key).add(value);
                } else {
                    ArrayDeque<String> ad = new ArrayDeque<>(32);
                    ad.add(value);
                    messageHM.put(key, ad);
                    messageTimestamp.put(key,System.currentTimeMillis());
                }
            }else {
                LOG.warn("Receive a wrong format message which does not have a '-' in it:{}",message);
            }
    
            //won't do the remove too frequently,will do it by the checkInterval
            long currentTimestamp = System.currentTimeMillis();
            if(currentTimestamp - this.lastCheckTimestamp > this.checkInterval){
                removeOldMessage(currentTimestamp);
                this.lastCheckTimestamp = currentTimestamp;
            }
    
        }
    
        /**
         * Remove the message which lives more than the liveDuration
         *
         * @param currentTime
         *         the check time
         * */
        private void removeOldMessage(long currentTime){
            for(String key : messageTimestamp.keySet()){
                long messageCreateTime = messageTimestamp.get(key);
                if(currentTime - messageCreateTime > this.liveDuration){
                    if(messageHM.containsKey(key)){
                        messageHM.remove(key);
                        LOG.info("Remove message for {}",key);
                    }
                    messageTimestamp.remove(key);
                }
    
            }
        }
    
        /**
         * return the message got by this appender until now
         * @param key
         *         identifier which will exists in the log message
         * @return message returned when key is found, null returned when key is not found
         * */
        public static ArrayDeque<String> getMessageQueueByKey(String key) {
            if (messageHM.containsKey(key) == false) {
                return null;
            }
            ArrayDeque<String> ad = messageHM.get(key);
            messageHM.remove(key);
            return ad;
        }
    
        @Override
        public void close() {
    
        }
    
        /**
         * the layout should be "%X - %m%n"
         * @param
         * @return
         * */
        @Override
        public boolean requiresLayout() {
            return true;
        }
    }

    对外保留了一个static方法 getMessageQueueByKey,供获取消息。

    注意看到,这个方法里,获取消息后,消息会被删掉,另外在removeOldMessage这个方法,也是为了删除过期的数据,避免日志过大,导致内存出问题。

    如下的类,会去获取消息:

     1 import java.util.ArrayDeque;
     2 import java.util.concurrent.ConcurrentLinkedQueue;
     3 
     4 /**
     5  * LogGetter is supposed to be executed in a thread. It get message by the key and store it in the clq which
     6  * is got in the construction method.User can get message by clq.poll().
     7  *
     8  * */
     9 public class LogGetter extends Thread {
    10     //identifier to get message
    11     private String logKey;
    12     //signal to stop the method run
    13     private boolean isStop = false;
    14     //a object to transfer message between LogGetter and the caller
    15     private ConcurrentLinkedQueue<String> concurrentLinkedQueue;
    16 
    17     /**
    18      * private construction prevents from wrong use of it.
    19      */
    20     private LogGetter() {
    21     }
    22 
    23     /**
    24      * @param key
    25      *         identifier to get message
    26      * @param clq
    27      *         a ConcurrentLinkedQueue<String> object to transfer message between LogGetter and the caller
    28      * */
    29     public LogGetter(String key, ConcurrentLinkedQueue<String> clq) {
    30         this.logKey = key;
    31         this.concurrentLinkedQueue = clq;
    32     }
    33     /**
    34      * set the signal to
    35      * */
    36     public void setStop(boolean stop) {
    37         isStop = stop;
    38     }
    39 
    40     /**
    41      * get message from AdapterAppender by key and store it in clq
    42      */
    43     @Override
    44     public void run() {
    45         while (!isStop) {
    46             
    47             ArrayDeque<String> al = AdapterAppender.getMessageQueueByKey(this.logKey);
    48             if (null == al) {
    49 
    50             } else {
    51                 for (String str : al) {
    52                     this.concurrentLinkedQueue.add(str);
    53                 }
    54             }
    55             
    56             try {
    57                 Thread.sleep(100);
    58             } catch (InterruptedException ie) {
    59                 ie.printStackTrace();
    60             }
    61         }
    62     }
    63 }

    这个类会循环获取消息,放在queue里。

    实际使用的片段如下:

    /**
            * send back the message keyed by mdcValue with responseObserver when execute future
            *
            * @param future
             *        a future which is already executed
            * @param mdcValue
             *        a value will be set into the MDC
             * @param responseObserver
             *        a observer which will send back message
            * **/
            private void sendFutureLog(Future future,
                                        String mdcValue,
                                        StreamObserver<AgentResultReply> responseObserver){
    
                ConcurrentLinkedQueue<String> messageQ = new ConcurrentLinkedQueue<>();
                LogGetter lg = new LogGetter(mdcValue, messageQ);
                this.executorService.submit(lg);
    
                String returnMessage;
                AgentResultReply agentResultReply;
    
                while (!future.isDone()) {
                    while (messageQ.size() > 0) {
                        returnMessage = responseJson.getResponseJson(ResponseJson.MessageType.INFO,
                                messageQ.poll());
                        agentResultReply = AgentResultReply.newBuilder().setMessage(returnMessage).build();
                        responseObserver.onNext(agentResultReply);
                    }
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException ie) {
                        LOG.error("Exception happened in sendFutureLog:{}",ie.getMessage());
                        ie.printStackTrace();
                    }
                }
    
                lg.setStop(true);
            }
  • 相关阅读:
    NCNN优化实时面部关键点检测
    使用 div 标签 contenteditable="true" 实现一个 聊天框,支持 Ctrl + v 粘贴图片
    《精益创业》读书总结
    DATAX使用
    canal增量同步原理以及使用说明
    element rules required 自定义表达式
    JavaScript 数组映射,重新整理
    wangeditor遮挡其他控件
    多个axios按顺序执行
    .NET Core 中基于 IHostedService 实现后台定时任务
  • 原文地址:https://www.cnblogs.com/029zz010buct/p/9449109.html
Copyright © 2020-2023  润新知