代码人,废话不多,直接上代码
主要重写部分,请关注以下代码和OdySession.getTraceTicket()相关联处!
还有一种解决方案,应用Log4j MDC技术,相关文档请搜索Log4j MDC。
MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。
package com.zcm.log; import com.odianyun.architecture.caddy.trace.session.OdySession; import org.apache.log4j.Layout; import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.helpers.PatternParser; import org.apache.log4j.helpers.PatternConverter; public class ZcmPatternLayout extends Layout { public final static String DEFAULT_CONVERSION_PATTERN ="%m%n"; public final static String TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %x - %m%n"; protected final int BUF_SIZE = 256; protected final int MAX_CAPACITY = 1024; private StringBuffer sbuf = new StringBuffer(BUF_SIZE); private String pattern; private PatternConverter head; public ZcmPatternLayout() { this(DEFAULT_CONVERSION_PATTERN); } public ZcmPatternLayout(String pattern) { this.pattern = pattern; head = createPatternParser((pattern == null) ? DEFAULT_CONVERSION_PATTERN : pattern).parse(); } public void setConversionPattern(String conversionPattern) { pattern = conversionPattern; head = createPatternParser(conversionPattern).parse(); } public String getConversionPattern() { return pattern; } public void activateOptions() { // nothing to do. } public boolean ignoresThrowable() { return true; } protected PatternParser createPatternParser(String pattern) { return new PatternParser(pattern); } public String format(LoggingEvent event) { // Reset working stringbuffer if(sbuf.capacity() > MAX_CAPACITY) { sbuf = new StringBuffer(BUF_SIZE); } else { sbuf.setLength(0); } PatternConverter c = head; while(c != null) { c.format(sbuf, event); c = c.next; } String ticket = OdySession.getTraceTicket(); return ticket == null?this.sbuf.toString():"[ticket:" + OdySession.getTraceTicket() + "] " + this.sbuf.toString(); } }
package com.zcm.log; import com.odianyun.architecture.caddy.trace.session.OdySession; import org.apache.log4j.FileAppender; import org.apache.log4j.Layout; import org.apache.log4j.helpers.LogLog; import org.apache.log4j.spi.LoggingEvent; import java.io.File; import java.io.IOException; import java.io.InterruptedIOException; import java.text.SimpleDateFormat; import java.util.*; import java.io.IOException; import java.io.File; import java.io.InterruptedIOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.GregorianCalendar; import java.util.Calendar; import java.util.TimeZone; import java.util.Locale; import org.apache.log4j.helpers.LogLog; import org.apache.log4j.spi.LoggingEvent; public class ZcmRollingFileAppender extends FileAppender { // The code assumes that the following constants are in a increasing // sequence. static final int TOP_OF_TROUBLE=-1; static final int TOP_OF_MINUTE = 0; static final int TOP_OF_HOUR = 1; static final int HALF_DAY = 2; static final int TOP_OF_DAY = 3; static final int TOP_OF_WEEK = 4; static final int TOP_OF_MONTH = 5; /** The date pattern. By default, the pattern is set to "'.'yyyy-MM-dd" meaning daily rollover. */ private String datePattern = "'.'yyyy-MM-dd"; /** The log file will be renamed to the value of the scheduledFilename variable when the next interval is entered. For example, if the rollover period is one hour, the log file will be renamed to the value of "scheduledFilename" at the beginning of the next hour. The precise time when a rollover occurs depends on logging activity. */ private String scheduledFilename; /** The next time we estimate a rollover should occur. */ private long nextCheck = System.currentTimeMillis () - 1; Date now = new Date(); SimpleDateFormat sdf; RollingCalendar rc = new RollingCalendar(); int checkPeriod = TOP_OF_TROUBLE; // The gmtTimeZone is used only in computeCheckPeriod() method. static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT"); /** The default constructor does nothing. */ public ZcmRollingFileAppender() { } /** Instantiate a <code>DailyRollingFileAppender</code> and open the file designated by <code>filename</code>. The opened filename will become the ouput destination for this appender. */ public ZcmRollingFileAppender (Layout layout, String filename, String datePattern) throws IOException { super(layout, filename, true); this.datePattern = datePattern; activateOptions(); } /** The <b>DatePattern</b> takes a string in the same format as expected by {@link SimpleDateFormat}. This options determines the rollover schedule. */ public void setDatePattern(String pattern) { datePattern = pattern; } /** Returns the value of the <b>DatePattern</b> option. */ public String getDatePattern() { return datePattern; } public void activateOptions() { super.activateOptions(); if(datePattern != null && fileName != null) { now.setTime(System.currentTimeMillis()); sdf = new SimpleDateFormat(datePattern); int type = computeCheckPeriod(); printPeriodicity(type); rc.setType(type); File file = new File(fileName); scheduledFilename = fileName+sdf.format(new Date(file.lastModified())); } else { LogLog.error("Either File or DatePattern options are not set for appender [" + name + "]."); } } void printPeriodicity(int type) { switch(type) { case TOP_OF_MINUTE: LogLog.debug("Appender ["+name+"] to be rolled every minute."); break; case TOP_OF_HOUR: LogLog.debug("Appender ["+name +"] to be rolled on top of every hour."); break; case HALF_DAY: LogLog.debug("Appender ["+name +"] to be rolled at midday and midnight."); break; case TOP_OF_DAY: LogLog.debug("Appender ["+name +"] to be rolled at midnight."); break; case TOP_OF_WEEK: LogLog.debug("Appender ["+name +"] to be rolled at start of week."); break; case TOP_OF_MONTH: LogLog.debug("Appender ["+name +"] to be rolled at start of every month."); break; default: LogLog.warn("Unknown periodicity for appender ["+name+"]."); } } // This method computes the roll over period by looping over the // periods, starting with the shortest, and stopping when the r0 is // different from from r1, where r0 is the epoch formatted according // the datePattern (supplied by the user) and r1 is the // epoch+nextMillis(i) formatted according to datePattern. All date // formatting is done in GMT and not local format because the test // logic is based on comparisons relative to 1970-01-01 00:00:00 // GMT (the epoch). int computeCheckPeriod() { RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone, Locale.getDefault()); // set sate to 1970-01-01 00:00:00 GMT Date epoch = new Date(0); if(datePattern != null) { for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern); simpleDateFormat.setTimeZone(gmtTimeZone); // do all date formatting in GMT String r0 = simpleDateFormat.format(epoch); rollingCalendar.setType(i); Date next = new Date(rollingCalendar.getNextCheckMillis(epoch)); String r1 = simpleDateFormat.format(next); //System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1); if(r0 != null && r1 != null && !r0.equals(r1)) { return i; } } } return TOP_OF_TROUBLE; // Deliberately head for trouble... } /** Rollover the current file to a new file. */ void rollOver() throws IOException { /* Compute filename, but only if datePattern is specified */ if (datePattern == null) { errorHandler.error("Missing DatePattern option in rollOver()."); return; } String datedFilename = fileName+sdf.format(now); // It is too early to roll over because we are still within the // bounds of the current interval. Rollover will occur once the // next interval is reached. if (scheduledFilename.equals(datedFilename)) { return; } // close current file, and rename it to datedFilename this.closeFile(); File target = new File(scheduledFilename); if (target.exists()) { target.delete(); } File file = new File(fileName); boolean result = file.renameTo(target); if(result) { LogLog.debug(fileName +" -> "+ scheduledFilename); } else { LogLog.error("Failed to rename ["+fileName+"] to ["+scheduledFilename+"]."); } try { // This will also close the file. This is OK since multiple // close operations are safe. this.setFile(fileName, true, this.bufferedIO, this.bufferSize); } catch(IOException e) { errorHandler.error("setFile("+fileName+", true) call failed."); } scheduledFilename = datedFilename; } /** * This method differentiates DailyRollingFileAppender from its * super class. * * <p>Before actually logging, this method will check whether it is * time to do a rollover. If it is, it will schedule the next * rollover time and then rollover. * */ protected void subAppend(LoggingEvent event) { long n = System.currentTimeMillis(); if (n >= nextCheck) { now.setTime(n); nextCheck = rc.getNextCheckMillis(now); try { rollOver(); } catch(IOException ioe) { if (ioe instanceof InterruptedIOException) { Thread.currentThread().interrupt(); } LogLog.error("rollOver() failed.", ioe); } } this.qw.write(this.layout.format(event)); if(layout.ignoresThrowable()) { String[] s = event.getThrowableStrRep(); if (s != null) { int len = s.length; for(int i = 0; i < len; i++) { if (i == 0){ this.qw.write("[ticket:" + OdySession.getTraceTicket() + "] "); } this.qw.write(s[i]); this.qw.write(Layout.LINE_SEP); } } } if(shouldFlush(event)) { this.qw.flush(); } } } /** * RollingCalendar is a helper class to DailyRollingFileAppender. * Given a periodicity type and the current time, it computes the * start of the next interval. * */ class RollingCalendar extends GregorianCalendar { private static final long serialVersionUID = -3560331770601814177L; int type = ZcmRollingFileAppender.TOP_OF_TROUBLE; RollingCalendar() { super(); } RollingCalendar(TimeZone tz, Locale locale) { super(tz, locale); } void setType(int type) { this.type = type; } public long getNextCheckMillis(Date now) { return getNextCheckDate(now).getTime(); } public Date getNextCheckDate(Date now) { this.setTime(now); switch(type) { case ZcmRollingFileAppender.TOP_OF_MINUTE: this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.MINUTE, 1); break; case ZcmRollingFileAppender.TOP_OF_HOUR: this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.HOUR_OF_DAY, 1); break; case ZcmRollingFileAppender.HALF_DAY: this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); int hour = get(Calendar.HOUR_OF_DAY); if(hour < 12) { this.set(Calendar.HOUR_OF_DAY, 12); } else { this.set(Calendar.HOUR_OF_DAY, 0); this.add(Calendar.DAY_OF_MONTH, 1); } break; case ZcmRollingFileAppender.TOP_OF_DAY: this.set(Calendar.HOUR_OF_DAY, 0); this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.DATE, 1); break; case ZcmRollingFileAppender.TOP_OF_WEEK: this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek()); this.set(Calendar.HOUR_OF_DAY, 0); this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.WEEK_OF_YEAR, 1); break; case ZcmRollingFileAppender.TOP_OF_MONTH: this.set(Calendar.DATE, 1); this.set(Calendar.HOUR_OF_DAY, 0); this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.MONTH, 1); break; default: throw new IllegalStateException("Unknown periodicity type."); } return getTime(); } }
相关推荐
接下来,我们谈谈如何自定义日志输出格式。Log4j的输出格式主要通过`Layout`组件来控制,最常见的`PatternLayout`允许我们使用占位符来指定输出格式。例如,`%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n`会输出...
总结来说,动态设置Log4j的日志级别是提高应用灵活性的重要手段,可以根据运行时的需求调整日志输出的详细程度,同时避免不必要的重启操作。通过配置文件、API、JMX以及Web界面等多种方式,开发者可以方便地实现这一...
2. **自定义日志输出格式**:默认情况下,Log4j会打印出时间戳、日志级别、logger名和日志消息。我们可以通过自定义`Layout`实现,例如创建一个XML布局或者JSON布局,使得日志输出更符合项目需求。 3. **自定义日志...
Log4j的核心在于其灵活性,可以通过自定义布局模板来控制日志输出的格式。最常用的布局模板类是PatternLayout,它允许用户通过特定的模式字符串定义输出格式。例如,一个常见的模式是`%d{yyyy-MM-dd HH:mm:ss} %-5p ...
在项目的配置文件(通常是`log4j.properties`或`log4j.xml`)中,需要定义这些自定义级别的输出目的地和格式,这样日志处理器才能正确地处理并记录这些级别的信息。 5. **在代码中使用**: 最后,应用程序中的...
要实现输出多个自定义路径的日志文件,我们需要在Log4j的配置文件(通常是`log4j.properties`或`log4j.xml`)中定义多个Appender。Appender是Log4j中负责将日志信息输出到指定目的地的组件,例如文件、控制台、网络...
使用Log4j2的Layout组件定义日志输出格式。例如,我们可以使用PatternLayout,并自定义模式来指定哪些字段需要脱敏。例如,`%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n` 是一个基本的日志...
`log4j配置输出日志案例.doc`文档中应该包含了更详细的配置和实际运行案例,包括如何调整日志级别、设置过滤器、自定义日志格式等。这些案例可以帮助读者更好地理解和应用log4j,解决实际开发中的日志管理问题。 ##...
参照了几个网上大神配置,部分教程的描述有误,最终调试完成,可以实现kettle日志输出,测试版本...需要替换的文件为,Kettle的程序目录下data-integration-6.0\plugins\kettle5-log4j-plugin中有一个log4j.xml文件。
Log4j是一款广泛使用的日志框架,它允许开发者自定义日志输出的方式,包括输出路径、日志级别和格式。在某些情况下,可能需要将不同类型的日志输出到不同的文件中,以方便管理和分析。本文将详细讲解如何使用Log4j...
在IT行业中,日志记录是系统调试和故障排查的关键环节,Log4net作为一个强大的日志框架,被广泛应用于.NET应用程序中。本主题聚焦于如何利用Log4net将自定义信息,如变量、属性或字段,存储到数据库中,这对于监控...
总结来说,Log4j提供了丰富的功能来满足日志输出的需求。通过配置多个Appender,我们可以实现日志的多文件输出;通过定制日志级别,我们可以更精细化地控制日志记录。这使得在复杂的项目环境中,日志管理变得更加...
在“Log4j2实现不同线程不同级别日志输出到不同的文件中”这个主题中,我们将深入探讨如何利用Log4j2实现这一高级日志管理需求。 1. **日志级别**: - 在Log4j2中,日志级别包括DEBUG、INFO、WARN、ERROR、FATAL...
4. 定义日志输出格式:通过配置log4j.appender.CATALINA.layout为org.apache.log4j.PatternLayout,可以定义日志的格式。例如,可以设置为%d{yyyy-MM-ddHH:mm:ss.SSS}%p[%t]%c|%m%n来让日志格式和Log4j保持一致。 5...
Log4j是一个广泛使用的Java日志框架,它提供了强大的日志处理功能,包括日志级别控制、自定义日志格式、多路复用等。然而,由于Android系统对第三方库的限制,直接在Android工程中使用Log4j需要一些额外的步骤。本文...
例如,若要将日志输出到名为"tomcat.log"的文件,并设置日志级别为INFO,"log4j.properties"文件可能包含以下内容: ``` log4j.rootLogger=INFO, FILE log4j.appender.FILE=org.apache.log4j.FileAppender log4j....
这是log4j框架的配置文件,使用Java Properties格式,用于定义日志输出的行为。主要包括以下几个核心部分: 1. **Logger**: 定义日志级别,例如DEBUG、INFO、WARN、ERROR和FATAL。日志级别决定了哪些信息会被记录。...
Log4j提供了一个名为`org.apache.log4j.ConsoleAppender`的类,用于将日志输出到控制台,我们可以通过自定义Filter或者继承现有Appender来实现这个功能。 下面是一个简单的示例,展示如何在Log4j配置文件中设置...
Log4j通过配置文件(通常是log4j.properties或log4j.xml)来设定日志输出级别、格式、目的地等参数。如果日志没有按预期输出,我们需要检查以下几个方面: 1. **配置文件**:确认配置文件是否正确包含在项目中,...
- **配置文件**:利用Log4J的配置文件(如log4j.properties)来指定日志输出的位置、格式等。 - **自定义Appender**:为了满足将日志信息同时写入文件和数据库的需求,需要开发自定义的Appender组件。这种Appender...