基于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这个方法,也是为了删除过期的数据,避免日志过大,导致内存出问题。
如下的类,会去获取消息:
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的消息流的实现之二消息传递的更多相关文章
- 基于log4j的消息流的实现之一消息获取
需求: 目前的程序中都是基于log4j来实现日志的管理,想要获取日志中的一部分消息,展示给用户. 约束: 由于程序中除了自己开发的代码,还会有层层依赖的第三方jar中的日志输出.需要展示给用户的消息, ...
- [蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)
开机初始化Log Log编号 函数名 所在文件名 000001: main ..\main.c 000002: timers_init ..\main.c 000003: gpiote_init ...
- Android 基于Netty的消息推送方案之对象的传递(四)
在上一篇文章中<Android 基于Netty的消息推送方案之字符串的接收和发送(三)>我们介绍了Netty的字符串传递,我们知道了Netty的消息传递都是基于流,通过ChannelBuf ...
- 开源一个自己造的轮子:基于图的任务流引擎GraphScheduleEngine
GraphScheduleEngine是什么: GraphScheduleEngine是一个基于DAG图的任务流引擎,不同语言编写.运行于不同机器上的模块.程序,均可以通过订阅GraphSchedul ...
- BTARN 接收消息流以3A7为例
 1.RNIFReceive.aspx 页接收来自发起方的传入消息. (如果发起方是BizTalk则类似于:http://localhost/BTARNApp/RNIFSend.aspx?TPUrl ...
- 大数据平台消息流系统Kafka
Kafka前世今生 随着大数据时代的到来,数据中蕴含的价值日益得到展现,仿佛一座待人挖掘的金矿,引来无数的掘金者.但随着数据量越来越大,如何实时准确地收集并分析如此大的数据成为摆在所有从业人员面前的难 ...
- Knative 实战:基于 Kafka 实现消息推送
作者 | 元毅 阿里云智能事业群高级开发工程师 导读:当前在 Knative 中已经提供了对 Kafka 事件源的支持,那么如何基于 Kafka 实现消息推送呢?本文作者将以阿里云 Kafka 产品为 ...
- IM系统-消息流化一些常见问题
原创不易,求分享.求一键三连 之前说过IM系统的一些优化,但是在网络上传输数据对于数据的流化和反流化也是处理异常情况的重点环节,不处理好可能会出现一些消息发送成功,但是解析失败的情况,本文就带大家来一 ...
- 基于SignalR的消息推送与二维码描登录实现
1 概要说明 使用微信扫描登录相信大家都不会陌生吧,二维码与手机结合产生了不同应用场景,基于二维码的应用更是比较广泛.为了满足ios.android客户端与web短信平台的结合,特开发了基于Singl ...
随机推荐
- MySQL 字符串函数:字符串截取
1.left(name,4)截取左边的4个字符 列: ,) 年 结果:2018 2.right(name,2)截取右边的2个字符 ,) 月份 结果:09 3.SUBSTRING(name,5,3) 截 ...
- Java OOP——第二章 继承
1. 继承: ●继承是面向对象的三大特征之一,是JAVA实现代码重用的重要手段之一: ●继承是代码重用的一种方式,将子类共有的属性和行为放到父类中: ●JAVA只支持单继承,即每一个类只有一个父类,继 ...
- vertical-align垂直居中
<div id="content"> <div id="weizi"> 锄禾日当午,<br> 汗滴禾下土.<br> ...
- JZOJ 5934. 列队
Description Sylvia是一个热爱学习的女孩子. 在平时的练习中,他总是能考到std以上的成绩,前段时间,他参加了一场练习赛,众所周知,机房是一个 的方阵.这 ...
- 查看ubuntu版本号命令
1.uname -a 查看内核版本号 2.cat /etc/issue 查看ubuntu版本号 3.sudo lsb_release -a 查看ubuntu版本号
- 第三章 最简单的C程序设计——顺序程序设计
一.数据的表现形式及其运算 1.常量和变量 在计算机高级语言中,数据有两种表现形式:常量和变量. 1.1.常量 在程序运行过程中,其值不能被改变的量称为常量.如:5,6,32,0.111. 数值常量就 ...
- 017---Django的中间件解决跨域
跨域 跨域是什么? 浏览器从一个域名的网页去请求另一个域名的资源的时候,如果不同源.请求的响应结果就会被浏览器的同源策略所拦截 同源策略是什么? 同源:协议 + 域名 + 端口 特点:阻止ajax请求 ...
- (数据科学学习手札11)K-means聚类法的原理简介&Python与R实现
kmeans法(K均值法)是麦奎因提出的,这种算法的基本思想是将每一个样本分配给最靠近中心(均值)的类中,具体的算法至少包括以下三个步骤: 1.将所有的样品分成k个初始类: 2.通过欧氏距离将某个样品 ...
- python2.7练习小例子(十九)
19):题目:一球从100米高度自由落下,每次落地后反跳回原高度的一半:再落下,求它在第10次落地时,共经过多少米?第10次反弹多高? #!/usr/bin/python # -*- codi ...
- 汇编实验15:安装新的int 9中断例程
汇编实验15:安装新的int 9中断例程 任务 安装一个新的int 9中断例程,功能:在DOS下,按下“A”键后,除非不在松开,一旦松开后,就显示满屏幕的“A”,其他键照常处理. 预备知识概要 这次实 ...