You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2015/07/18 17:05:19 UTC
[2/2] logging-log4j2 git commit: LOG4J2-1076 Added support for system
nanosecond time in pattern layout
LOG4J2-1076 Added support for system nanosecond time in pattern layout
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/7f075a71
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/7f075a71
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/7f075a71
Branch: refs/heads/master
Commit: 7f075a710230484cd7f9f3850b57d4cb7ee4ec9f
Parents: 8901ffc
Author: rpopma <rp...@apache.org>
Authored: Sun Jul 19 00:05:37 2015 +0900
Committer: rpopma <rp...@apache.org>
Committed: Sun Jul 19 00:05:37 2015 +0900
----------------------------------------------------------------------
.../logging/log4j/core/AbstractLogEvent.java | 4 +
.../org/apache/logging/log4j/core/LogEvent.java | 7 +
.../logging/log4j/core/LoggerContext.java | 12 +-
.../db/jpa/AbstractLogEventWrapperEntity.java | 10 +
.../appender/db/jpa/BasicLogEventEntity.java | 19 +-
.../logging/log4j/core/async/AsyncLogger.java | 28 ++-
.../log4j/core/async/RingBufferLogEvent.java | 35 +++-
.../async/RingBufferLogEventTranslator.java | 10 +-
.../logging/log4j/core/impl/Log4jLogEvent.java | 104 ++++++++--
.../log4j/core/jackson/JsonConstants.java | 1 +
.../log4j/core/layout/JacksonFactory.java | 13 ++
.../core/pattern/NanoTimePatternConverter.java | 57 +++++
.../log4j/core/pattern/PatternParser.java | 21 +-
.../logging/log4j/core/util/DummyNanoClock.java | 48 +++++
.../logging/log4j/core/util/NanoClock.java | 30 +++
.../log4j/core/util/NanoClockFactory.java | 80 +++++++
.../log4j/core/util/SystemNanoClock.java | 33 +++
log4j-core/src/main/resources/Log4j-events.xsd | 1 +
.../apache/logging/log4j/core/LogEventTest.java | 48 +++++
.../core/appender/db/jpa/JpaH2AppenderTest.java | 2 +-
.../db/jpa/JpaHyperSqlAppenderTest.java | 2 +-
.../core/appender/db/jpa/TestBaseEntity.java | 7 +-
.../log4j/core/async/AsyncLoggerTest.java | 6 +
.../core/async/AsyncLoggerTestNanoTime.java | 92 ++++++++
.../core/async/RingBufferLogEventTest.java | 15 +-
.../core/impl/Log4jLogEventNanoTimeTest.java | 90 ++++++++
.../log4j/core/impl/Log4jLogEventTest.java | 208 +++++++++++++++++++
.../pattern/NanoTimePatternConverterTest.java | 39 ++++
.../log4j/core/pattern/PatternParserTest.java | 36 +++-
.../log4j/core/util/DummyNanoClockTest.java | 39 ++++
.../log4j/core/util/NanoClockFactoryTest.java | 65 ++++++
.../log4j/core/util/SystemNanoClockTest.java | 39 ++++
.../src/test/resources/NanoTimeToFileTest.xml | 17 ++
log4j-core/src/test/resources/log4j2-config.xml | 2 +-
.../log4j/flume/appender/FlumeEvent.java | 10 +
src/changes/changes.xml | 3 +
src/site/xdoc/manual/layouts.xml.vm | 9 +
37 files changed, 1195 insertions(+), 47 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java
index ef5374a..cd1b4e9 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java
@@ -117,4 +117,8 @@ public abstract class AbstractLogEvent implements LogEvent {
// do nothing
}
+ @Override
+ public long getNanoTime() {
+ return 0;
+ }
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
index ca05766..e89f6e5 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
@@ -170,4 +170,11 @@ public interface LogEvent extends Serializable {
*/
void setIncludeLocation(boolean locationRequired);
+ /**
+ * Returns the value of the running Java Virtual Machine's high-resolution time source when this event was created,
+ * or a dummy value if it is known that this value will not be used downstream.
+ * @return The value of the running Java Virtual Machine's high-resolution time source when this event was created.
+ * @since Log4J 2.4
+ */
+ long getNanoTime();
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
index 9072717..c61c764 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
@@ -29,6 +29,7 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.async.AsyncLogger;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationListener;
@@ -36,15 +37,17 @@ import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.DefaultConfiguration;
import org.apache.logging.log4j.core.config.NullConfiguration;
import org.apache.logging.log4j.core.config.Reconfigurable;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.core.jmx.Server;
import org.apache.logging.log4j.core.util.Cancellable;
+import org.apache.logging.log4j.core.util.NanoClockFactory;
import org.apache.logging.log4j.core.util.NetUtils;
import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
import org.apache.logging.log4j.message.MessageFactory;
import org.apache.logging.log4j.spi.AbstractLogger;
import org.apache.logging.log4j.spi.LoggerContextFactory;
-import static org.apache.logging.log4j.core.util.ShutdownCallbackRegistry.SHUTDOWN_HOOK_MARKER;
+import static org.apache.logging.log4j.core.util.ShutdownCallbackRegistry.*;
/**
* The LoggerContext is the anchor for the logging system. It maintains a list
@@ -374,6 +377,13 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi
// LOG4J2-716: Android has no java.lang.management
LOGGER.error("Could not reconfigure JMX", t);
}
+ Log4jLogEvent.setNanoClock(NanoClockFactory.createNanoClock());
+ try {
+ AsyncLogger.setNanoClock(NanoClockFactory.createNanoClock());
+ } catch (Throwable ignored) {
+ // LMAX Disruptor jar may not be in the classpath. Ignore this.
+ LOGGER.debug("Could not set AsyncLogger NanoClock. Ignoring: ", ignored);
+ }
return prev;
} finally {
configLock.unlock();
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
index 9a84eee..07dec4e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
@@ -172,6 +172,16 @@ public abstract class AbstractLogEventWrapperEntity implements LogEvent {
* @param millis Ignored.
*/
@SuppressWarnings("unused")
+ public void setNanoTime(final long nanoTime) {
+ // this entity is write-only
+ }
+
+ /**
+ * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
+ *
+ * @param millis Ignored.
+ */
+ @SuppressWarnings("unused")
public void setTimeMillis(final long millis) {
// this entity is write-only
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
index e7f46a2..a732e53 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
@@ -40,9 +40,9 @@ import org.apache.logging.log4j.message.Message;
/**
* Users of the JPA appender may want to extend this class instead of {@link AbstractLogEventWrapperEntity}. This class
* implements all of the required mutator methods but does not implement a mutable entity ID property. In order to
- * create an entity based on this class, you need only create two constructors matching this class's
- * constructors, annotate the class {@link javax.persistence.Entity @Entity} and {@link javax.persistence.Table @Table},
- * and implement the fully mutable entity ID property annotated with {@link javax.persistence.Id @Id} and
+ * create an entity based on this class, you need only create two constructors matching this class's constructors,
+ * annotate the class {@link javax.persistence.Entity @Entity} and {@link javax.persistence.Table @Table}, and implement
+ * the fully mutable entity ID property annotated with {@link javax.persistence.Id @Id} and
* {@link javax.persistence.GeneratedValue @GeneratedValue} to tell the JPA provider how to calculate an ID for new
* events.<br>
* <br>
@@ -73,7 +73,6 @@ public abstract class BasicLogEventEntity extends AbstractLogEventWrapperEntity
* signature. The no-argument constructor is required for a standards-compliant JPA provider to accept this as an
* entity.
*/
- @SuppressWarnings("unused")
public BasicLogEventEntity() {
super();
}
@@ -170,6 +169,18 @@ public abstract class BasicLogEventEntity extends AbstractLogEventWrapperEntity
}
/**
+ * Returns the value of the running Java Virtual Machine's high-resolution time source when this event was created,
+ * or a dummy value if it is known that this value will not be used downstream.
+ *
+ * @return the JVM nano time
+ */
+ @Override
+ @Basic
+ public long getNanoTime() {
+ return this.getWrappedEvent().getNanoTime();
+ }
+
+ /**
* Gets the exception logged. Annotated with {@code @Convert(converter = ThrowableAttributeConverter.class)}.
*
* @return the exception logged.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
index 9230f5b..5518472 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
@@ -17,6 +17,7 @@
package org.apache.logging.log4j.core.async;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -30,8 +31,10 @@ import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
import org.apache.logging.log4j.core.util.Clock;
import org.apache.logging.log4j.core.util.ClockFactory;
+import org.apache.logging.log4j.core.util.DummyNanoClock;
import org.apache.logging.log4j.core.util.Integers;
import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.core.util.NanoClock;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MessageFactory;
import org.apache.logging.log4j.message.TimestampMessage;
@@ -112,6 +115,7 @@ public class AsyncLogger extends Logger {
}
private static volatile Disruptor<RingBufferLogEvent> disruptor;
private static final Clock clock = ClockFactory.getClock();
+ private static volatile NanoClock nanoClock = new DummyNanoClock();
private static final ExecutorService executor = Executors
.newSingleThreadExecutor(new DaemonThreadFactory("AsyncLogger-"));
@@ -276,7 +280,9 @@ public class AsyncLogger extends Logger {
// CachedClock: 10% faster than system clock, smaller gaps
// LOG4J2-744 avoid calling clock altogether if message has the timestamp
message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() :
- clock.currentTimeMillis());
+ clock.currentTimeMillis(), //
+ nanoClock.nanoTime() //
+ );
// LOG4J2-639: catch NPE if disruptor field was set to null after our check above
try {
@@ -346,4 +352,24 @@ public class AsyncLogger extends Logger {
public static RingBufferAdmin createRingBufferAdmin(final String contextName) {
return RingBufferAdmin.forAsyncLogger(disruptor.getRingBuffer(), contextName);
}
+
+ /**
+ * Returns the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
+ * @return the {@code NanoClock} to use for creating the nanoTime timestamp of log events
+ */
+ public static NanoClock getNanoClock() {
+ return nanoClock;
+ }
+
+ /**
+ * Sets the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
+ * <p>
+ * FOR INTERNAL USE. This method may be called with a different {@code NanoClock} implementation when the
+ * configuration changes.
+ *
+ * @param nanoClock the {@code NanoClock} to use for creating the nanoTime timestamp of log events
+ */
+ public static void setNanoClock(NanoClock nanoClock) {
+ AsyncLogger.nanoClock = Objects.requireNonNull(nanoClock, "NanoClock must be non-null");
+ }
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
index 9644cfb..74feb32 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
@@ -70,11 +70,12 @@ public class RingBufferLogEvent implements LogEvent {
private long currentTimeMillis;
private boolean endOfBatch;
private boolean includeLocation;
+ private long nanoTime;
public void setValues(final AsyncLogger asyncLogger, final String loggerName, final Marker marker,
final String fqcn, final Level level, final Message data, final Throwable throwable,
final Map<String, String> map, final ContextStack contextStack, final String threadName,
- final StackTraceElement location, final long currentTimeMillis) {
+ final StackTraceElement location, final long currentTimeMillis, final long nanoTime) {
this.asyncLogger = asyncLogger;
this.loggerName = loggerName;
this.marker = marker;
@@ -88,6 +89,7 @@ public class RingBufferLogEvent implements LogEvent {
this.threadName = threadName;
this.location = location;
this.currentTimeMillis = currentTimeMillis;
+ this.nanoTime = nanoTime;
}
/**
@@ -202,6 +204,11 @@ public class RingBufferLogEvent implements LogEvent {
public long getTimeMillis() {
return currentTimeMillis;
}
+
+ @Override
+ public long getNanoTime() {
+ return nanoTime;
+ }
/**
* Merges the contents of the specified map into the contextMap, after replacing any variables in the property
@@ -246,7 +253,8 @@ public class RingBufferLogEvent implements LogEvent {
null, // contextStack
null, // threadName
null, // location
- 0 // currentTimeMillis
+ 0, // currentTimeMillis
+ 0 // nanoTime
);
}
@@ -264,4 +272,27 @@ public class RingBufferLogEvent implements LogEvent {
final LogEvent result = new Log4jLogEvent.Builder(this).build();
return result;
}
+
+ /**
+ * Initializes the specified {@code Log4jLogEvent.Builder} from this {@code RingBufferLogEvent}.
+ * @param builder the builder whose fields to populate
+ */
+ public void initializeBuilder(Log4jLogEvent.Builder builder) {
+ builder.setContextMap(contextMap) //
+ .setContextStack(contextStack) //
+ .setEndOfBatch(endOfBatch) //
+ .setIncludeLocation(includeLocation) //
+ .setLevel(getLevel()) // ensure non-null
+ .setLoggerFqcn(fqcn) //
+ .setLoggerName(loggerName) //
+ .setMarker(marker) //
+ .setMessage(getMessage()) // ensure non-null
+ .setNanoTime(nanoTime) //
+ .setSource(location) //
+ .setThreadName(threadName) //
+ .setThrown(getThrown()) // may deserialize from thrownProxy
+ .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy
+ .setTimeMillis(currentTimeMillis) //
+ ;
+ }
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
index 20e89b0..d82bd5d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
@@ -46,13 +46,14 @@ public class RingBufferLogEventTranslator implements
private String threadName;
private StackTraceElement location;
private long currentTimeMillis;
+ private long nanoTime;
// @Override
@Override
public void translateTo(final RingBufferLogEvent event, final long sequence) {
event.setValues(asyncLogger, loggerName, marker, fqcn, level, message,
thrown, contextMap, contextStack, threadName, location,
- currentTimeMillis);
+ currentTimeMillis, nanoTime);
clear();
}
@@ -72,7 +73,8 @@ public class RingBufferLogEventTranslator implements
null, // contextStack
null, // threadName
null, // location
- 0 // currentTimeMillis
+ 0, // currentTimeMillis
+ 0 // nanoTime
);
}
@@ -80,7 +82,7 @@ public class RingBufferLogEventTranslator implements
final Marker marker, final String fqcn, final Level level, final Message message,
final Throwable thrown, final Map<String, String> contextMap,
final ContextStack contextStack, final String threadName,
- final StackTraceElement location, final long currentTimeMillis) {
+ final StackTraceElement location, final long currentTimeMillis, final long nanoTime) {
this.asyncLogger = asyncLogger;
this.loggerName = loggerName;
this.marker = marker;
@@ -93,6 +95,6 @@ public class RingBufferLogEventTranslator implements
this.threadName = threadName;
this.location = location;
this.currentTimeMillis = currentTimeMillis;
+ this.nanoTime = nanoTime;
}
-
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
index 00b8b2d..c5dd320 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
@@ -29,9 +29,12 @@ import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.async.RingBufferLogEvent;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.util.Clock;
import org.apache.logging.log4j.core.util.ClockFactory;
+import org.apache.logging.log4j.core.util.DummyNanoClock;
+import org.apache.logging.log4j.core.util.NanoClock;
import org.apache.logging.log4j.message.LoggerNameAwareMessage;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.TimestampMessage;
@@ -44,6 +47,7 @@ public class Log4jLogEvent implements LogEvent {
private static final long serialVersionUID = -1351367343806656055L;
private static final Clock clock = ClockFactory.getClock();
+ private static volatile NanoClock nanoClock = new DummyNanoClock();
private final String loggerFqcn;
private final Marker marker;
private final Level level;
@@ -58,6 +62,8 @@ public class Log4jLogEvent implements LogEvent {
private StackTraceElement source;
private boolean includeLocation;
private boolean endOfBatch = false;
+ /** @since Log4J 2.4 */
+ private transient final long nanoTime;
public static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> {
@@ -75,12 +81,18 @@ public class Log4jLogEvent implements LogEvent {
private StackTraceElement source;
private boolean includeLocation;
private boolean endOfBatch = false;
+ private long nanoTime;
public Builder() {
}
public Builder(LogEvent other) {
Objects.requireNonNull(other);
+ if (other instanceof RingBufferLogEvent) {
+ RingBufferLogEvent evt = (RingBufferLogEvent) other;
+ evt.initializeBuilder(this);
+ return;
+ }
this.loggerFqcn = other.getLoggerFqcn();
this.marker = other.getMarker();
this.level = other.getLevel();
@@ -88,15 +100,23 @@ public class Log4jLogEvent implements LogEvent {
this.message = other.getMessage();
this.timeMillis = other.getTimeMillis();
this.thrown = other.getThrown();
- // avoid creating thrownProxy until necessary
- this.thrownProxy = other instanceof Log4jLogEvent ? ((Log4jLogEvent) other).thrownProxy
- : other.getThrownProxy();
this.contextMap = other.getContextMap();
this.contextStack = other.getContextStack();
- this.source = other.getSource();
- this.threadName = other.getThreadName();
this.includeLocation = other.isIncludeLocation();
this.endOfBatch = other.isEndOfBatch();
+ this.nanoTime = other.getNanoTime();
+
+ // Avoid unnecessarily initializing thrownProxy, threadName and source if possible
+ if (other instanceof Log4jLogEvent) {
+ Log4jLogEvent evt = (Log4jLogEvent) other;
+ this.thrownProxy = evt.thrownProxy;
+ this.source = evt.source;
+ this.threadName = evt.threadName;
+ } else {
+ this.thrownProxy = other.getThrownProxy();
+ this.source = other.getSource();
+ this.threadName = other.getThreadName();
+ }
}
public Builder setLevel(final Level level) {
@@ -169,10 +189,21 @@ public class Log4jLogEvent implements LogEvent {
return this;
}
+ /**
+ * Sets the nano time for the event.
+ * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event
+ * was created.
+ * @return this builder
+ */
+ public Builder setNanoTime(long nanoTime) {
+ this.nanoTime = nanoTime;
+ return this;
+ }
+
@Override
public Log4jLogEvent build() {
final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFqcn, level, message, thrown,
- thrownProxy, contextMap, contextStack, threadName, source, timeMillis);
+ thrownProxy, contextMap, contextStack, threadName, source, timeMillis, nanoTime);
result.setIncludeLocation(includeLocation);
result.setEndOfBatch(endOfBatch);
return result;
@@ -196,7 +227,8 @@ public class Log4jLogEvent implements LogEvent {
}
public Log4jLogEvent() {
- this(clock.currentTimeMillis());
+ this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, null, null,
+ clock.currentTimeMillis(), nanoClock.nanoTime());
}
/**
@@ -204,7 +236,8 @@ public class Log4jLogEvent implements LogEvent {
* @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release.
*/
public Log4jLogEvent(final long timestamp) {
- this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, null, null, timestamp);
+ this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, null, null, timestamp,
+ nanoClock.nanoTime());
}
/**
@@ -232,8 +265,7 @@ public class Log4jLogEvent implements LogEvent {
* @param properties properties to add to the event.
* @param t A Throwable or null.
*/
- // This constructor is called from DefaultLogEventFactory and is in the hot path.
- // Do not replace this constructor with a Builder.
+ // This constructor is called from LogEventFactories.
public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
final Message message, final List<Property> properties, final Throwable t) {
this(loggerName, marker, loggerFQCN, level, message, t, null,
@@ -243,7 +275,8 @@ public class Log4jLogEvent implements LogEvent {
// LOG4J2-628 use log4j.Clock for timestamps
// LOG4J2-744 unless TimestampMessage already has one
message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() :
- clock.currentTimeMillis());
+ clock.currentTimeMillis(),
+ nanoClock.nanoTime());
}
/**
@@ -266,7 +299,7 @@ public class Log4jLogEvent implements LogEvent {
final ThreadContext.ContextStack ndc, final String threadName,
final StackTraceElement location, final long timestampMillis) {
this(loggerName, marker, loggerFQCN, level, message, t, null, mdc, ndc, threadName,
- location, timestampMillis);
+ location, timestampMillis, nanoClock.nanoTime());
}
/**
@@ -292,7 +325,7 @@ public class Log4jLogEvent implements LogEvent {
final String threadName, final StackTraceElement location,
final long timestamp) {
final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown,
- thrownProxy, mdc, ndc, threadName, location, timestamp);
+ thrownProxy, mdc, ndc, threadName, location, timestamp, nanoClock.nanoTime());
return result;
}
@@ -310,11 +343,13 @@ public class Log4jLogEvent implements LogEvent {
* @param threadName The name of the thread.
* @param source The locations of the caller.
* @param timestamp The timestamp of the event.
+ * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event was
+ * created.
*/
private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
final Map<String, String> contextMap, final ThreadContext.ContextStack contextStack,
- final String threadName, final StackTraceElement source, final long timestamp) {
+ final String threadName, final StackTraceElement source, final long timestampMillis, final long nanoTime) {
this.loggerName = loggerName;
this.marker = marker;
this.loggerFqcn = loggerFQCN;
@@ -324,12 +359,15 @@ public class Log4jLogEvent implements LogEvent {
this.thrownProxy = thrownProxy;
this.contextMap = contextMap == null ? ThreadContext.EMPTY_MAP : contextMap;
this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack;
- this.timeMillis = message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : timestamp;
+ this.timeMillis = message instanceof TimestampMessage
+ ? ((TimestampMessage) message).getTimestamp()
+ : timestampMillis;
this.threadName = threadName;
this.source = source;
if (message != null && message instanceof LoggerNameAwareMessage) {
((LoggerNameAwareMessage) message).setLoggerName(loggerName);
}
+ this.nanoTime = nanoTime;
}
private static Map<String, String> createMap(final List<Property> properties) {
@@ -349,6 +387,26 @@ public class Log4jLogEvent implements LogEvent {
}
return Collections.unmodifiableMap(map);
}
+
+ /**
+ * Returns the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
+ * @return the {@code NanoClock} to use for creating the nanoTime timestamp of log events
+ */
+ public static NanoClock getNanoClock() {
+ return nanoClock;
+ }
+
+ /**
+ * Sets the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
+ * <p>
+ * FOR INTERNAL USE. This method may be called with a different {@code NanoClock} implementation when the
+ * configuration changes.
+ *
+ * @param nanoClock the {@code NanoClock} to use for creating the nanoTime timestamp of log events
+ */
+ public static void setNanoClock(NanoClock nanoClock) {
+ Log4jLogEvent.nanoClock = Objects.requireNonNull(nanoClock, "NanoClock must be non-null");
+ }
/**
* Returns the logging Level.
@@ -509,6 +567,11 @@ public class Log4jLogEvent implements LogEvent {
this.endOfBatch = endOfBatch;
}
+ @Override
+ public long getNanoTime() {
+ return nanoTime;
+ }
+
/**
* Creates a LogEventProxy that can be serialized.
* @return a LogEventProxy.
@@ -535,7 +598,7 @@ public class Log4jLogEvent implements LogEvent {
final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker,
proxy.loggerFQCN, proxy.level, proxy.message,
proxy.thrown, proxy.thrownProxy, proxy.contextMap, proxy.contextStack, proxy.threadName,
- proxy.source, proxy.timeMillis);
+ proxy.source, proxy.timeMillis, proxy.nanoTime);
result.setEndOfBatch(proxy.isEndOfBatch);
result.setIncludeLocation(proxy.isLocationRequired);
return result;
@@ -577,6 +640,9 @@ public class Log4jLogEvent implements LogEvent {
if (timeMillis != that.timeMillis) {
return false;
}
+ if (nanoTime != that.nanoTime) {
+ return false;
+ }
if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) {
return false;
}
@@ -622,6 +688,7 @@ public class Log4jLogEvent implements LogEvent {
result = 31 * result + loggerName.hashCode();
result = 31 * result + message.hashCode();
result = 31 * result + (int) (timeMillis ^ (timeMillis >>> 32));
+ result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32));
result = 31 * result + (thrown != null ? thrown.hashCode() : 0);
result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0);
result = 31 * result + (contextMap != null ? contextMap.hashCode() : 0);
@@ -653,6 +720,8 @@ public class Log4jLogEvent implements LogEvent {
private final StackTraceElement source;
private final boolean isLocationRequired;
private final boolean isEndOfBatch;
+ /** @since Log4J 2.4 */
+ private final transient long nanoTime;
public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) {
this.loggerFQCN = event.loggerFqcn;
@@ -669,6 +738,7 @@ public class Log4jLogEvent implements LogEvent {
this.threadName = event.getThreadName();
this.isLocationRequired = includeLocation;
this.isEndOfBatch = event.endOfBatch;
+ this.nanoTime = event.nanoTime;
}
/**
@@ -677,7 +747,7 @@ public class Log4jLogEvent implements LogEvent {
*/
protected Object readResolve() {
final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown,
- thrownProxy, contextMap, contextStack, threadName, source, timeMillis);
+ thrownProxy, contextMap, contextStack, threadName, source, timeMillis, nanoTime);
result.setEndOfBatch(isEndOfBatch);
result.setIncludeLocation(isLocationRequired);
return result;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/JsonConstants.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/JsonConstants.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/JsonConstants.java
index d307f7f..8e9ed2c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/JsonConstants.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/JsonConstants.java
@@ -30,4 +30,5 @@ public final class JsonConstants {
public static final String ELT_THROWN = "thrown";
public static final String ELT_MESSAGE = "message";
public static final String ELT_EXTENDED_STACK_TRACE = "extendedStackTrace";
+ public static final String ELT_NANO_TIME = "nanoTime";
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/JacksonFactory.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/JacksonFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/JacksonFactory.java
index 8ee4aae..537b634 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/JacksonFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/JacksonFactory.java
@@ -49,6 +49,11 @@ abstract class JacksonFactory {
}
@Override
+ protected String getPropertNameForNanoTime() {
+ return JsonConstants.ELT_NANO_TIME;
+ }
+
+ @Override
protected PrettyPrinter newCompactPrinter() {
return new MinimalPrettyPrinter();
}
@@ -77,6 +82,11 @@ abstract class JacksonFactory {
}
@Override
+ protected String getPropertNameForNanoTime() {
+ return JsonConstants.ELT_NANO_TIME;
+ }
+
+ @Override
protected PrettyPrinter newCompactPrinter() {
// Yes, null is the proper answer.
return null;
@@ -96,6 +106,8 @@ abstract class JacksonFactory {
abstract protected String getPropertNameForContextMap();
abstract protected String getPropertNameForSource();
+
+ abstract protected String getPropertNameForNanoTime();
abstract protected PrettyPrinter newCompactPrinter();
@@ -112,6 +124,7 @@ abstract class JacksonFactory {
if (!properties) {
except.add(this.getPropertNameForContextMap());
}
+ except.add(this.getPropertNameForNanoTime());
filters.addFilter(Log4jLogEvent.class.getName(), SimpleBeanPropertyFilter.serializeAllExcept(except));
final ObjectWriter writer = this.newObjectMapper().writer(compact ? this.newCompactPrinter() : this.newPrettyPrinter());
return writer.with(filters);
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NanoTimePatternConverter.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NanoTimePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NanoTimePatternConverter.java
new file mode 100644
index 0000000..2bee187
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NanoTimePatternConverter.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.pattern;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+
+/**
+ * Converts and formats the event's nanoTime in a StringBuilder.
+ */
+@Plugin(name = "NanoTimePatternConverter", category = PatternConverter.CATEGORY)
+@ConverterKeys({ "N", "nano" })
+public final class NanoTimePatternConverter extends LogEventPatternConverter {
+
+ /**
+ * Obtains an instance of pattern converter.
+ *
+ * @param options
+ * options, may be null.
+ * @return instance of pattern converter.
+ */
+ public static NanoTimePatternConverter newInstance(final String[] options) {
+ return new NanoTimePatternConverter(options);
+ }
+
+ /**
+ * Private constructor.
+ *
+ * @param options
+ * options, may be null.
+ */
+ private NanoTimePatternConverter(final String[] options) {
+ super("Nanotime", "nanotime");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void format(final LogEvent event, final StringBuilder output) {
+ output.append(event.getNanoTime());
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java
index 28d4180..08b66db 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java
@@ -16,13 +16,6 @@
*/
package org.apache.logging.log4j.core.pattern;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
-import org.apache.logging.log4j.core.config.plugins.util.PluginType;
-import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.Strings;
-
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
@@ -32,6 +25,14 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
+import org.apache.logging.log4j.core.config.plugins.util.PluginType;
+import org.apache.logging.log4j.core.util.NanoClockFactory;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
+
/**
* Most of the work of the {@link org.apache.logging.log4j.core.layout.PatternLayout} class is delegated to the
* PatternParser class.
@@ -172,7 +173,13 @@ public final class PatternParser {
final Iterator<FormattingInfo> fieldIter = fields.iterator();
boolean handlesThrowable = false;
+ NanoClockFactory.setMode(NanoClockFactory.Mode.Dummy); // LOG4J2-1074 use dummy clock by default
for (final PatternConverter converter : converters) {
+ if (converter instanceof NanoTimePatternConverter) {
+ // LOG4J2-1074 Switch to actual clock if nanosecond timestamps are required in config.
+ // LoggerContext will notify known NanoClockFactory users that the configuration has changed.
+ NanoClockFactory.setMode(NanoClockFactory.Mode.System);
+ }
LogEventPatternConverter pc;
if (converter instanceof LogEventPatternConverter) {
pc = (LogEventPatternConverter) converter;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DummyNanoClock.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DummyNanoClock.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DummyNanoClock.java
new file mode 100644
index 0000000..8200c38
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DummyNanoClock.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.util;
+
+/**
+ * Implementation of the {@code NanoClock} interface that always returns a fixed value.
+ */
+public final class DummyNanoClock implements NanoClock {
+
+ private final long fixedNanoTime;
+
+ public DummyNanoClock() {
+ this(0L);
+ }
+
+ /**
+ * Constructs a new DummyNanoClock with the specified value to return.
+ * @param fixedNanoTime the value to return from {@link #nanoTime()}.
+ */
+ public DummyNanoClock(long fixedNanoTime) {
+ this.fixedNanoTime = fixedNanoTime;
+ }
+
+ /**
+ * Returns the constructor value.
+ *
+ * @return the constructor value
+ */
+ @Override
+ public long nanoTime() {
+ return fixedNanoTime;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NanoClock.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NanoClock.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NanoClock.java
new file mode 100644
index 0000000..8fe8bf8
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NanoClock.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.util;
+
+/**
+ * Provides the high-resolution time stamp used in log events.
+ */
+public interface NanoClock {
+ /**
+ * Returns the current value of the running Java Virtual Machine's high-resolution time source, in nanoseconds.
+ *
+ * @return the current value of the running Java Virtual Machine's high-resolution time source, in nanoseconds
+ */
+ long nanoTime();
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NanoClockFactory.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NanoClockFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NanoClockFactory.java
new file mode 100644
index 0000000..73e3be8
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NanoClockFactory.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.util;
+
+import java.util.Objects;
+
+/**
+ * Creates the appropriate {@link NanoClock} instance for the current configuration.
+ */
+public class NanoClockFactory {
+
+ /**
+ * Enum over the different kinds of nano clocks this factory can create.
+ */
+ public static enum Mode {
+ /**
+ * Creates dummy nano clocks that always return a fixed value.
+ */
+ Dummy {
+ public NanoClock createNanoClock() {
+ return new DummyNanoClock();
+ }
+ },
+ /**
+ * Creates real nano clocks which call {{System.nanoTime()}}.
+ */
+ System {
+ public NanoClock createNanoClock() {
+ return new SystemNanoClock();
+ }
+ },
+ ;
+
+ public abstract NanoClock createNanoClock();
+ }
+
+ private static volatile Mode mode = Mode.Dummy;
+
+ /**
+ * Returns a new {@code NanoClock} determined by the mode of this factory.
+ *
+ * @return the appropriate {@code NanoClock} for the factory mode
+ */
+ public static NanoClock createNanoClock() {
+ return mode.createNanoClock();
+ }
+
+ /**
+ * Returns the factory mode.
+ *
+ * @return the factory mode that determines which kind of nano clocks this factory creates
+ */
+ public static Mode getMode() {
+ return mode;
+ }
+
+ /**
+ * Sets the factory mode.
+ *
+ * @param mode the factory mode that determines which kind of nano clocks this factory creates
+ */
+ public static void setMode(Mode mode) {
+ NanoClockFactory.mode = Objects.requireNonNull(mode, "mode must be non-null");
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/java/org/apache/logging/log4j/core/util/SystemNanoClock.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/SystemNanoClock.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/SystemNanoClock.java
new file mode 100644
index 0000000..9310e6c
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/SystemNanoClock.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.util;
+
+/**
+ * Implementation of the {@code NanoClock} interface that returns the system nano time.
+ */
+public final class SystemNanoClock implements NanoClock {
+
+ /**
+ * Returns the system high-resolution time.
+ * @return the result of calling {@code System.nanoTime()}
+ */
+ @Override
+ public long nanoTime() {
+ return System.nanoTime();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/main/resources/Log4j-events.xsd
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/resources/Log4j-events.xsd b/log4j-core/src/main/resources/Log4j-events.xsd
index 6e18154..153302a 100644
--- a/log4j-core/src/main/resources/Log4j-events.xsd
+++ b/log4j-core/src/main/resources/Log4j-events.xsd
@@ -56,6 +56,7 @@
</xs:sequence>
<xs:attribute name="logger" type="xs:string" use="required"/>
<xs:attribute name="timestamp" type="xs:long" use="required"/>
+ <xs:attribute name="nanoTime" type="xs:long" use="optional"/>
<xs:attribute name="level" type="log4j:LevelEnum" use="required"/>
<xs:attribute name="thread" type="xs:string" use="required"/>
</xs:complexType>
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/test/java/org/apache/logging/log4j/core/LogEventTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/LogEventTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/LogEventTest.java
index 8ba225b..4c0b85b 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/LogEventTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/LogEventTest.java
@@ -72,6 +72,54 @@ public class LogEventTest {
}
}
+ @Test
+ public void testNanoTimeIsNotSerialized1() throws Exception {
+ final LogEvent event1 = Log4jLogEvent.newBuilder() //
+ .setLoggerName(this.getClass().getName()) //
+ .setLoggerFqcn("org.apache.logging.log4j.core.Logger") //
+ .setLevel(Level.INFO) //
+ .setMessage(new SimpleMessage("Hello, world!")) //
+ .setThreadName("this must be initialized or the test fails") //
+ .setNanoTime(12345678L) //
+ .build();
+ final LogEvent copy = new Log4jLogEvent.Builder(event1).build();
+
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(event1);
+
+ final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ final ObjectInputStream ois = new ObjectInputStream(bais);
+
+ LogEvent actual = (LogEvent) ois.readObject();
+ assertNotEquals("Different event: nanoTime", copy, actual);
+ assertNotEquals("Different nanoTime", copy.getNanoTime(), actual.getNanoTime());
+ assertEquals("deserialized nanoTime is zero", 0, actual.getNanoTime());
+ }
+
+ @Test
+ public void testNanoTimeIsNotSerialized2() throws Exception {
+ final LogEvent event1 = Log4jLogEvent.newBuilder() //
+ .setLoggerName(this.getClass().getName()) //
+ .setLoggerFqcn("org.apache.logging.log4j.core.Logger") //
+ .setLevel(Level.INFO) //
+ .setMessage(new SimpleMessage("Hello, world!")) //
+ .setThreadName("this must be initialized or the test fails") //
+ .setNanoTime(0) //
+ .build();
+ final LogEvent event2 = new Log4jLogEvent.Builder(event1).build();
+
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(event1);
+
+ final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ final ObjectInputStream ois = new ObjectInputStream(bais);
+
+ LogEvent actual = (LogEvent) ois.readObject();
+ assertEquals("both zero nanoTime", event2, actual);
+ }
+
public void testEquals() {
final LogEvent event1 = Log4jLogEvent.newBuilder() //
.setLoggerName(this.getClass().getName()) //
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JpaH2AppenderTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JpaH2AppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JpaH2AppenderTest.java
index f71fdc6..c4b9ae7 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JpaH2AppenderTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JpaH2AppenderTest.java
@@ -44,7 +44,7 @@ public class JpaH2AppenderTest extends AbstractJpaAppenderTest {
statement = connection.createStatement();
statement.executeUpdate("CREATE TABLE jpaBasicLogEntry ( " +
- "id INTEGER IDENTITY, timemillis BIGINT, level NVARCHAR(10), loggerName NVARCHAR(255), " +
+ "id INTEGER IDENTITY, timemillis BIGINT, nanoTime BIGINT, level NVARCHAR(10), loggerName NVARCHAR(255), " +
"message NVARCHAR(1024), thrown NVARCHAR(1048576), contextMapJson NVARCHAR(1048576)," +
"loggerFQCN NVARCHAR(1024), contextStack NVARCHAR(1048576), marker NVARCHAR(255), source NVARCHAR(2048)," +
"threadName NVARCHAR(255)" +
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JpaHyperSqlAppenderTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JpaHyperSqlAppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JpaHyperSqlAppenderTest.java
index 39c407c..7ef8a24 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JpaHyperSqlAppenderTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JpaHyperSqlAppenderTest.java
@@ -48,7 +48,7 @@ public class JpaHyperSqlAppenderTest extends AbstractJpaAppenderTest {
statement = connection.createStatement();
statement.executeUpdate("CREATE TABLE jpaBasicLogEntry ( " +
- "id INTEGER IDENTITY, timemillis BIGINT, level VARCHAR(10), loggerName VARCHAR(255), " +
+ "id INTEGER IDENTITY, timemillis BIGINT, nanoTime BIGINT, level VARCHAR(10), loggerName VARCHAR(255), " +
"message VARCHAR(1024), thrown VARCHAR(1048576), contextMapJson VARCHAR(1048576)," +
"loggerFQCN VARCHAR(1024), contextStack VARCHAR(1048576), marker VARCHAR(255), source VARCHAR(2048)," +
"threadName VARCHAR(255)" +
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
index 51ee918..e70cdb9 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
@@ -43,7 +43,6 @@ import org.apache.logging.log4j.message.Message;
@Entity
@Table(name = "jpaBaseLogEntry")
-@SuppressWarnings("unused")
public class TestBaseEntity extends AbstractLogEventWrapperEntity {
private static final long serialVersionUID = 1L;
@@ -124,6 +123,12 @@ public class TestBaseEntity extends AbstractLogEventWrapperEntity {
}
@Override
+ @Transient
+ public long getNanoTime() {
+ return this.getWrappedEvent().getNanoTime();
+ }
+
+ @Override
@Convert(converter = ThrowableAttributeConverter.class)
@Column(name = "exception")
public Throwable getThrown() {
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTest.java
index b7f1842..ad60edb 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTest.java
@@ -25,6 +25,7 @@ import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.CoreLoggerContexts;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.util.Constants;
+import org.apache.logging.log4j.core.util.DummyNanoClock;
import org.apache.logging.log4j.util.Strings;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@@ -67,5 +68,10 @@ public class AsyncLoggerTest {
final String location = "testAsyncLogWritesToLog";
assertTrue("no location", !line1.contains(location));
}
+
+ @Test
+ public void testNanoClockInitiallyDummy() {
+ assertTrue(AsyncLogger.getNanoClock() instanceof DummyNanoClock);
+ }
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTestNanoTime.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTestNanoTime.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTestNanoTime.java
new file mode 100644
index 0000000..0288f85
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTestNanoTime.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.async;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.CoreLoggerContexts;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.util.Constants;
+import org.apache.logging.log4j.core.util.DummyNanoClock;
+import org.apache.logging.log4j.core.util.SystemNanoClock;
+import org.apache.logging.log4j.util.Strings;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class AsyncLoggerTestNanoTime {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR,
+ AsyncLoggerContextSelector.class.getName());
+ System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "NanoTimeToFileTest.xml");
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR, Strings.EMPTY);
+ }
+
+ @Test
+ public void testAsyncLogUsesNanoTimeClock() throws Exception {
+ final File file = new File("target", "NanoTimeToFileTest.log");
+ // System.out.println(f.getAbsolutePath());
+ file.delete();
+ final Logger log = LogManager.getLogger("com.foo.Bar");
+ final long before = System.nanoTime();
+ log.info("Use actual System.nanoTime()");
+ assertTrue("using SystemNanoClock", AsyncLogger.getNanoClock() instanceof SystemNanoClock);
+
+ final long DUMMYNANOTIME = 123;
+ AsyncLogger.setNanoClock(new DummyNanoClock(DUMMYNANOTIME));
+ log.info("Use dummy nano clock");
+ assertTrue("using SystemNanoClock", AsyncLogger.getNanoClock() instanceof DummyNanoClock);
+
+ CoreLoggerContexts.stopLoggerContext(file); // stop async thread
+
+ final BufferedReader reader = new BufferedReader(new FileReader(file));
+ final String line1 = reader.readLine();
+ final String line2 = reader.readLine();
+ // System.out.println(line1);
+ // System.out.println(line2);
+ reader.close();
+ file.delete();
+
+ assertNotNull("line1", line1);
+ assertNotNull("line2", line2);
+ final String[] line1Parts = line1.split(" AND ");
+ assertEquals("Use actual System.nanoTime()", line1Parts[2]);
+ assertEquals(line1Parts[0], line1Parts[1]);
+ long loggedNanoTime = Long.parseLong(line1Parts[0]);
+ assertTrue("used system nano time", loggedNanoTime - before < TimeUnit.SECONDS.toNanos(1));
+
+ final String[] line2Parts = line2.split(" AND ");
+ assertEquals("Use dummy nano clock", line2Parts[2]);
+ assertEquals(String.valueOf(DUMMYNANOTIME), line2Parts[0]);
+ assertEquals(String.valueOf(DUMMYNANOTIME), line2Parts[1]);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
index 64eb873..2da7b11 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
@@ -58,8 +58,9 @@ public class RingBufferLogEventTest {
final String threadName = null;
final StackTraceElement location = null;
final long currentTimeMillis = 0;
+ final long nanoTime = 1;
evt.setValues(null, loggerName, marker, fqcn, level, data, t, map,
- contextStack, threadName, location, currentTimeMillis);
+ contextStack, threadName, location, currentTimeMillis, nanoTime);
assertEquals(Level.OFF, evt.getLevel());
}
@@ -77,8 +78,9 @@ public class RingBufferLogEventTest {
final String threadName = null;
final StackTraceElement location = null;
final long currentTimeMillis = 0;
+ final long nanoTime = 1;
evt.setValues(null, loggerName, marker, fqcn, level, data, t, map,
- contextStack, threadName, location, currentTimeMillis);
+ contextStack, threadName, location, currentTimeMillis, nanoTime);
assertNotNull(evt.getMessage());
}
@@ -96,8 +98,9 @@ public class RingBufferLogEventTest {
final String threadName = null;
final StackTraceElement location = null;
final long currentTimeMillis = 123;
+ final long nanoTime = 1;
evt.setValues(null, loggerName, marker, fqcn, level, data, t, map,
- contextStack, threadName, location, currentTimeMillis);
+ contextStack, threadName, location, currentTimeMillis, nanoTime);
assertEquals(123, evt.getTimeMillis());
}
@@ -115,8 +118,9 @@ public class RingBufferLogEventTest {
final String threadName = "main";
final StackTraceElement location = null;
final long currentTimeMillis = 12345;
+ final long nanoTime = 1;
evt.setValues(null, loggerName, marker, fqcn, level, data, t, map,
- contextStack, threadName, location, currentTimeMillis);
+ contextStack, threadName, location, currentTimeMillis, nanoTime);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ObjectOutputStream out = new ObjectOutputStream(baos);
@@ -153,8 +157,9 @@ public class RingBufferLogEventTest {
final String threadName = "main";
final StackTraceElement location = null;
final long currentTimeMillis = 12345;
+ final long nanoTime = 1;
evt.setValues(null, loggerName, marker, fqcn, level, data, t, map,
- contextStack, threadName, location, currentTimeMillis);
+ contextStack, threadName, location, currentTimeMillis, nanoTime);
final LogEvent actual = evt.createMemento();
assertEquals(evt.getLoggerName(), actual.getLoggerName());
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventNanoTimeTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventNanoTimeTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventNanoTimeTest.java
new file mode 100644
index 0000000..b159feb
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventNanoTimeTest.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.impl;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.CoreLoggerContexts;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.util.Constants;
+import org.apache.logging.log4j.core.util.DummyNanoClock;
+import org.apache.logging.log4j.core.util.SystemNanoClock;
+import org.apache.logging.log4j.util.Strings;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class Log4jLogEventNanoTimeTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "NanoTimeToFileTest.xml");
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR, Strings.EMPTY);
+ }
+
+ @Test
+ public void testLog4jLogEventUsesNanoTimeClock() throws Exception {
+ final File file = new File("target", "NanoTimeToFileTest.log");
+ // System.out.println(f.getAbsolutePath());
+ file.delete();
+ final Logger log = LogManager.getLogger("com.foo.Bar");
+ final long before = System.nanoTime();
+ log.info("Use actual System.nanoTime()");
+ assertTrue("using SystemNanoClock", Log4jLogEvent.getNanoClock() instanceof SystemNanoClock);
+
+ final long DUMMYNANOTIME = 123;
+ Log4jLogEvent.setNanoClock(new DummyNanoClock(DUMMYNANOTIME));
+ log.info("Use dummy nano clock");
+ assertTrue("using SystemNanoClock", Log4jLogEvent.getNanoClock() instanceof DummyNanoClock);
+
+ CoreLoggerContexts.stopLoggerContext(file); // stop async thread
+
+ final BufferedReader reader = new BufferedReader(new FileReader(file));
+ final String line1 = reader.readLine();
+ final String line2 = reader.readLine();
+ // System.out.println(line1);
+ // System.out.println(line2);
+ reader.close();
+ file.delete();
+
+ assertNotNull("line1", line1);
+ assertNotNull("line2", line2);
+ final String[] line1Parts = line1.split(" AND ");
+ assertEquals("Use actual System.nanoTime()", line1Parts[2]);
+ assertEquals(line1Parts[0], line1Parts[1]);
+ long loggedNanoTime = Long.parseLong(line1Parts[0]);
+ assertTrue("used system nano time", loggedNanoTime - before < TimeUnit.SECONDS.toNanos(1));
+
+ final String[] line2Parts = line2.split(" AND ");
+ assertEquals("Use dummy nano clock", line2Parts[2]);
+ assertEquals(String.valueOf(DUMMYNANOTIME), line2Parts[0]);
+ assertEquals(String.valueOf(DUMMYNANOTIME), line2Parts[1]);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
index 540bfeb..516c9ec 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
@@ -21,16 +21,24 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import javax.xml.bind.DatatypeConverter;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.MarkerManager;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.ThreadContext.ContextStack;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.util.Clock;
import org.apache.logging.log4j.core.util.ClockFactory;
import org.apache.logging.log4j.core.util.ClockFactoryTest;
+import org.apache.logging.log4j.core.util.DummyNanoClock;
import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.ObjectMessage;
import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.util.Strings;
import org.junit.AfterClass;
@@ -193,6 +201,206 @@ public class Log4jLogEventTest {
public void testTimestampGeneratedByClock() {
final LogEvent evt = Log4jLogEvent.newBuilder().build();
assertEquals(FixedTimeClock.FIXED_TIME, evt.getTimeMillis());
+ }
+
+ @Test
+ public void testInitiallyDummyNanoClock() {
+ assertTrue(Log4jLogEvent.getNanoClock() instanceof DummyNanoClock);
+ assertEquals("initial dummy nanotime", 0, Log4jLogEvent.getNanoClock().nanoTime());
+ }
+
+ @Test
+ public void testNanoTimeGeneratedByNanoClock() {
+ Log4jLogEvent.setNanoClock(new DummyNanoClock(123));
+ verifyNanoTimeWithAllConstructors(123);
+ Log4jLogEvent.setNanoClock(new DummyNanoClock(87654));
+ verifyNanoTimeWithAllConstructors(87654);
+ }
+ @SuppressWarnings("deprecation")
+ private void verifyNanoTimeWithAllConstructors(long expected) {
+ assertEquals(expected, Log4jLogEvent.getNanoClock().nanoTime());
+
+ assertEquals("No-arg constructor", expected, new Log4jLogEvent().getNanoTime());
+ assertEquals("1-arg constructor", expected, new Log4jLogEvent(98).getNanoTime());
+ assertEquals("6-arg constructor", expected, new Log4jLogEvent("l", null, "a", null, null, null).getNanoTime());
+ assertEquals("7-arg constructor", expected, new Log4jLogEvent("l", null, "a", null, null, null, null)
+ .getNanoTime());
+ assertEquals("11-arg constructor", expected, new Log4jLogEvent("l", null, "a", null, null, null, null, null,
+ null, null, 0).getNanoTime());
+ assertEquals("12-arg factory method", expected, Log4jLogEvent.createEvent("l", null, "a", null, null, null,
+ null, null, null, null, null, 0).getNanoTime());
+ }
+
+ @Test
+ public void testBuilderCorrectlyCopiesAllEventAttributes() {
+ final Map<String, String> contextMap = new HashMap<String, String>();
+ contextMap.put("A", "B");
+ final ContextStack contextStack = ThreadContext.getImmutableStack();
+ final Exception exception = new Exception("test");
+ final Marker marker = MarkerManager.getMarker("EVENTTEST");
+ final Message message = new SimpleMessage("foo");
+ final StackTraceElement stackTraceElement = new StackTraceElement("A", "B", "file", 123);
+ final String fqcn = "qualified";
+ final String name = "Ceci n'est pas une pipe";
+ final String threadName = "threadName";
+ final Log4jLogEvent event = Log4jLogEvent.newBuilder() //
+ .setContextMap(contextMap) //
+ .setContextStack(contextStack) //
+ .setEndOfBatch(true) //
+ .setIncludeLocation(true) //
+ .setLevel(Level.FATAL) //
+ .setLoggerFqcn(fqcn) //
+ .setLoggerName(name) //
+ .setMarker(marker) //
+ .setMessage(message) //
+ .setNanoTime(1234567890L) //
+ .setSource(stackTraceElement) //
+ .setThreadName(threadName) //
+ .setThrown(exception) //
+ .setTimeMillis(987654321L)
+ .build();
+
+ assertSame(contextMap, event.getContextMap());
+ assertSame(contextStack, event.getContextStack());
+ assertEquals(true, event.isEndOfBatch());
+ assertEquals(true, event.isIncludeLocation());
+ assertSame(Level.FATAL, event.getLevel());
+ assertSame(fqcn, event.getLoggerFqcn());
+ assertSame(name, event.getLoggerName());
+ assertSame(marker, event.getMarker());
+ assertSame(message, event.getMessage());
+ assertEquals(1234567890L, event.getNanoTime());
+ assertSame(stackTraceElement, event.getSource());
+ assertSame(threadName, event.getThreadName());
+ assertSame(exception, event.getThrown());
+ assertEquals(987654321L, event.getTimeMillis());
+
+ LogEvent event2 = new Log4jLogEvent.Builder(event).build();
+ assertEquals("copy constructor builder", event2, event);
+ assertEquals("same hashCode", event2.hashCode(), event.hashCode());
+ }
+
+ @Test
+ public void testEquals() {
+ final Map<String, String> contextMap = new HashMap<String, String>();
+ contextMap.put("A", "B");
+ ThreadContext.push("first");
+ final ContextStack contextStack = ThreadContext.getImmutableStack();
+ final Exception exception = new Exception("test");
+ final Marker marker = MarkerManager.getMarker("EVENTTEST");
+ final Message message = new SimpleMessage("foo");
+ final StackTraceElement stackTraceElement = new StackTraceElement("A", "B", "file", 123);
+ final String fqcn = "qualified";
+ final String name = "Ceci n'est pas une pipe";
+ final String threadName = "threadName";
+ final Log4jLogEvent event = Log4jLogEvent.newBuilder() //
+ .setContextMap(contextMap) //
+ .setContextStack(contextStack) //
+ .setEndOfBatch(true) //
+ .setIncludeLocation(true) //
+ .setLevel(Level.FATAL) //
+ .setLoggerFqcn(fqcn) //
+ .setLoggerName(name) //
+ .setMarker(marker) //
+ .setMessage(message) //
+ .setNanoTime(1234567890L) //
+ .setSource(stackTraceElement) //
+ .setThreadName(threadName) //
+ .setThrown(exception) //
+ .setTimeMillis(987654321L)
+ .build();
+
+ assertSame(contextMap, event.getContextMap());
+ assertSame(contextStack, event.getContextStack());
+ assertEquals(true, event.isEndOfBatch());
+ assertEquals(true, event.isIncludeLocation());
+ assertSame(Level.FATAL, event.getLevel());
+ assertSame(fqcn, event.getLoggerFqcn());
+ assertSame(name, event.getLoggerName());
+ assertSame(marker, event.getMarker());
+ assertSame(message, event.getMessage());
+ assertEquals(1234567890L, event.getNanoTime());
+ assertSame(stackTraceElement, event.getSource());
+ assertSame(threadName, event.getThreadName());
+ assertSame(exception, event.getThrown());
+ assertEquals(987654321L, event.getTimeMillis());
+
+ final LogEvent event2 = builder(event).build();
+ assertEquals("copy constructor builder", event2, event);
+ assertEquals("same hashCode", event2.hashCode(), event.hashCode());
+
+ assertSame(contextMap, event2.getContextMap());
+ assertSame(contextStack, event2.getContextStack());
+ assertEquals(true, event2.isEndOfBatch());
+ assertEquals(true, event2.isIncludeLocation());
+ assertSame(Level.FATAL, event2.getLevel());
+ assertSame(fqcn, event2.getLoggerFqcn());
+ assertSame(name, event2.getLoggerName());
+ assertSame(marker, event2.getMarker());
+ assertSame(message, event2.getMessage());
+ assertEquals(1234567890L, event2.getNanoTime());
+ assertSame(stackTraceElement, event2.getSource());
+ assertSame(threadName, event2.getThreadName());
+ assertSame(exception, event2.getThrown());
+ assertEquals(987654321L, event2.getTimeMillis());
+
+ final Map<String, String> differentMap = Collections.emptyMap();
+ different("different contextMap", builder(event).setContextMap(differentMap), event);
+ different("null contextMap", builder(event).setContextMap(null), event);
+
+ ThreadContext.push("abc");
+ final ContextStack contextStack2 = ThreadContext.getImmutableStack();
+ different("different contextStack", builder(event).setContextStack(contextStack2), event);
+ different("null contextStack", builder(event).setContextStack(null), event);
+
+ different("different EndOfBatch", builder(event).setEndOfBatch(false), event);
+ different("different IncludeLocation", builder(event).setIncludeLocation(false), event);
+
+ different("different level", builder(event).setLevel(Level.INFO), event);
+ different("null level", builder(event).setLevel(null), event);
+
+ different("different fqcn", builder(event).setLoggerFqcn("different"), event);
+ different("null fqcn", builder(event).setLoggerFqcn(null), event);
+
+ different("different name", builder(event).setLoggerName("different"), event);
+ try { // TODO null logger name throws NPE in equals. Use Objects.requireNonNull in constructor?
+ different("null name", builder(event).setLoggerName(null), event);
+ fail("Expected NullPointerException");
+ } catch (NullPointerException ok) {
+ }
+
+ different("different marker", builder(event).setMarker(MarkerManager.getMarker("different")), event);
+ different("null marker", builder(event).setMarker(null), event);
+
+ different("different message", builder(event).setMessage(new ObjectMessage("different")), event);
+ try { // TODO null message throws NPE in equals(). Use Objects.requireNonNull in constructor?
+ different("null message", builder(event).setMessage(null), event);
+ fail("Expected NullPointerException");
+ } catch (NullPointerException ok) {
+ }
+
+ different("different nanoTime", builder(event).setNanoTime(135), event);
+ different("different milliTime", builder(event).setTimeMillis(137), event);
+
+ final StackTraceElement stack2 = new StackTraceElement("XXX", "YYY", "file", 123);
+ different("different source", builder(event).setSource(stack2), event);
+ different("null source", builder(event).setSource(null), event);
+
+ different("different threadname", builder(event).setThreadName("different"), event);
+ different("null threadname", builder(event).setThreadName(null), event);
+
+ different("different exception", builder(event).setThrown(new Error("Boo!")), event);
+ different("null exception", builder(event).setThrown(null), event);
+ }
+
+ private static Log4jLogEvent.Builder builder(LogEvent event) {
+ return new Log4jLogEvent.Builder(event);
+ }
+
+ private void different(String reason, Log4jLogEvent.Builder builder, LogEvent event) {
+ final LogEvent other = builder.build();
+ assertNotEquals(reason, other, event);
+ assertNotEquals(reason + " hashCode", other.hashCode(), event.hashCode());
}
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7f075a71/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/NanoTimePatternConverterTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/NanoTimePatternConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/NanoTimePatternConverterTest.java
new file mode 100644
index 0000000..f9598d2
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/NanoTimePatternConverterTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.pattern;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class NanoTimePatternConverterTest {
+
+ @Test
+ public void testConverterAppendsLogEventNanoTimeToStringBuilder() {
+ final LogEvent event = Log4jLogEvent.newBuilder() //
+ .setNanoTime(1234567).build();
+ final StringBuilder sb = new StringBuilder();
+ final NanoTimePatternConverter converter = NanoTimePatternConverter.newInstance(null);
+ converter.format(event, sb);
+ assertEquals("1234567", sb.toString());
+ }
+}