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

  1. import org.apache.commons.collections.map.HashedMap;
  2. import org.apache.log4j.AppenderSkeleton;
  3. import org.apache.log4j.spi.LoggingEvent;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6.  
  7. import java.util.ArrayDeque;
  8. import java.util.Map;
  9. import java.util.concurrent.ConcurrentHashMap;
  10.  
  11. /**
  12. * AdapterAppender requires the layout as "%X - %m%n", and the message will be stored by identifier.
  13. * getMessageQueueByKey can be used to get the message by identifier
  14. * */
  15. public class AdapterAppender extends AppenderSkeleton {
  16. private static final Logger LOG = LoggerFactory.getLogger(AdapterAppender.class);
  17. //to store message got from log4j by key
  18. private static ConcurrentHashMap<String, ArrayDeque<String>> messageHM = new ConcurrentHashMap<>();
  19. //to store the first time the message got by key
  20. private Map<String,Long> messageTimestamp = new HashedMap();
  21. //last time we do the remove method
  22. private long lastCheckTimestamp = System.currentTimeMillis();
  23. //will do the remove by one minute
  24. private final int checkInterval = 60000;
  25. //messages which has lived more than 10 minute will be removed
  26. private final int liveDuration = 600000;
  27.  
  28. /**
  29. * format the log message as <identifier - information>, store the <identifier,information>
  30. *
  31. * @param loggingEvent
  32. * log message sent from log4j rootLogger
  33. * */
  34. @Override
  35. protected void append(LoggingEvent loggingEvent) {
  36. String message = this.layout.format(loggingEvent);
  37. if (message.contains("-")) {
  38. String key = message.substring(0, message.indexOf("-") - 1).trim();
  39. String value = message.substring(message.indexOf("-") + 1).trim();
  40. if (messageHM.containsKey(key)) {
  41. messageHM.get(key).add(value);
  42. } else {
  43. ArrayDeque<String> ad = new ArrayDeque<>(32);
  44. ad.add(value);
  45. messageHM.put(key, ad);
  46. messageTimestamp.put(key,System.currentTimeMillis());
  47. }
  48. }else {
  49. LOG.warn("Receive a wrong format message which does not have a '-' in it:{}",message);
  50. }
  51.  
  52. //won't do the remove too frequently,will do it by the checkInterval
  53. long currentTimestamp = System.currentTimeMillis();
  54. if(currentTimestamp - this.lastCheckTimestamp > this.checkInterval){
  55. removeOldMessage(currentTimestamp);
  56. this.lastCheckTimestamp = currentTimestamp;
  57. }
  58.  
  59. }
  60.  
  61. /**
  62. * Remove the message which lives more than the liveDuration
  63. *
  64. * @param currentTime
  65. * the check time
  66. * */
  67. private void removeOldMessage(long currentTime){
  68. for(String key : messageTimestamp.keySet()){
  69. long messageCreateTime = messageTimestamp.get(key);
  70. if(currentTime - messageCreateTime > this.liveDuration){
  71. if(messageHM.containsKey(key)){
  72. messageHM.remove(key);
  73. LOG.info("Remove message for {}",key);
  74. }
  75. messageTimestamp.remove(key);
  76. }
  77.  
  78. }
  79. }
  80.  
  81. /**
  82. * return the message got by this appender until now
  83. * @param key
  84. * identifier which will exists in the log message
  85. * @return message returned when key is found, null returned when key is not found
  86. * */
  87. public static ArrayDeque<String> getMessageQueueByKey(String key) {
  88. if (messageHM.containsKey(key) == false) {
  89. return null;
  90. }
  91. ArrayDeque<String> ad = messageHM.get(key);
  92. messageHM.remove(key);
  93. return ad;
  94. }
  95.  
  96. @Override
  97. public void close() {
  98.  
  99. }
  100.  
  101. /**
  102. * the layout should be "%X - %m%n"
  103. * @param
  104. * @return
  105. * */
  106. @Override
  107. public boolean requiresLayout() {
  108. return true;
  109. }
  110. }

对外保留了一个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里。

实际使用的片段如下:

  1. /**
  2. * send back the message keyed by mdcValue with responseObserver when execute future
  3. *
  4. * @param future
  5. * a future which is already executed
  6. * @param mdcValue
  7. * a value will be set into the MDC
  8. * @param responseObserver
  9. * a observer which will send back message
  10. * **/
  11. private void sendFutureLog(Future future,
  12. String mdcValue,
  13. StreamObserver<AgentResultReply> responseObserver){
  14.  
  15. ConcurrentLinkedQueue<String> messageQ = new ConcurrentLinkedQueue<>();
  16. LogGetter lg = new LogGetter(mdcValue, messageQ);
  17. this.executorService.submit(lg);
  18.  
  19. String returnMessage;
  20. AgentResultReply agentResultReply;
  21.  
  22. while (!future.isDone()) {
  23. while (messageQ.size() > 0) {
  24. returnMessage = responseJson.getResponseJson(ResponseJson.MessageType.INFO,
  25. messageQ.poll());
  26. agentResultReply = AgentResultReply.newBuilder().setMessage(returnMessage).build();
  27. responseObserver.onNext(agentResultReply);
  28. }
  29. try {
  30. Thread.sleep(50);
  31. } catch (InterruptedException ie) {
  32. LOG.error("Exception happened in sendFutureLog:{}",ie.getMessage());
  33. ie.printStackTrace();
  34. }
  35. }
  36.  
  37. lg.setStop(true);
  38. }

基于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. 『嗨威说』常见的C++函数模板整理(一)

    开学两天,身上的职责直接变为两个班班长,三个小组组长,哇这事情估计够我忙活了,想躲都躲不掉啊,看来我还是真招人推荐各种管理职务啊,以后要是有人推荐我当经理啊领导啊该多好哈哈哈哈.记得今天奶奶生日,很开 ...

  2. poj 3177 Redundant Paths 求最少添加几条边成为双联通图: tarjan O(E)

    /** problem: http://poj.org/problem?id=3177 tarjan blog: https://blog.csdn.net/reverie_mjp/article/d ...

  3. chromium之tracked

    //------------------------------------------------------------------------------ // Tracked is the b ...

  4. 在mac上显示网速的软件——iStat Menus 5:

    在mac上显示网速的软件——iStat Menus 5: https://bjango.com/mac/istatmenus/ 注册码: Email: 982092332@qq.com SN: GAW ...

  5. mongo复制集脑裂问题如何处理

    mongo replication 脑裂问题如何处理: 一.问题描述:一套mongo replication有4个节点.1个仲裁节点.在停止实例(或实例毁坏)的时候,导致所有节点都变为SECONDAR ...

  6. MySQL备份恢复之mysqldump

      Preface       The day before yesterday,there's a motif about the lock procedure when backing up My ...

  7. 导入项目后,js文件报错解决方法

    导入项目后,发现 js文件报错,但是js文件是从官网下载的.解决办法: 选中报错的js文件, 右键选择 MyEclipse-->Exclude From Validation : 然后继续右键执 ...

  8. 中间件kafka

    * kafka----一个发布订阅消息系统,中间件:一个分布式.分区.可重复的日志服务kafka需要了解基础几层结构,生产者订阅者等使用方法,和在高并发.一致性场景使用.(凡事面试问一致性.高并发都脱 ...

  9. Django自带后台使用配置

    参考官网地址:https://docs.djangoproject.com/en/1.11/ref/contrib/admin/ ,本文章值是介绍简单配置,如果需要详细内容可以查阅官方文档 自动管理界 ...

  10. Python爬虫基础(一)——HTTP

    前言 因特网联系的是世界各地的计算机(通过电缆),万维网联系的是网上的各种各样资源(通过超文本链接),如静态的HTML文件,动态的软件程序······.由于万维网的存在,处于因特网中的每台计算机可以很 ...