在“基于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. 密钥登录LINUX步骤

    1.创建目录2.创建一个文件3.给目录和文件授权4.关闭防火墙5.然后才可以登录.

  2. 服务命令只支持基本的LSB操作(启动、停止、重新启动、尝试重启、重新加载、强制重新加载、状态)。对于其他操作,请尝试使用systemctl。

    The service command supports only basic LSB actions (start, stop, restart, try-restart, reload, forc ...

  3. Apache Maven(六):存储库

    Maven 存储库主要是存放一些第三方依赖jar包等. 严格来说,只有两种存储库:本地和远程,本地存储库是指您远程下载到本地的一个缓存,还包含尚未发布的临时构建文件.远程存储库是指一些可以通过各种协议 ...

  4. Source Insight的使用

    1. source insight查看函数的上一级调用的位置(函数) --> 鼠标放在函数上,右键 选择 Jump To caller,就可以看到有哪些函数调用它了:

  5. 数据分析处理库Pandas——时间

    时间戳 向后推的时间戳 备注:五天后的时间. 指定日期和时间 时间的Series结构 按要求显示时间(开始时间,时间间隔,时间个数) 转换为时间格式,并设置时间列为索引列 方法一 方法二 筛选显示 方 ...

  6. 001---Linux系统的启动过程

    Linux系统的启动过程 按下电源 开机自检(BIOS):检查cpu.内存.硬盘是否有问题,找到启动盘. MBR引导(master boot record):主引导记录,读取存储设备的512bytes ...

  7. R语言学习笔记(十六):构建分割点函数

    选取预测概率的分割点 cutoff<- function(n,p){ pp<-1 i<-0 while (pp>=0.02) { model.predfu<-rep(&q ...

  8. spring boot打包问题

    java.lang.IllegalArgumentException: No auto configuration classes found in META-INF/spring.factories ...

  9. MFC接收ShellExecute多个参数

    在应用程序开发过程中,我们经常需要带参数启动另一个执行程序,如何传递多个参数,如何解析多个参数呢?   传参数 传递参数可使用ShellExecute函数,示例如下: ShellExecute(NUL ...

  10. python2.7入门---file(文件)&OS 文件&目录方法

        首先file 对象使用 open 函数来创建,下表列出了 file 对象常用的函数: 序号 方法及描述 1 file.close() 关闭文件.关闭后文件不能再进行读写操作. 2 file.f ...