在“基于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这个方法,也是为了删除过期的数据,避免日志过大,导致内存出问题。

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

 import java.util.ArrayDeque;
import java.util.concurrent.ConcurrentLinkedQueue; /**
* LogGetter is supposed to be executed in a thread. It get message by the key and store it in the clq which
* is got in the construction method.User can get message by clq.poll().
*
* */
public class LogGetter extends Thread {
//identifier to get message
private String logKey;
//signal to stop the method run
private boolean isStop = false;
//a object to transfer message between LogGetter and the caller
private ConcurrentLinkedQueue<String> concurrentLinkedQueue; /**
* private construction prevents from wrong use of it.
*/
private LogGetter() {
} /**
* @param key
* identifier to get message
* @param clq
* a ConcurrentLinkedQueue<String> object to transfer message between LogGetter and the caller
* */
public LogGetter(String key, ConcurrentLinkedQueue<String> clq) {
this.logKey = key;
this.concurrentLinkedQueue = clq;
}
/**
* set the signal to
* */
public void setStop(boolean stop) {
isStop = stop;
} /**
* get message from AdapterAppender by key and store it in clq
*/
@Override
public void run() {
while (!isStop) { ArrayDeque<String> al = AdapterAppender.getMessageQueueByKey(this.logKey);
if (null == al) { } else {
for (String str : al) {
this.concurrentLinkedQueue.add(str);
}
} try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}

这个类会循环获取消息,放在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);
}

基于log4j的消息流的实现之二消息传递的更多相关文章

  1. 基于log4j的消息流的实现之一消息获取

    需求: 目前的程序中都是基于log4j来实现日志的管理,想要获取日志中的一部分消息,展示给用户. 约束: 由于程序中除了自己开发的代码,还会有层层依赖的第三方jar中的日志输出.需要展示给用户的消息, ...

  2. [蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)

    开机初始化Log Log编号 函数名   所在文件名 000001: main ..\main.c 000002: timers_init ..\main.c 000003: gpiote_init ...

  3. Android 基于Netty的消息推送方案之对象的传递(四)

    在上一篇文章中<Android 基于Netty的消息推送方案之字符串的接收和发送(三)>我们介绍了Netty的字符串传递,我们知道了Netty的消息传递都是基于流,通过ChannelBuf ...

  4. 开源一个自己造的轮子:基于图的任务流引擎GraphScheduleEngine

    GraphScheduleEngine是什么: GraphScheduleEngine是一个基于DAG图的任务流引擎,不同语言编写.运行于不同机器上的模块.程序,均可以通过订阅GraphSchedul ...

  5. BTARN 接收消息流以3A7为例

     1.RNIFReceive.aspx 页接收来自发起方的传入消息. (如果发起方是BizTalk则类似于:http://localhost/BTARNApp/RNIFSend.aspx?TPUrl ...

  6. 大数据平台消息流系统Kafka

    Kafka前世今生 随着大数据时代的到来,数据中蕴含的价值日益得到展现,仿佛一座待人挖掘的金矿,引来无数的掘金者.但随着数据量越来越大,如何实时准确地收集并分析如此大的数据成为摆在所有从业人员面前的难 ...

  7. Knative 实战:基于 Kafka 实现消息推送

    作者 | 元毅 阿里云智能事业群高级开发工程师 导读:当前在 Knative 中已经提供了对 Kafka 事件源的支持,那么如何基于 Kafka 实现消息推送呢?本文作者将以阿里云 Kafka 产品为 ...

  8. IM系统-消息流化一些常见问题

    原创不易,求分享.求一键三连 之前说过IM系统的一些优化,但是在网络上传输数据对于数据的流化和反流化也是处理异常情况的重点环节,不处理好可能会出现一些消息发送成功,但是解析失败的情况,本文就带大家来一 ...

  9. 基于SignalR的消息推送与二维码描登录实现

    1 概要说明 使用微信扫描登录相信大家都不会陌生吧,二维码与手机结合产生了不同应用场景,基于二维码的应用更是比较广泛.为了满足ios.android客户端与web短信平台的结合,特开发了基于Singl ...

随机推荐

  1. 寻找AP数

    题目背景 正整数n是无穷的,但其中有些数有神奇的性质,我们给它个名字--AP数. 题目描述 对于一个数字i是AP数的充要条件是所有比它小的数的因数个数都没有i的因数个数多.比如6的因数是1 2 3 6 ...

  2. 【shell脚本学习-3】

    part-1 #!/bin/bash:<<FTP#test [ 1 -eq 2] #条件测试x="abc" #不允许有空格y="abc" [ &qu ...

  3. com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column

    com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column …… 出现这个异常的很大可能性是 数据库是没有问题的 ...

  4. Mysqldump自定义导出n条记录

    很多时候DBA需要导出部分记录至开发.测试环境,因数据量需求较小,如果原库的记录多,且表数量也多,在用mysqldump命令导出时可以添加一个where参数,自定义导出n条记录,而不必全量导出. 示例 ...

  5. 利用phpspreadsheet切割excel大文件

    背景: 利用phpspreadsheet可以轻松的解析excel文件,但是phpspreadsheet的内存消耗也是比较大的,我试过解析将近5M的纯文字excel内存使用量就会超过php默认的最大内存 ...

  6. PHP中判断变量为空的几种方法小结

    isset  主要用来判断变量是否被初始化过empty  可以将值为 "假"."空"."0"."NULL"." ...

  7. 字典树(Trie)的学习笔记

    按照一本通往下学,学到吐血了... 例题1 字典树模板题吗. 先讲讲字典树: 给出代码(太简单了...)! #include<cstdio> #include<cstring> ...

  8. C++ 指针初始化要注意的地方

    1. 声明多个指针的时候: int* P1,P2; 如上所示,声明的是创建一个指针P1和一个int型的变量P2.而不是声明的两个指针. 对每个指针变量名,都需要使用一个*. 在C++中,int* 是一 ...

  9. TreeMap与LinkedHashMap的区别

    TreeMap是根据元素的内部比较器进行排序的,它可以根据key值的大小排序: LinkedHashMap是保持存放顺序的. TreeMap采用红黑树算法,遍历效率高: LinkedHashMap采用 ...

  10. Fragment保持状态切换

    在使用Activity管理多个Fragment时,每次切换Fragment使用的是replace,结果导致出现xxx is not currently in the FragmentManager异常 ...