blob: c28cd8c9dfca920a210cff923fe9d9e9f292b2f0 [file] [log] [blame]
/**
* 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.pulsar.structuredeventlog.slf4j;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.BufferedReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.WriterAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig ;
import org.apache.logging.log4j.core.layout.JsonLayout;
import org.apache.pulsar.structuredeventlog.Event;
import org.apache.pulsar.structuredeventlog.EventGroup;
import org.apache.pulsar.structuredeventlog.EventResources;
import org.apache.pulsar.structuredeventlog.StructuredEventLog;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class StructuredEventLogTest {
private final static String APPENDER_NAME = "stevlogtest";
StringWriter writer;
@BeforeMethod
public void setupLog4j() throws Exception {
LoggerContext context = LoggerContext.getContext(false);
Configuration config = context.getConfiguration();
writer = new StringWriter();
LoggerConfig logConfig = config.getLoggerConfig(APPENDER_NAME);
for (Map.Entry<String, Appender> a : logConfig.getAppenders().entrySet()) {
if (a.getKey().equals(APPENDER_NAME)) {
a.getValue().stop();
}
}
logConfig.removeAppender(APPENDER_NAME);
JsonLayout layout = JsonLayout.newBuilder().setEventEol(true).setCompact(true).setProperties(true).build();
Appender appender = WriterAppender.createAppender(layout, null, writer, "stevlogtest", false, true);
appender.start();
logConfig.addAppender(appender, null, null);
logConfig.setLevel(Level.DEBUG);
}
@Test
public void testTraceId() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
Event e = log.newRootEvent();
e.newChildEvent().log("child");
e.log("parent");
log.newRootEvent().log("second");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("child"));
assertThat(logged.get(1).get("message"), equalTo("parent"));
assertThat(logged.get(2).get("message"), equalTo("second"));
assertThat(contextMapField(logged.get(0), "traceId"),
equalTo(contextMapField(logged.get(1), "traceId")));
assertThat(contextMapField(logged.get(0), "traceId"),
not(equalTo(contextMapField(logged.get(2), "traceId"))));
}
@Test
public void testParentId() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
Event e1 = log.newRootEvent();
Event e2 = e1.newChildEvent();
e2.newChildEvent().log("child2");
e2.log("child1");
e1.log("parent");
log.newRootEvent().log("second");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("child2"));
assertThat(logged.get(1).get("message"), equalTo("child1"));
assertThat(logged.get(2).get("message"), equalTo("parent"));
assertThat(logged.get(3).get("message"), equalTo("second"));
assertThat(contextMapField(logged.get(0), "parentId"), not(nullValue()));
assertThat(contextMapField(logged.get(0), "parentId"),
equalTo(contextMapField(logged.get(1), "id")));
assertThat(contextMapField(logged.get(1), "parentId"),
equalTo(contextMapField(logged.get(2), "id")));
assertThat(contextMapField(logged.get(2), "parentId"), nullValue());
assertThat(contextMapField(logged.get(3), "parentId"), nullValue());
}
@Test
public void testResources() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
EventResources res = log.newEventResources()
.resource("r1", "v1")
.resource("r2", () -> "v2");
Event e1 = log.newRootEvent()
.resources(res)
.resource("r3", "v3")
.resource("r4", () -> "v4");
Event e2 = e1.newChildEvent().resource("r5", "v5");
e2.newChildEvent().resource("r6", "v6").log("child2");
e2.log("child1");
e1.log("parent");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("child2"));
assertThat(logged.get(1).get("message"), equalTo("child1"));
assertThat(logged.get(2).get("message"), equalTo("parent"));
assertThat(contextMapField(logged.get(0), "r6"), equalTo("v6"));
assertThat(contextMapField(logged.get(0), "r5"), equalTo("v5"));
assertThat(contextMapField(logged.get(0), "r4"), equalTo("v4"));
assertThat(contextMapField(logged.get(0), "r3"), equalTo("v3"));
assertThat(contextMapField(logged.get(0), "r2"), equalTo("v2"));
assertThat(contextMapField(logged.get(0), "r1"), equalTo("v1"));
assertThat(contextMapField(logged.get(1), "r6"), nullValue());
assertThat(contextMapField(logged.get(1), "r5"), equalTo("v5"));
assertThat(contextMapField(logged.get(1), "r4"), equalTo("v4"));
assertThat(contextMapField(logged.get(1), "r3"), equalTo("v3"));
assertThat(contextMapField(logged.get(1), "r2"), equalTo("v2"));
assertThat(contextMapField(logged.get(1), "r1"), equalTo("v1"));
assertThat(contextMapField(logged.get(2), "r6"), nullValue());
assertThat(contextMapField(logged.get(2), "r5"), nullValue());
assertThat(contextMapField(logged.get(2), "r4"), equalTo("v4"));
assertThat(contextMapField(logged.get(2), "r3"), equalTo("v3"));
assertThat(contextMapField(logged.get(2), "r2"), equalTo("v2"));
assertThat(contextMapField(logged.get(2), "r1"), equalTo("v1"));
}
@Test
public void testResourcesNullTest() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
EventResources res = log.newEventResources()
.resource(null, "v1")
.resource("r1", null)
.resource("r2", () -> null);
Event e1 = log.newRootEvent()
.resources(res)
.resource(null, "v2")
.resource("r3", null)
.resource("r4", () -> null);
e1.newChildEvent()
.resource(null, "v3")
.resource("r5", null)
.log("child1");
e1.log("parent");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("child1"));
assertThat(logged.get(1).get("message"), equalTo("parent"));
assertThat(contextMapField(logged.get(0), "r5"), equalTo("null"));
assertThat(contextMapField(logged.get(0), "r4"), equalTo("null"));
assertThat(contextMapField(logged.get(0), "r3"), equalTo("null"));
assertThat(contextMapField(logged.get(0), "r2"), equalTo("null"));
assertThat(contextMapField(logged.get(0), "r1"), equalTo("null"));
assertThat(contextMapField(logged.get(0), "null"), equalTo("v3"));
assertThat(contextMapField(logged.get(1), "r5"), nullValue());
assertThat(contextMapField(logged.get(1), "r4"), equalTo("null"));
assertThat(contextMapField(logged.get(1), "r3"), equalTo("null"));
assertThat(contextMapField(logged.get(1), "r2"), equalTo("null"));
assertThat(contextMapField(logged.get(1), "r1"), equalTo("null"));
assertThat(contextMapField(logged.get(1), "null"), equalTo("v2"));
}
@Test
public void testAttributes() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
Event e1 = log.newRootEvent()
.attr("a1", "v1")
.attr("a2", () -> "v2");
Event e2 = e1.newChildEvent().attr("a3", "v3");
e2.newChildEvent().resource("a4", "v4").log("child2");
e2.log("child1");
e1.log("parent");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("child2"));
assertThat(logged.get(1).get("message"), equalTo("child1"));
assertThat(logged.get(2).get("message"), equalTo("parent"));
assertThat(contextMapField(logged.get(0), "a4"), equalTo("v4"));
assertThat(contextMapField(logged.get(0), "a3"), nullValue());
assertThat(contextMapField(logged.get(0), "a2"), nullValue());
assertThat(contextMapField(logged.get(0), "a1"), nullValue());
assertThat(contextMapField(logged.get(1), "a4"), nullValue());
assertThat(contextMapField(logged.get(1), "a3"), equalTo("v3"));
assertThat(contextMapField(logged.get(1), "a2"), nullValue());
assertThat(contextMapField(logged.get(1), "a1"), nullValue());
assertThat(contextMapField(logged.get(2), "a4"), nullValue());
assertThat(contextMapField(logged.get(2), "a3"), nullValue());
assertThat(contextMapField(logged.get(2), "a2"), equalTo("v2"));
assertThat(contextMapField(logged.get(2), "a1"), equalTo("v1"));
}
@Test
public void testAttributedNullTest() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
log.newRootEvent()
.attr(null, "v1")
.attr("a1", null)
.attr("a2", () -> null)
.log("msg");
log.newRootEvent()
.attr(null, "v1")
.attr("a1", null)
.attr("a2", () -> null)
.log("msg");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("msg"));
assertThat(contextMapField(logged.get(0), "null"), equalTo("v1"));
assertThat(contextMapField(logged.get(0), "a1"), equalTo("null"));
assertThat(contextMapField(logged.get(0), "a2"), equalTo("null"));
}
@Test
public void testInfoLevel() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
log.newRootEvent().log("info1");
log.newRootEvent().atInfo().log("info2");
log.newRootEvent().atError().atInfo().log("info3");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("info1"));
assertThat(logged.get(1).get("message"), equalTo("info2"));
assertThat(logged.get(2).get("message"), equalTo("info3"));
assertThat(logged.get(0).get("level"), equalTo("INFO"));
assertThat(logged.get(1).get("level"), equalTo("INFO"));
assertThat(logged.get(2).get("level"), equalTo("INFO"));
}
@SuppressWarnings("unchecked")
@Test
public void testInfoLevelException() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
log.newRootEvent().exception(new Throwable("cause1")).log("info1");
log.newRootEvent().atInfo().exception(new Throwable("cause2")).log("info2");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("info1"));
assertThat(((Map<String, Object>)logged.get(0).get("thrown")).get("message"), equalTo("cause1"));
assertThat(logged.get(1).get("message"), equalTo("info2"));
assertThat(((Map<String, Object>)logged.get(1).get("thrown")).get("message"), equalTo("cause2"));
}
@Test
public void testWarnLevel() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
log.newRootEvent().atWarn().log("warn1");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("warn1"));
assertThat(logged.get(0).get("level"), equalTo("WARN"));
}
@SuppressWarnings("unchecked")
@Test
public void testWarnLevelException() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
log.newRootEvent().atWarn().exception(new Throwable("cause1")).log("warn1");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("warn1"));
assertThat(((Map<String, Object>)logged.get(0).get("thrown")).get("message"), equalTo("cause1"));
}
@Test
public void testErrorLevel() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
log.newRootEvent().atError().log("error1");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("error1"));
assertThat(logged.get(0).get("level"), equalTo("ERROR"));
}
@SuppressWarnings("unchecked")
@Test
public void testErrorLevelException() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
log.newRootEvent().atError().exception(new Throwable("cause1")).log("error1");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("error1"));
assertThat(((Map<String, Object>)logged.get(0).get("thrown")).get("message"), equalTo("cause1"));
}
@Test
public void testTimedEvent() throws Exception {
MockClock clock = new MockClock();
StructuredEventLog log = StructuredEventLog.newLogger();
((Slf4jStructuredEventLog)log).clock = clock;
Event e = log.newRootEvent().timed();
clock.advanceTime(1234, TimeUnit.MILLISECONDS);
e.log("timed");
List<Map<String, Object>> logged = getLogged();
assertThat(logged.get(0).get("message"), equalTo("timed"));
assertThat(contextMapField(logged.get(0), "startTimestamp"), equalTo("1970-01-02T03:46:40Z"));
assertThat(contextMapField(logged.get(0), "durationMs"), equalTo("1234"));
}
@EventGroup(component="foobar")
public enum Events {
TEST_EVENT
}
@Test
public void testEventGroups() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
log.newRootEvent().log(Events.TEST_EVENT);
List<Map<String, Object>> logged = getLogged();
System.out.println(logged);
assertThat(logged.get(0).get("message"), equalTo("TEST_EVENT"));
assertThat(logged.get(0).get("loggerName"), equalTo("stevlog.foobar.TEST_EVENT"));
assertThat(contextMapField(logged.get(0), "component"), equalTo("foobar"));
}
public enum BareEvents {
BARE_EVENT
}
@Test
public void testBareEnum() throws Exception {
StructuredEventLog log = StructuredEventLog.newLogger();
log.newRootEvent().log(BareEvents.BARE_EVENT);
List<Map<String, Object>> logged = getLogged();
System.out.println(logged);
assertThat(logged.get(0).get("message"), equalTo("BARE_EVENT"));
assertThat(logged.get(0).get("loggerName"), equalTo("stevlog.BARE_EVENT"));
assertThat(contextMapField(logged.get(0), "component"), nullValue());
}
@SuppressWarnings("unchecked")
private Object contextMapField(Map<String, Object> map, String field) {
return ((Map<String, Object>)map.get("contextMap")).get(field);
}
@SuppressWarnings("unchecked")
private List<Map<String, Object>> getLogged() throws Exception {
List<Map<String, Object>> logged = new ArrayList<>();
ObjectMapper o = new ObjectMapper();
try (BufferedReader r = new BufferedReader(new StringReader(writer.toString()))) {
String line = r.readLine();
while (line != null) {
Map<String, Object> log = o.readValue(line, Map.class);
if (log.get("loggerName").toString().startsWith("stevlog")) {
logged.add(log);
}
line = r.readLine();
}
}
return logged;
}
private static class MockClock extends Clock {
AtomicLong ticker = new AtomicLong(100000000);
void advanceTime(int duration, TimeUnit unit) {
ticker.addAndGet(unit.toMillis(duration));
}
@Override
public Instant instant() {
return Instant.ofEpochMilli(ticker.get());
}
@Override
public ZoneId getZone( ) {
return ZoneId.of("UTC");
}
@Override
public Clock withZone(ZoneId zoneId) {
return this;
}
}
}