You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2020/10/04 01:03:49 UTC
[logging-log4j2] branch release-2.x updated: [LOG4J2-2889]
HtmlLayout support datePattern and timezone
This is an automated email from the ASF dual-hosted git repository.
mattsicker pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/release-2.x by this push:
new 6673bd9 [LOG4J2-2889] HtmlLayout support datePattern and timezone
new e69401d Merge pull request #423 from gengyuanzhe/release-2.x
6673bd9 is described below
commit 6673bd9ed8d96dbe5c0c76876b3542bc4aeb6a09
Author: gengyuanzhe <ge...@gmail.com>
AuthorDate: Sun Sep 20 22:59:42 2020 +0800
[LOG4J2-2889] HtmlLayout support datePattern and timezone
---
.../logging/log4j/core/layout/HtmlLayout.java | 36 +++++-
.../logging/log4j/core/layout/HtmlLayoutTest.java | 123 +++++++++++++++++++++
.../core/pattern/DatePatternConverterTest.java | 4 +-
src/site/xdoc/manual/layouts.xml.vm | 28 +++++
4 files changed, 185 insertions(+), 6 deletions(-)
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/HtmlLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/HtmlLayout.java
index efc3407..ea9d7e8 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/HtmlLayout.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/HtmlLayout.java
@@ -37,6 +37,7 @@ import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.pattern.DatePatternConverter;
import org.apache.logging.log4j.core.util.Transform;
import org.apache.logging.log4j.util.Strings;
@@ -59,6 +60,7 @@ public final class HtmlLayout extends AbstractStringLayout {
private static final String REGEXP = Strings.LINE_SEPARATOR.equals("\n") ? "\n" : Strings.LINE_SEPARATOR + "|\n";
private static final String DEFAULT_TITLE = "Log4j Log Messages";
private static final String DEFAULT_CONTENT_TYPE = "text/html";
+ private static final String DEFAULT_DATE_PATTERN = "JVM_ELAPSE_TIME";
private final long jvmStartTime = ManagementFactory.getRuntimeMXBean().getStartTime();
@@ -69,6 +71,7 @@ public final class HtmlLayout extends AbstractStringLayout {
private final String font;
private final String fontSize;
private final String headerSize;
+ private final DatePatternConverter datePatternConverter;
/**Possible font sizes */
public static enum FontSize {
@@ -100,7 +103,7 @@ public final class HtmlLayout extends AbstractStringLayout {
}
private HtmlLayout(final boolean locationInfo, final String title, final String contentType, final Charset charset,
- final String font, final String fontSize, final String headerSize) {
+ final String font, final String fontSize, final String headerSize, String datePattern, String timezone) {
super(charset);
this.locationInfo = locationInfo;
this.title = title;
@@ -108,6 +111,8 @@ public final class HtmlLayout extends AbstractStringLayout {
this.font = font;
this.fontSize = fontSize;
this.headerSize = headerSize;
+ this.datePatternConverter = DEFAULT_DATE_PATTERN.equals(datePattern) ? null
+ : DatePatternConverter.newInstance(new String[] {datePattern, timezone});
}
/**
@@ -149,7 +154,12 @@ public final class HtmlLayout extends AbstractStringLayout {
sbuf.append(Strings.LINE_SEPARATOR).append("<tr>").append(Strings.LINE_SEPARATOR);
sbuf.append("<td>");
- sbuf.append(event.getTimeMillis() - jvmStartTime);
+
+ if (datePatternConverter == null) {
+ sbuf.append(event.getTimeMillis() - jvmStartTime);
+ } else {
+ datePatternConverter.format(event, sbuf);
+ }
sbuf.append("</td>").append(Strings.LINE_SEPARATOR);
final String escapedThread = Transform.escapeHtmlTags(event.getThreadName());
@@ -339,6 +349,7 @@ public final class HtmlLayout extends AbstractStringLayout {
* @param font The font to use for the text.
* @return An HTML Layout.
*/
+ @Deprecated
@PluginFactory
public static HtmlLayout createLayout(
@PluginAttribute(value = "locationInfo") final boolean locationInfo,
@@ -353,7 +364,8 @@ public final class HtmlLayout extends AbstractStringLayout {
if (contentType == null) {
contentType = DEFAULT_CONTENT_TYPE + "; charset=" + charset;
}
- return new HtmlLayout(locationInfo, title, contentType, charset, font, fontSize, headerSize);
+ return new HtmlLayout(locationInfo, title, contentType, charset, font, fontSize, headerSize, DEFAULT_DATE_PATTERN,
+ null);
}
/**
@@ -390,6 +402,12 @@ public final class HtmlLayout extends AbstractStringLayout {
@PluginBuilderAttribute
private String fontName = DEFAULT_FONT_FAMILY;
+ @PluginBuilderAttribute
+ private String datePattern = DEFAULT_DATE_PATTERN;
+
+ @PluginBuilderAttribute
+ private String timezone = null; // null means default timezone
+
private Builder() {
}
@@ -423,6 +441,16 @@ public final class HtmlLayout extends AbstractStringLayout {
return this;
}
+ public Builder setDatePattern(final String datePattern) {
+ this.datePattern = datePattern;
+ return this;
+ }
+
+ public Builder setTimezone(final String timezone) {
+ this.timezone = timezone;
+ return this;
+ }
+
@Override
public HtmlLayout build() {
// TODO: extract charset from content-type
@@ -430,7 +458,7 @@ public final class HtmlLayout extends AbstractStringLayout {
contentType = DEFAULT_CONTENT_TYPE + "; charset=" + charset;
}
return new HtmlLayout(locationInfo, title, contentType, charset, fontName, fontSize.getFontSize(),
- fontSize.larger().getFontSize());
+ fontSize.larger().getFontSize(), datePattern, timezone);
}
}
}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/HtmlLayoutTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/HtmlLayoutTest.java
index b6ad4c4..8915e9b 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/HtmlLayoutTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/HtmlLayoutTest.java
@@ -16,27 +16,69 @@
*/
package org.apache.logging.log4j.core.layout;
+import java.lang.management.ManagementFactory;
import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.AbstractLogEvent;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.BasicConfigurationFactory;
+import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.time.Instant;
+import org.apache.logging.log4j.core.time.MutableInstant;
import org.apache.logging.log4j.junit.UsingAnyThreadContext;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.test.appender.ListAppender;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
+import static org.apache.logging.log4j.core.util.datetime.FixedDateFormat.FixedFormat;
@UsingAnyThreadContext
public class HtmlLayoutTest {
+ private static class MyLogEvent extends AbstractLogEvent {
+ private static final long serialVersionUID = 0;
+
+ @Override
+ public Instant getInstant() {
+ MutableInstant result = new MutableInstant();
+ result.initFromEpochMilli(getTimeMillis(), 456789);
+ return result;
+ }
+
+ @Override
+ public long getTimeMillis() {
+ final Calendar cal = Calendar.getInstance();
+ cal.set(2012, Calendar.NOVEMBER, 02, 14, 34, 02);
+ cal.set(Calendar.MILLISECOND, 123);
+ return cal.getTimeInMillis();
+ }
+
+ @Override
+ public Level getLevel() {
+ return Level.DEBUG;
+ }
+
+ @Override
+ public Message getMessage() {
+ return new SimpleMessage("msg");
+ }
+ }
+
private final LoggerContext ctx = LoggerContext.getContext();
private final Logger root = ctx.getRootLogger();
@@ -150,4 +192,85 @@ public class HtmlLayoutTest {
root.addAppender(app);
}
}
+
+ @Test
+ public void testLayoutWithoutDataPattern() {
+ final HtmlLayout layout = HtmlLayout.newBuilder().build();
+
+ MyLogEvent event = new MyLogEvent();
+ String actual = getDateLine(layout.toSerializable(event));
+
+ long jvmStratTime = ManagementFactory.getRuntimeMXBean().getStartTime();
+ assertEquals("<td>" + (event.getTimeMillis() - jvmStratTime) + "</td>", actual, "Incorrect date:" + actual);
+ }
+
+ @Test
+ public void testLayoutWithDatePatternJvmElapseTime() {
+ final HtmlLayout layout = HtmlLayout.newBuilder().setDatePattern("JVM_ELAPSE_TIME").build();
+
+ MyLogEvent event = new MyLogEvent();
+ String actual = getDateLine(layout.toSerializable(event));
+
+ long jvmStratTime = ManagementFactory.getRuntimeMXBean().getStartTime();
+ assertEquals("<td>" + (event.getTimeMillis() - jvmStratTime) + "</td>", actual, "Incorrect date:" + actual);
+ }
+
+ @Test
+ public void testLayoutWithDatePatternUnix() {
+ final HtmlLayout layout = HtmlLayout.newBuilder().setDatePattern("UNIX").build();
+
+ MyLogEvent event = new MyLogEvent();
+ String actual = getDateLine(layout.toSerializable(event));
+
+ assertEquals("<td>" + event.getInstant().getEpochSecond() + "</td>", actual, "Incorrect date:" + actual);
+ }
+
+ @Test
+ public void testLayoutWithDatePatternUnixMillis() {
+ final HtmlLayout layout = HtmlLayout.newBuilder().setDatePattern("UNIX_MILLIS").build();
+
+ MyLogEvent event = new MyLogEvent();
+ String actual = getDateLine(layout.toSerializable(event));
+
+ assertEquals("<td>" + event.getTimeMillis() + "</td>", actual, "Incorrect date:" + actual);
+ }
+
+ @Test
+ public void testLayoutWithDatePatternFixedFormat() {
+ for (final String timeZone : new String[] {"GMT+8", "UTC", null}) {
+ for (final FixedFormat format : FixedFormat.values()) {
+ testLayoutWithDatePatternFixedFormat(format, timeZone);
+ }
+ }
+ }
+
+ private String getDateLine(String logEventString) {
+ return logEventString.split(System.lineSeparator())[2];
+ }
+
+ private void testLayoutWithDatePatternFixedFormat(FixedFormat format, String timezone) {
+ final HtmlLayout layout = HtmlLayout.newBuilder().setDatePattern(format.name()).setTimezone(timezone).build();
+
+ LogEvent event = new MyLogEvent();
+ String actual = getDateLine(layout.toSerializable(event));
+
+ // build expected date string
+ java.time.Instant instant =
+ java.time.Instant.ofEpochSecond(event.getInstant().getEpochSecond(), event.getInstant().getNanoOfSecond());
+ ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault());
+ if (timezone != null) {
+ zonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of(timezone));
+ }
+
+ // For DateTimeFormatter of jdk,
+ // Pattern letter 'S' means fraction-of-second, 'n' means nano-of-second. Log4j2 needs S.
+ // Pattern letter 'X' (upper case) will output 'Z' when the offset to be output would be zero,
+ // whereas pattern letter 'x' (lower case) will output '+00', '+0000', or '+00:00'. Log4j2 needs x.
+ DateTimeFormatter dateTimeFormatter =
+ DateTimeFormatter.ofPattern(format.getPattern().replace('n', 'S').replace('X', 'x'));
+ String expected = zonedDateTime.format(dateTimeFormatter);
+
+ assertEquals("<td>" + expected + "</td>", actual,
+ MessageFormat.format("Incorrect date={0}, format={1}, timezone={2}", actual, format.name(), timezone));
+ }
}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java
index 04a93b9..b3c7499 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java
@@ -417,7 +417,7 @@ public class DatePatternConverterTest {
final StringBuilder milliBuilder = new StringBuilder();
final LogEvent event = new MyLogEvent();
- for (final String timeZone : new String[]{"PDT", null}) { // Pacific Daylight Time=UTC-8:00
+ for (final String timeZone : new String[]{"PST", null}) { // Pacific Standard Time=UTC-8:00
for (final FixedDateFormat.FixedFormat format : FixedDateFormat.FixedFormat.values()) {
for (int i = 1; i <= 9; i++) {
final String pattern = format.getPattern();
@@ -467,7 +467,7 @@ public class DatePatternConverterTest {
@Test
public void testPredefinedFormatWithTimezone() {
for (final FixedDateFormat.FixedFormat format : FixedDateFormat.FixedFormat.values()) {
- final String[] options = {format.name(), "PDT"}; // Pacific Daylight Time=UTC-8:00
+ final String[] options = {format.name(), "PST"}; // Pacific Standard Time=UTC-8:00
final DatePatternConverter converter = DatePatternConverter.newInstance(options);
assertEquals(format.getPattern(), converter.getPattern());
}
diff --git a/src/site/xdoc/manual/layouts.xml.vm b/src/site/xdoc/manual/layouts.xml.vm
index 8b56f0a..52b415d 100644
--- a/src/site/xdoc/manual/layouts.xml.vm
+++ b/src/site/xdoc/manual/layouts.xml.vm
@@ -447,8 +447,35 @@ logger.debug("one={}, two={}, three={}", 1, 2, 3);
<td>String</td>
<td>The <code>font-size</code> to use. The default is "small".</td>
</tr>
+ <tr>
+ <td>datePattern</td>
+ <td>String</td>
+ <td>The date format of the logging event. The default is "JVM_ELAPSE_TIME", which outputs the
+ milliseconds since JVM started. For other valid values, refer to the
+ <a href="#PatternDate">date pattern</a> of PatternLayout.</td>
+ </tr>
+ <tr>
+ <td>timezone</td>
+ <td>String</td>
+ <td>The timezone id of the logging event. If not specified, this layout uses the
+ <a class="javadoc" href="${javadocRoot}/java/util/TimeZone.html${sharp}getDefault()">
+ java.util.TimeZone.getDefault</a> as default timezone.
+ Like <a href="#PatternDate">date pattern</a> of PatternLayout, you can use timezone id from
+ <a class="javadoc" href="${javadocRoot}/java/util/TimeZone.html${sharp}getTimeZone(java.lang.String)">
+ java.util.TimeZone.getTimeZone</a>.</td>
+ </tr>
<caption align="top">HtmlLayout Parameters</caption>
</table>
+ <p>
+ Configure as follows to use dataPattern and timezone in HtmlLayout:
+ </p>
+ <pre class="prettyprint linenums">
+<Appenders>
+ <Console name="console">
+ <HtmlLayout datePattern="ISO8601" timezone="GMT+0"/>
+ </Console>
+</Appenders>
+</pre>
</subsection>
<a name="JSONLayout"/>
<subsection name="JSON Layout">
@@ -850,6 +877,7 @@ WARN [main]: Message 2</pre>
</tr>
<tr>
<td align="center">
+ <a name="PatternDate" />
<b>d</b>{pattern}<br />
<b>date</b>{pattern}
</td>