程序员你为什么这么累:编码习惯之日志建议
2017-07-05 15:13
821 查看
开发中日志这个问题,每个公司都强调,也制定了一大堆规范,但根据实际情况看,效果不是很明显,主要是这个东西不好测试和考核,没有日志功能一样跑啊。
但编程活久见,开发久了,总会遇到“这个问题生产环境上能重现,但是没有日志,业务很复杂,不知道哪一步出错了?” 这个时候,怎么办? 还能怎么办,发个版本,就是把所有地方加上日志,没有任何新功能,然后在让用户重现一遍,拿下日志来看,哦,原来是这个问题。
有没有很熟悉的感觉?
还有一种情况,我们系统有
所以我对日志的最少有以下2点要求:
能找到那个机器
能找到同一个请求散布在不同日志文件之间的日志
针对第一点,我修改了一下
upstream serverlist {
server localhost:8080;
server localhost:8080;
}
server {
listen 80;
server_name a.com;
location / {
proxy_pass http://serverlist/; add_header X-Slave $upstream_addr;
}
}
效果如图,返回了处理的节点:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/a07b42e44891caca642a8446d9aff57f)
第二点,我们的项目业务越来越复杂,很多时候需要拆分,不同的业务类型分别记录自己的日志,甚至对于数据库操作的日志都可以单独归类到一个日志文件中,只是这样如何从海量不同日志文件里面能快速找到同一线程的目标日志呢。所以找了一下log4j的配置,果然log4j有个叫
package com.somnus.solo.support.web.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.MDC;
import org.springframework.web.filter.OncePerRequestFilter;
import com.somnus.solo.support.common.Constants;
import com.somnus.solo.support.util.SessionUtil;
/**
* 日志处理过滤器
*/
public class LoggingProcessFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//扩展日志追踪号
MDC.put(Constants.SESSION_ID, SessionUtil.getSessionId());
try {
filterChain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}
package com.somnus.solo.support.util;
public class SessionUtil {
/**
* @Description: 为每个线程生成唯一的sessionId
* @return String sessionId
* @throws
*/
public static String getSessionId() {
String sessionId = System.currentTimeMillis() + "-"
+ (int) ((Math.random() * 10000));
return sessionId;
}
}
log4j配置,增加日志追踪号变量:
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg表示日志消息,%n表示换行符-->
<property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{sessionId}] %-5level [%thread] %logger{80} :%msg%n" />
最终效果如下:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/e447265918c82c5a747abc27b243be2b)
是不是相当实用呢。
但编程活久见,开发久了,总会遇到“这个问题生产环境上能重现,但是没有日志,业务很复杂,不知道哪一步出错了?” 这个时候,怎么办? 还能怎么办,发个版本,就是把所有地方加上日志,没有任何新功能,然后在让用户重现一遍,拿下日志来看,哦,原来是这个问题。
有没有很熟悉的感觉?
还有一种情况,我们系统有
3*5=15个节点,出了问题找日志真是痛苦,一个一个机器翻,N分钟后终于找到了,找到了后发现好多相似日志,一个一个排查;日志有了,发现逻辑很复杂,不知道走到那个分支,只能根据逻辑分析,半天过去了,终于找到了原因。。。一个问题定位就过去了2个小时,变更时间过去了一半。。。
所以我对日志的最少有以下2点要求:
能找到那个机器
能找到同一个请求散布在不同日志文件之间的日志
针对第一点,我修改了一下
nginx的配置文件,让返回头里面返回是那个机器处理的。
nginx的基本配置,大家查阅一下资料就知道。简单配置如下(生产环境比这个完善)
upstream serverlist {
server localhost:8080;
server localhost:8080;
}
server {
listen 80;
server_name a.com;
location / {
proxy_pass http://serverlist/; add_header X-Slave $upstream_addr;
}
}
效果如图,返回了处理的节点:
第二点,我们的项目业务越来越复杂,很多时候需要拆分,不同的业务类型分别记录自己的日志,甚至对于数据库操作的日志都可以单独归类到一个日志文件中,只是这样如何从海量不同日志文件里面能快速找到同一线程的目标日志呢。所以找了一下log4j的配置,果然log4j有个叫
MDC(Mapped Diagnostic Context)的类(技术上使用了
ThreadLocal实现,重点技术)。具体使用方法请自行查询。具体使用如下:
filter为每个线程生成唯一的
sessionId,并放入
MDC,记住
filter后要清理掉(因为tomcat线程池线程重用的原因)。
package com.somnus.solo.support.web.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.MDC;
import org.springframework.web.filter.OncePerRequestFilter;
import com.somnus.solo.support.common.Constants;
import com.somnus.solo.support.util.SessionUtil;
/**
* 日志处理过滤器
*/
public class LoggingProcessFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//扩展日志追踪号
MDC.put(Constants.SESSION_ID, SessionUtil.getSessionId());
try {
filterChain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}
package com.somnus.solo.support.util;
public class SessionUtil {
/**
* @Description: 为每个线程生成唯一的sessionId
* @return String sessionId
* @throws
*/
public static String getSessionId() {
String sessionId = System.currentTimeMillis() + "-"
+ (int) ((Math.random() * 10000));
return sessionId;
}
}
log4j配置,增加日志追踪号变量:
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg表示日志消息,%n表示换行符-->
<property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{sessionId}] %-5level [%thread] %logger{80} :%msg%n" />
最终效果如下:
是不是相当实用呢。
相关文章推荐
- 程序员你为什么这么累【续】:编码习惯之日志建议
- 程序员你为什么这么累:编码习惯之业务异常
- 程序员你为什么这么累:编码习惯之异常处理
- 程序员你为什么这么累【续】:编码习惯之接口定义
- 程序员你为什么这么累【续】:编码习惯之异常处理
- 编码习惯之日志建议
- 程序员你为什么这么累 | 编码规范
- 为什么程序员这么讨厌被人问「会不会修电脑」?
- “80后”程序员的薪水为什么这么低?
- 程序员为什么这么累?
- c#好的程序员必须掌握的编码习惯
- “苹果”为什么这么大 (入选推荐日志,加10币)
- 优秀程序员的45个习惯之33——记录问题解决日志
- 为什么我们程序员总是这么累、做出来的东西这么差?
- 企业编码工作为什么这么难?
- 中国的程序员为什么这么辛苦?
- 为什么成为优秀程序员这么难
- 现在找一个程序员的工作为什么这么难?
- 为什么成为优秀程序员这么难
- 【连载】优秀程序员的45个习惯之33――记录问题解决日志