浏览 7155 次
锁定老帖子 主题:关于日志的设计模式讨论
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-10-12
背景: 1.有很多用户同时使用的应用软件;日志是按时间顺序打印出来的;不同用户的日志是交叉在一起; 在日志文件里一般很难区分出专属于某个用户的日志; 如果应用软件出现问题,拿到现场的日志,很难分析; 2.用户操作可能是一个流程 比如:登陆-->访问自服务-->导出报表 现在如何在日志文件中找到某个用户的流程日志? 抽象的日志模型: 1.线程日志:定义为用户操作某个功能后打印出的相关日志;日志可能在A.class,B.class打印; 2.流程日志:多个线程组成一个流程,由这几个线程打印出的日志; 解决访问: 1.线程日志:这个比较容易解决,为每个线程生成一个线程号即可; 2.流程日志:在不改变原先应用软件模型的基础上,暂时没想到好的解决办法; 有想到能否在应用软件级别中,实现类似服务器中的session的管理,但不知道session如何管理,log4j又怎么知道用户的session; 在可以改变应用软件模型的基础上: 建议用request.session.sessionId,也就是保证在调用log4j打印日志的时候,得到这个sessionId,并将sessionId打印出来; 抛庄引... 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-10-12
已经找到解决方案了
------------------------------------------------------- 最终实现:用户登陆系统后,能捕获到跟该用户相关的所有的日志; 用户访问系统输出日志如下: [3*99N1192177074953 ahau205109] 2007-10-12 16:20:08 用户登陆 [3*99N1192177074953 ahau205109] 2007-10-12 16:20:09 检查用户密码有效性 [3*99N1192177074953 ahau205109] 2007-10-12 16:20:10 登陆成功 用户登陆系统后,访问某个功能页面,输出日志如下: [3*99N1192177074953 ahau205109] 2007-10-12 16:20:18 导出报表[财务] [3*99N1192177074953 ahau205109] 2007-10-12 16:20:28 导出报表[成功] 整个日志文件内容: [3*99N1192177074953 ahau205109] 2007-10-12 16:20:08 用户登陆 [3*99N1192177074953 ahau205109] 2007-10-12 16:20:09 检查用户密码有效性 [3*99N1192177074954 武功] 2007-10-12 16:20:10 用户登陆 [3*99N1192177074953 ahau205109] 2007-10-12 16:20:12 登陆成功 [3*99N1192177074954 武功] 2007-10-12 16:20:16 检查用户密码有效性 [3*99N1192177074954 武功] 2007-10-12 16:20:18 登陆成功 [3*99N1192177074953 ahau205109] 2007-10-12 16:20:20 导出报表[财务] [3*99N1192177074953 ahau205109] 2007-10-12 16:20:22 导出报表[成功] [3*99N1192177074954 武功] 2007-10-12 16:20:25 添加操作员 [3*99N1192177074954 武功] 2007-10-12 16:20:30 添加成功 根据流程标志:比如 [3*99N1192177074953 ahau205109], [3*99N1192177074954 武功] 就能很容易的从日志文件分析到日志了; 实现过程: 1.扩展log4j的 1) LogginEvent:增加字段及构造函数 String flow ; //[流程标志号] 2) Category:增加方法 在原先的log,error,info,debug等方法中增加参数,String flow 3) ConsoleAppender 扩展 重写方法: protected void subAppend(LoggingEvent event) 1. 将event的flow标志缓存起来 2. 如果缓存不存在flow的流程号,则建立之 3. 维护缓存更新策略 4.定义输出日志格式,增加流程号 |
|
返回顶楼 | |
发表时间:2007-10-13
给个源代码来看看
|
|
返回顶楼 | |
发表时间:2007-10-13
又做了个扩展,无需修改log4j源代码,也无需修改原先所有的业务逻辑代码;做成插件模式
1.添加类 package log; public class LoggingFlow { private LoggingFlow() {} public LoggingFlow(Object flow) { this.flow = flow; } private Object flow; public Object getFlow() { return flow; } public void setFlow(Object flow) { this.flow = flow; } } package log; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.Layout; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; public class ConsoleAppenderEx extends ConsoleAppender { ThreadLocal currentThread = new ThreadLocal(); static String TA = "[TA]"; //事务ID /** * [TA] 的生成规则 Thread:seq0000001;User:user01; ***/ protected void subAppend(LoggingEvent event) { if (event.getMessage() instanceof LoggingFlow) { LoggingFlow flow = (LoggingFlow) event.getMessage(); if (flow.getFlow() != null && flow.getFlow() instanceof HttpServletRequest) { init((HttpServletRequest) flow.getFlow()); } else if (flow.getFlow() != null) { init(flow.getFlow()); } return; } String flow = String.valueOf(currentThread.get() == null ? "System" : currentThread.get()); //flow = StringUtils.rightPad(flow, 40); String message = StringUtils.replace(super.layout.format(event), ConsoleAppenderEx.TA, flow); qw.write(message); if (super.layout.ignoresThrowable()) { String s[] = event.getThrowableStrRep(); if (s != null) { int len = s.length; for (int i = 0; i < len; i++) { qw.write(s[i]); qw.write(Layout.LINE_SEP); } } } if (immediateFlush) { qw.flush(); } } public Object getUser(HttpServletRequest request){ //自己实现... return null; } private void init(HttpServletRequest request) { Object obj = null; //从request获取用户信息 obj = getUser(request); //同时记录用户所属的ip,如果不存在用户,则记录ip if (obj == null) { obj = request.getRemoteAddr(); } else { if (!request.getRemoteAddr().equalsIgnoreCase(obj.toString())) { obj = request.getRemoteAddr() + " " + obj; } } init(obj); } private void init(Object object) { Object obj = currentThread.get(); if (object != null) { obj = object; } currentThread.set(obj); } } package log; import java.io.*; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; public class LoggingFilter implements Filter { /** * Called by the web container to indicate to a filter that it is being taken out of service. * * @todo Implement this javax.servlet.Filter method */ public void destroy() { } /** * The <code>doFilter</code> method of the Filter is called by the container each time a request/response pair is * passed through the chain due to a client request for a resource at the end of the chain. * * @param request ServletRequest * @param response ServletResponse * @param chain FilterChain * @throws IOException * @throws ServletException * @todo Implement this javax.servlet.Filter method */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { org.apache.log4j.Logger.getInstance(this.getClass()).error(new log.LoggingFlow((HttpServletRequest)request)); chain.doFilter(request,response); } /** * Called by the web container to indicate to a filter that it is being placed into service. * * @param filterConfig FilterConfig * @throws ServletException * @todo Implement this javax.servlet.Filter method */ public void init(FilterConfig filterConfig) throws ServletException { } } 2.修改web.xml <filter> <filter-name>loggingFilter</filter-name> <filter-class>vas.base.common.log.LoggingFilter</filter-class> </filter> <filter-mapping> <filter-name>loggingFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> <filter-mapping> <filter-name>loggingFilter</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> 3.修改log4j.properties 的appender配置 #CONSOLE appender org.apache.log4j.ConsoleAppenderEx log4j.appender.CONSOLE=log.ConsoleAppenderEx log4j.appender.CONSOLE.Target=System.out log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=[[TA]] [%t] %d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n ok,看看代码运行效果吧 如果用户事先没登陆,则用用户的ip来访问日志; 用户登陆成功后,用用户的ip加用户的标志来访问日志 |
|
返回顶楼 | |
发表时间:2007-10-15
建议看一下log4j的NDC或者MDC。
|
|
返回顶楼 | |