1. Logback的设计目标之一是审查和调试复杂的分布式应用程序。真实世界的多数分布式系统需要同时处理多个客户端。在一个典型的多线程方式实现的分布式系统里, 不同的线程

处理不同的客户端。区分不同客户端的记录输出的一个可行的但不好的方法是为每个客户端都创建新的、独立的logger。但是这种技术使logger的数量增多且大大增加了管理开销。

2. 一个轻量的技术是为客户端的每个记录请求添加唯一戳(uniquely stamp)。Logback在SLJ4J里使用了这种技术的一种变体: 映射诊断环境(MDC)。

3. 为了给每个请求添加唯一戳, 可以把用户信息放进MDC。MDC类的重要部分如下:

4. MDC类只有静态方法, 开发者可以把信息放进一个诊断环境, 之后用其他logback组件获取这些信息。MDC是基于每个线程进行管理的。子线程自动继承其父的映射诊断环境

的一个副本。典型地, 当开始为新的客户端请求服务时, 开发者会向MDC里插入恰当的环境信息, 比如客户端id、客户端IP地址、请求参数等等。Logback组件会自动在每个记录

条目里包含这些信息。

5. MDC简单使用例子

5.1. 新建一个名为SampleMDC的Java项目, 同时添加相关jar包。

5.2. 新建SampleMDC.java

package com.fj.smdc;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;public class SampleMDC {private static final Logger logger = LoggerFactory.getLogger(SampleMDC.class);public static void main(String[] args) {MDC.put("first", "错误");MDC.put("last", "警告");logger.error("MDC简单使用错误信息。");logger.warn("MDC简单使用警告信息。");MDC.put("first", "信息");MDC.put("last", "测试");logger.info("MDC简单使用信息。");logger.debug("MDC简单使用测试信息。");}
}

5.3. 新建logback.xml

5.4. 运行项目

6. 映射诊断环境在客户端-服务器模式下最有奇效。典型情况是, 多个客户端被服务器的多个线程所处理。虽然MDC里的方法都是静态的, 但MDC是基于每个线程进行管理的, 这允许服务器的每个线程都有自己独立的MDC戳。MDC的操作, 比如put()和get(), 只作用于当前线程和当前线程的子线程, 不影响其他线程里的MDC。由于MDC的信息是基于每个线程进行管理的, 因此每个线程都有自己的MDC副本。所以在使用MDC时, 开发者不需要操心线程安全或同步, 因为MDC透明地、安全地处理了这些问题。

7. 客户端-服务器例子

7.1. 新建一个名为ClientServerMDC的Java项目, 同时添加相关jar包。

7.2. 在src目录下创建logback.xml

7.3. 新建一个MyServer.java

package com.fj.csmdc;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;public class MyServer {public static void main(String[] args) {ServerSocket server = null;try {server = new ServerSocket(9999);System.out.println("server start...");int tnum = 0;while(true) {Socket socket = server.accept();new Thread(new ServerHandler(socket), "t" + (++tnum)).start();}} catch (IOException e) {e.printStackTrace();} finally {if(server != null) {try {server.close();} catch (IOException e) {e.printStackTrace();}}}}
}

7.4. 新建一个ServerHandler.java

package com.fj.csmdc;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;public class ServerHandler implements Runnable {private static final Logger logger = LoggerFactory.getLogger(ServerHandler.class);private Socket socket;public ServerHandler( Socket socket) {this.socket = socket;}@Overridepublic void run() {if(socket == null) {logger.error("socket为空。");return;}MDC.put("client", socket.getRemoteSocketAddress().toString());try {BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));String line = null;while(socket != null && (!socket.isClosed()) && (line = br.readLine()) != null) {logger.info(line);}} catch (IOException e) {e.printStackTrace();} finally {if(socket != null && (!socket.isClosed())) {try {socket.close();} catch (IOException e) {e.printStackTrace();}}socket = null;}}
}

7.5. 新建一个MyClient.java

package com.fj.csmdc;import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;public class MyClient {public static void main(String[] args) {try {Socket socket = new Socket("192.168.25.1", 9999);System.out.println("client start...");new Thread(new ClienWriter(socket)).start();} catch (IOException e) {e.printStackTrace();}}
}class ClienWriter implements Runnable{private Socket socket;public ClienWriter(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String line = null;while(socket != null && (!socket.isClosed())) {System.out.println("请输入内容(不允许为空), 输入886退出程序: ");line = br.readLine();if(line == null || line.length() <= 0) {continue;}if(line.equals("886")) {break;}bw.write(line);bw.newLine();bw.flush();}} catch (IOException e) {e.printStackTrace();} finally {System.out.println("client close socket writer " + System.currentTimeMillis());if(socket != null  && (!socket.isClosed())) {try {socket.close();} catch (Exception e) {e.printStackTrace();}}socket = null;}}
}

7.6. 运行MyServer.java

7.7. 运行MyClient.java

7.8. 在MyClient运行控制台输入"你好哇", 然后按下回车键

7.9. 查看运行的MyServer控制台

7.10. 再运行一个MyClient.java客户端

7.11. 在第二个MyClient运行控制台输入"很高兴见到你", 然后按下回车键

7.12.  查看运行的MyServer控制台

8. MDCInsertingServletFilter

8.1. 我们已经看到, 在处理多个客户端时, MDC非常有用。在一个管理用户验证的web程序里, 可以在MDC里设置用户名, 然后在用户注销登录时从MDC删除用户名。不幸的是, 上面的方法不是始终可靠的。由于MDC 是基于每个线程对数据进行管理的, 所以服务器循环使用线程时会导致错误地使用MDC里的信息。

8.2. 在处理请求时, 为保证MDC里的信息始终正确, 一个可行的方法是, 在处理流程的开头存储用户名, 然后再这个处理流程的尾部移除用户名。这种情况可以用一个servlet过滤器。

8.3. 在servlet过滤器的doFilter()方法里, 我们可以从请求取得相关的用户数据, 然后存储在MDC。后续的其他过滤器和servlet将自动从先前存储的MDC里受益。最终, 当我们的servlet 过滤器重新得到控制权后, 就有机会清除MDC数据。就是在过滤器链完成后, 该过滤器从MDC移除用户信息。

8.4. 在web程序里, 经常需要取得http请求的主机名、请求uri和user-agent。MDCInsertingServletFilter把这些数据放入MDC, 所用的键如下:

8.5. 新建一个名为FilterMDC的动态Web工程, 同时添加相关jar包。

8.6. 在src目录下创建logback.xml

8.7. 编写index.html

8.8. 编写MyServlet.java

package com.fj.fmdc;import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class MyServlet extends HttpServlet {private static final long serialVersionUID = 1L;private static final Logger logger = LoggerFactory.getLogger(MyServlet.class);@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String userName = req.getParameter("userName");String password = req.getParameter("password");if(userName != null && password != null) {logger.debug("userName = " + userName + ", password = " + password);resp.getWriter().write("login success.");}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}
}

8.9. 配置web.xml

8.10. 运行项目, 使用浏览器访问

8.11. 点击提交, 控制台打印

8.12. 浏览器输出

9. 子线程访问MDC副本

9.1. 在初始(主线程)上调用MDC.getCopyOfContextMap()。当子线程运行时, 它的第一个动作就应该是调用MDC.setContextMapValues(), 为子线程关联初始MDC值的副本。

9.2. 更改我们的SampleMDC.java

package com.fj.smdc;import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;public class SampleMDC {private static final Logger logger = LoggerFactory.getLogger(SampleMDC.class);public static void main(String[] args) {MDC.put("first", "错误");MDC.put("last", "警告");logger.error("MDC简单使用错误信息。");logger.warn("MDC简单使用警告信息。");MDC.put("first", "信息");Map<String, String> contextMap = MDC.getCopyOfContextMap();new Thread(new Runnable() {@Overridepublic void run() {MDC.setContextMap(contextMap);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}logger.info("MDC简单使用信息。");logger.debug("MDC简单使用测试信息。");}}).start();MDC.put("last", "测试");try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}}
}

9.3. 运行程序

010-映射诊断环境相关推荐

  1. 分布式框架-日志系统思路及实现

    转自:https://www.jianshu.com/p/ce30c31111ca 背景 随着互联网时代数据规模的爆发式增长,传统的单机系统在性能和可用性上已经无法胜任,分布式应用和服务化应用开始走进 ...

  2. java jee curd_Java / JEE中的有效日志记录–映射的诊断上下文

    java jee curd 这一切始于当我和一位同事坐在一起解决一些应用程序问题时,当我注意到一些有趣的事情时. 他正在合并代码,我的眼睛吸引了此类" org.apache.log4j.MD ...

  3. Java / JEE中的有效日志记录–映射的诊断上下文

    当我和一位同事坐在一起解决一些应用程序问题时,一切都开始了,当时我注意到了一些有趣的事情. 他正在合并代码,我的眼睛吸引了此类" org.apache.log4j.MDC"的注意. ...

  4. 使用阿里云ACM简化你的Spring Cloud微服务环境配置管理

    摘要: 本文我们就如何使用阿里云ACM这样的配置管理产品在Spring Cloud中替代Spring Cloud Config帮助简化环境配置管理做一个简单的示例,帮助你理解基于ACM来简化微服务环境 ...

  5. 使用阿里云ACM简化你的Spring Cloud微服务环境配置管理 1

    摘要: 本文我们就如何使用阿里云ACM这样的配置管理产品在Spring Cloud中替代Spring Cloud Config帮助简化环境配置管理做一个简单的示例,帮助你理解基于ACM来简化微服务环境 ...

  6. 如何用ACM简化你的Spring Cloud微服务环境配置管理

    摘要: 本文我们就如何使用阿里云ACM这样的配置管理产品在Spring Cloud中替代Spring Cloud Config帮助简化环境配置管理做一个简单的示例,帮助你理解基于ACM来简化微服务环境 ...

  7. Hibernate4.3.1搭建Log4J日志环境

    导语:原项目中使用了log4j-1.2.8.jar,在 引入hibernate包后,提示报错,在构建路径中将log4j-1.2.8.jar删除后正常.但记录日志还需要用到log4j,以下方法可解决上述 ...

  8. 【防火墙篇】01. 映射 Web 服务器 ❀ WatchGuard 防火墙

    [简介]端口映射就是将外网主机的IP地址的一个端口映射到内网中一台机器,提供相应的服务.当用户访问该IP的这个端口时,服务器自动将请求映射到对应局域网内部的机器上.端口映射有动态和静态之分.   什么 ...

  9. Chapter4、色调映射的一般方法

    因为这本书对具体方案的原理并不细说,只提了有哪些方法和有哪些效果,类似于综述.因此,我以后也按照综述的方法来写,不细说原理了. 4.1.色调映射的一种一般方法 4.1.1.建模一种通用的色调映射算子 ...

最新文章

  1. Bootstrap 基本模板
  2. RedisManager使用手册(六)-- 监控功能介绍
  3. Unity 新手入门 如何理解协程 IEnumerator yield
  4. ZT Web Control 开发系列(一) 页面的生命周期
  5. 如何才能轻松地分析日志?
  6. 运行时修改Standard shader的Mode
  7. 怎么卡我的世界服务器物品,我的世界怎么卡服务器物品 | 手游网游页游攻略大全...
  8. 电路原理解析_接近开关检测旋转设备的工作原理
  9. Spring IoC、应用上下文容器层次分析
  10. 未来教育考试系统V4.0——安装后双击打不开
  11. 2021数学建模国赛b题思路总结
  12. 四、Storm入门之Spout
  13. 生成划掉的字_哪种备忘录划删除线,能划掉文字在字中间划线的便签
  14. 【问题描述】输入一行字符串,含有数字和非数字字符以及空格等,如: df23adfd56 2343?23dgjop535 如果将其中所有连续出现的数字视为一个整数,要求统计在该字符串中共有多少个整数,并
  15. 如何学习android
  16. cdr mac majave os_苹果最新 macOS Mojave 10.14 正式版 懒人镜像
  17. 用Prometheus和Grafana监控Java Spring应用
  18. spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during v
  19. 谷歌L3到L7扎堆升职,股票refresh多过别人年薪!
  20. 跑步、列计划、早起、读书、独处…自律真的能改变人生

热门文章

  1. HTTP长连接短连接
  2. 针对数据 gnuplot画图 初试
  3. mysql全量和增量备份脚本
  4. Event Aggregator
  5. php作为弱语言是最棒的~哈哈哈哈
  6. 基于docker搭建svn-server
  7. 交互输入与for语句
  8. CRC原理及其逆向分析方法
  9. 程序员的高速学习法——以JS学习为例,进行图解
  10. mysql退出当前数据库,再次 show databases