Add a logback appender that can be added to a config to export context IDs (#1165)
* Add a logback appender that exports IDs. * Progress * Finish * NOTICE
This commit is contained in:
parent
5c86bcc20d
commit
d21db7b0a6
|
@ -0,0 +1,19 @@
|
||||||
|
This product contains a modified part of Armeria, distributed by LINE:
|
||||||
|
|
||||||
|
* License:
|
||||||
|
|
||||||
|
Copyright 2015 LINE Corporation
|
||||||
|
|
||||||
|
LINE Corporation 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:
|
||||||
|
|
||||||
|
https://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.
|
||||||
|
|
||||||
|
* Homepage: https://armeria.dev
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Logback Integration
|
||||||
|
|
||||||
|
This module integrates instrumentation with Logback by injecting the trace ID and span ID from a
|
||||||
|
mounted span using a custom Logback appender.
|
||||||
|
|
||||||
|
To use it, add the module to your application's runtime classpath and add the appender to your
|
||||||
|
`logback.xml`.
|
||||||
|
|
||||||
|
**Maven**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.opentelemetry.instrumentation</groupId>
|
||||||
|
<artifactId>opentelemetry-logback-1.0.0</artifactId>
|
||||||
|
<version>0.8.0-SNAPSHOT</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gradle**
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
dependencies {
|
||||||
|
runtimeOnly("io.opentelemetry.instrumentation:opentelemetry-logback-1.0.0:0.8.0-SNAPSHOT")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**logback.xml**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{HH:mm:ss.SSS} %X{traceId} %X{spanId} %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- Just wrap your logging appender, for example ConsoleAppender, with OpenTelemetryAppender -->
|
||||||
|
<appender name="OTEL" class="io.opentelemetry.instrumentation.logback.v1_0_0.OpenTelemetryAppender">
|
||||||
|
<appender-ref ref="CONSOLE" />
|
||||||
|
</appender>
|
||||||
|
...
|
||||||
|
</configuration>
|
||||||
|
```
|
||||||
|
|
||||||
|
Logging events will automatically have context information from the span context injected. The
|
||||||
|
following attributes are available for use:
|
||||||
|
|
||||||
|
- `traceId`
|
||||||
|
- `spanId`
|
||||||
|
- `traceFlags`
|
|
@ -0,0 +1,7 @@
|
||||||
|
apply from: "$rootDir/gradle/instrumentation-library.gradle"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
library group: 'ch.qos.logback', name: 'logback-classic', version: '1.0.0'
|
||||||
|
|
||||||
|
testImplementation project(':instrumentation:logback:logback-1.0.0:testing')
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed 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 io.opentelemetry.instrumentation.logback.v1_0_0;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.Level;
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
|
import ch.qos.logback.classic.spi.IThrowableProxy;
|
||||||
|
import ch.qos.logback.classic.spi.LoggerContextVO;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.slf4j.Marker;
|
||||||
|
|
||||||
|
final class LoggingEventWrapper implements ILoggingEvent {
|
||||||
|
private final ILoggingEvent event;
|
||||||
|
private final Map<String, String> mdcPropertyMap;
|
||||||
|
private final LoggerContextVO vo;
|
||||||
|
|
||||||
|
LoggingEventWrapper(ILoggingEvent event, Map<String, String> mdcPropertyMap) {
|
||||||
|
this.event = event;
|
||||||
|
this.mdcPropertyMap = mdcPropertyMap;
|
||||||
|
|
||||||
|
final LoggerContextVO oldVo = event.getLoggerContextVO();
|
||||||
|
if (oldVo != null) {
|
||||||
|
vo = new LoggerContextVO(oldVo.getName(), mdcPropertyMap, oldVo.getBirthTime());
|
||||||
|
} else {
|
||||||
|
vo = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] getArgumentArray() {
|
||||||
|
return event.getArgumentArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Level getLevel() {
|
||||||
|
return event.getLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLoggerName() {
|
||||||
|
return event.getLoggerName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getThreadName() {
|
||||||
|
return event.getThreadName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IThrowableProxy getThrowableProxy() {
|
||||||
|
return event.getThrowableProxy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepareForDeferredProcessing() {
|
||||||
|
event.prepareForDeferredProcessing();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoggerContextVO getLoggerContextVO() {
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return event.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTimeStamp() {
|
||||||
|
return event.getTimeStamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StackTraceElement[] getCallerData() {
|
||||||
|
return event.getCallerData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasCallerData() {
|
||||||
|
return event.hasCallerData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Marker getMarker() {
|
||||||
|
return event.getMarker();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFormattedMessage() {
|
||||||
|
return event.getFormattedMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getMDCPropertyMap() {
|
||||||
|
return mdcPropertyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A synonym for {@link #getMDCPropertyMap}.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #getMDCPropertyMap()}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public Map<String, String> getMdc() {
|
||||||
|
return event.getMDCPropertyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return event.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed 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 io.opentelemetry.instrumentation.logback.v1_0_0;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
|
import ch.qos.logback.core.Appender;
|
||||||
|
import ch.qos.logback.core.UnsynchronizedAppenderBase;
|
||||||
|
import ch.qos.logback.core.spi.AppenderAttachable;
|
||||||
|
import ch.qos.logback.core.spi.AppenderAttachableImpl;
|
||||||
|
import io.opentelemetry.trace.Span;
|
||||||
|
import io.opentelemetry.trace.SpanContext;
|
||||||
|
import io.opentelemetry.trace.TracingContextUtils;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class OpenTelemetryAppender extends UnsynchronizedAppenderBase<ILoggingEvent>
|
||||||
|
implements AppenderAttachable<ILoggingEvent> {
|
||||||
|
|
||||||
|
private final AppenderAttachableImpl<ILoggingEvent> aai = new AppenderAttachableImpl<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void append(ILoggingEvent event) {
|
||||||
|
Span currentSpan = TracingContextUtils.getCurrentSpan();
|
||||||
|
if (!currentSpan.getContext().isValid()) {
|
||||||
|
aai.appendLoopOnAppenders(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> contextData = new HashMap<>();
|
||||||
|
SpanContext spanContext = currentSpan.getContext();
|
||||||
|
contextData.put("traceId", spanContext.getTraceId().toLowerBase16());
|
||||||
|
contextData.put("spanId", spanContext.getSpanId().toLowerBase16());
|
||||||
|
contextData.put("traceFlags", spanContext.getTraceFlags().toLowerBase16());
|
||||||
|
|
||||||
|
Map<String, String> eventContext = event.getMDCPropertyMap();
|
||||||
|
if (eventContext == null) {
|
||||||
|
eventContext = contextData;
|
||||||
|
} else {
|
||||||
|
eventContext = new UnionMap<>(eventContext, contextData);
|
||||||
|
}
|
||||||
|
|
||||||
|
ILoggingEvent wrapped = new LoggingEventWrapper(event, eventContext);
|
||||||
|
aai.appendLoopOnAppenders(wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAppender(Appender<ILoggingEvent> appender) {
|
||||||
|
aai.addAppender(appender);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Appender<ILoggingEvent>> iteratorForAppenders() {
|
||||||
|
return aai.iteratorForAppenders();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Appender<ILoggingEvent> getAppender(String name) {
|
||||||
|
return aai.getAppender(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAttached(Appender<ILoggingEvent> appender) {
|
||||||
|
return aai.isAttached(appender);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void detachAndStopAllAppenders() {
|
||||||
|
aai.detachAndStopAllAppenders();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean detachAppender(Appender<ILoggingEvent> appender) {
|
||||||
|
return aai.detachAppender(appender);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean detachAppender(String name) {
|
||||||
|
return aai.detachAppender(name);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,211 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed 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 io.opentelemetry.instrumentation.logback.v1_0_0;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.AbstractSet;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable view over two maps, with keys resolving from the first map first, or otherwise the
|
||||||
|
* second if not present in the first.
|
||||||
|
*/
|
||||||
|
final class UnionMap<K, V> extends AbstractMap<K, V> {
|
||||||
|
|
||||||
|
private final Map<K, V> first;
|
||||||
|
private final Map<K, V> second;
|
||||||
|
private int size = -1;
|
||||||
|
private Set<Entry<K, V>> entrySet;
|
||||||
|
|
||||||
|
UnionMap(Map<K, V> first, Map<K, V> second) {
|
||||||
|
this.first = first;
|
||||||
|
this.second = second;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
if (size >= 0) {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<K, V> a;
|
||||||
|
final Map<K, V> b;
|
||||||
|
if (first.size() >= second.size()) {
|
||||||
|
a = first;
|
||||||
|
b = second;
|
||||||
|
} else {
|
||||||
|
a = second;
|
||||||
|
b = first;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = a.size();
|
||||||
|
if (!b.isEmpty()) {
|
||||||
|
for (K k : b.keySet()) {
|
||||||
|
if (!a.containsKey(k)) {
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return first.isEmpty() && second.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
return first.containsKey(key) || second.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
return first.containsValue(value) || second.containsValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(Object key) {
|
||||||
|
final V value = first.get(key);
|
||||||
|
return value != null ? value : second.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V put(K key, V value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V remove(Object key) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entry<K, V>> entrySet() {
|
||||||
|
if (entrySet != null) {
|
||||||
|
return entrySet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for dupes first to reduce allocations on the vastly more common case where there aren't
|
||||||
|
// any.
|
||||||
|
boolean secondHasDupes = false;
|
||||||
|
for (Entry<K, V> entry : second.entrySet()) {
|
||||||
|
if (first.containsKey(entry.getKey())) {
|
||||||
|
secondHasDupes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<Entry<K, V>> filteredSecond;
|
||||||
|
if (!secondHasDupes) {
|
||||||
|
filteredSecond = second.entrySet();
|
||||||
|
} else {
|
||||||
|
filteredSecond = new LinkedHashSet<>();
|
||||||
|
for (Entry<K, V> entry : second.entrySet()) {
|
||||||
|
if (!first.containsKey(entry.getKey())) {
|
||||||
|
filteredSecond.add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entrySet =
|
||||||
|
Collections.unmodifiableSet(new ConcatenatedSet<>(first.entrySet(), filteredSecond));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Member sets must be deduped by caller.
|
||||||
|
private static final class ConcatenatedSet<T> extends AbstractSet<T> {
|
||||||
|
|
||||||
|
private final Set<T> first;
|
||||||
|
private final Set<T> second;
|
||||||
|
|
||||||
|
private final int size;
|
||||||
|
|
||||||
|
ConcatenatedSet(Set<T> first, Set<T> second) {
|
||||||
|
this.first = first;
|
||||||
|
this.second = second;
|
||||||
|
|
||||||
|
size = first.size() + second.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(T t) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<? extends T> c) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(Collection<?> c) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return new Iterator<T>() {
|
||||||
|
|
||||||
|
final Iterator<T> firstItr = first.iterator();
|
||||||
|
final Iterator<T> secondItr = second.iterator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return firstItr.hasNext() || secondItr.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T next() {
|
||||||
|
if (firstItr.hasNext()) {
|
||||||
|
return firstItr.next();
|
||||||
|
}
|
||||||
|
return secondItr.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed 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 io.opentelemetry.instrumentation.logback.v1_0_0
|
||||||
|
|
||||||
|
class LogbackTest extends AbstractLogbackTest {
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed 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 io.opentelemetry.instrumentation.logback.v1_0_0
|
||||||
|
|
||||||
|
import spock.lang.Specification
|
||||||
|
|
||||||
|
class UnionMapTest extends Specification {
|
||||||
|
|
||||||
|
def "maps"() {
|
||||||
|
when:
|
||||||
|
def union = new UnionMap(first, second)
|
||||||
|
|
||||||
|
then:
|
||||||
|
union['cat'] == 'meow'
|
||||||
|
union['dog'] == 'bark'
|
||||||
|
union['foo'] == 'bar'
|
||||||
|
union['hello'] == 'world'
|
||||||
|
union['giraffe'] == null
|
||||||
|
|
||||||
|
!union.isEmpty()
|
||||||
|
union.size() == 4
|
||||||
|
union.containsKey('cat')
|
||||||
|
union.containsKey('dog')
|
||||||
|
union.containsKey('foo')
|
||||||
|
union.containsKey('hello')
|
||||||
|
!union.containsKey('giraffe')
|
||||||
|
|
||||||
|
def set = union.entrySet()
|
||||||
|
!set.isEmpty()
|
||||||
|
set.size() == 4
|
||||||
|
def copy = new ArrayList(set)
|
||||||
|
copy.size() == 4
|
||||||
|
|
||||||
|
where:
|
||||||
|
first | second
|
||||||
|
[cat: 'meow', dog: 'bark'] | [foo: 'bar', hello: 'world']
|
||||||
|
// Overlapping entries in second does not affect the union.
|
||||||
|
[cat: 'meow', dog: 'bark'] | [foo: 'bar', hello: 'world', cat: 'moo']
|
||||||
|
}
|
||||||
|
|
||||||
|
def "both empty"() {
|
||||||
|
when:
|
||||||
|
def union = new UnionMap(Collections.emptyMap(), Collections.emptyMap())
|
||||||
|
|
||||||
|
then:
|
||||||
|
union.isEmpty()
|
||||||
|
union.size() == 0
|
||||||
|
union['cat'] == null
|
||||||
|
|
||||||
|
def set = union.entrySet()
|
||||||
|
set.isEmpty()
|
||||||
|
set.size() == 0
|
||||||
|
def copy = new ArrayList(set)
|
||||||
|
copy.size() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
def "one empty"() {
|
||||||
|
when:
|
||||||
|
def union = new UnionMap(first, second)
|
||||||
|
|
||||||
|
then:
|
||||||
|
!union.isEmpty()
|
||||||
|
union.size() == 1
|
||||||
|
union['cat'] == 'meow'
|
||||||
|
union['dog'] == null
|
||||||
|
|
||||||
|
def set = union.entrySet()
|
||||||
|
!set.isEmpty()
|
||||||
|
set.size() == 1
|
||||||
|
def copy = new ArrayList(set)
|
||||||
|
copy.size() == 1
|
||||||
|
|
||||||
|
where:
|
||||||
|
first | second
|
||||||
|
[cat: 'meow'] | Collections.emptyMap()
|
||||||
|
Collections.emptyMap() | [cat: 'meow']
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright The OpenTelemetry Authors
|
||||||
|
~
|
||||||
|
~ Licensed 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<configuration>
|
||||||
|
<appender name="LIST" class="ch.qos.logback.core.read.ListAppender" />
|
||||||
|
|
||||||
|
<appender name="OTEL" class="io.opentelemetry.instrumentation.logback.v1_0_0.OpenTelemetryAppender">
|
||||||
|
<appender-ref ref="LIST" />
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="test">
|
||||||
|
<level value="info" />
|
||||||
|
<appender-ref ref="OTEL" />
|
||||||
|
</logger>
|
||||||
|
</configuration>
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply from: "$rootDir/gradle/java.gradle"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly project(":instrumentation:logback:logback-1.0.0:library")
|
||||||
|
|
||||||
|
api project(':testing-common')
|
||||||
|
|
||||||
|
api group: 'ch.qos.logback', name: 'logback-classic', version: '1.0.0'
|
||||||
|
|
||||||
|
implementation deps.guava
|
||||||
|
|
||||||
|
implementation deps.groovy
|
||||||
|
implementation deps.opentelemetryApi
|
||||||
|
implementation deps.spock
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed 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 io.opentelemetry.instrumentation.logback.v1_0_0
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent
|
||||||
|
import ch.qos.logback.core.read.ListAppender
|
||||||
|
import io.opentelemetry.auto.test.utils.TraceUtils
|
||||||
|
import io.opentelemetry.trace.Span
|
||||||
|
import io.opentelemetry.trace.TracingContextUtils
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import spock.lang.Shared
|
||||||
|
import spock.lang.Specification
|
||||||
|
|
||||||
|
abstract class AbstractLogbackTest extends Specification {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger("test")
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
ListAppender<ILoggingEvent> listAppender
|
||||||
|
|
||||||
|
def setupSpec() {
|
||||||
|
ch.qos.logback.classic.Logger logbackLogger = (ch.qos.logback.classic.Logger) logger
|
||||||
|
listAppender = (logbackLogger.getAppender("OTEL") as OpenTelemetryAppender)
|
||||||
|
.getAppender("LIST") as ListAppender<ILoggingEvent>
|
||||||
|
}
|
||||||
|
|
||||||
|
def setup() {
|
||||||
|
listAppender.list.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
def "no ids when no span"() {
|
||||||
|
when:
|
||||||
|
logger.info("log message 1")
|
||||||
|
logger.info("log message 2")
|
||||||
|
|
||||||
|
def events = listAppender.list
|
||||||
|
|
||||||
|
then:
|
||||||
|
events.size() == 2
|
||||||
|
events[0].message == "log message 1"
|
||||||
|
events[0].getMDCPropertyMap().get("traceId") == null
|
||||||
|
events[0].getMDCPropertyMap().get("spanId") == null
|
||||||
|
events[0].getMDCPropertyMap().get("traceFlags") == null
|
||||||
|
|
||||||
|
events[1].message == "log message 2"
|
||||||
|
events[1].getMDCPropertyMap().get("traceId") == null
|
||||||
|
events[1].getMDCPropertyMap().get("spanId") == null
|
||||||
|
events[1].getMDCPropertyMap().get("traceFlags") == null
|
||||||
|
}
|
||||||
|
|
||||||
|
def "ids when span"() {
|
||||||
|
when:
|
||||||
|
Span span1
|
||||||
|
TraceUtils.runUnderTrace("test") {
|
||||||
|
span1 = TracingContextUtils.currentSpan
|
||||||
|
logger.info("log message 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("log message 2")
|
||||||
|
|
||||||
|
Span span2
|
||||||
|
TraceUtils.runUnderTrace("test 2") {
|
||||||
|
span2 = TracingContextUtils.currentSpan
|
||||||
|
logger.info("log message 3")
|
||||||
|
}
|
||||||
|
|
||||||
|
def events = listAppender.list
|
||||||
|
|
||||||
|
then:
|
||||||
|
events.size() == 3
|
||||||
|
events[0].message == "log message 1"
|
||||||
|
events[0].getMDCPropertyMap().get("traceId") == span1.context.traceId.toLowerBase16()
|
||||||
|
events[0].getMDCPropertyMap().get("spanId") == span1.context.spanId.toLowerBase16()
|
||||||
|
events[0].getMDCPropertyMap().get("traceFlags") == span1.context.traceFlags.toLowerBase16()
|
||||||
|
|
||||||
|
events[1].message == "log message 2"
|
||||||
|
events[1].getMDCPropertyMap().get("traceId") == null
|
||||||
|
events[1].getMDCPropertyMap().get("spanId") == null
|
||||||
|
events[1].getMDCPropertyMap().get("traceFlags") == null
|
||||||
|
|
||||||
|
events[2].message == "log message 3"
|
||||||
|
events[2].getMDCPropertyMap().get("traceId") == span2.context.traceId.toLowerBase16()
|
||||||
|
events[2].getMDCPropertyMap().get("spanId") == span2.context.spanId.toLowerBase16()
|
||||||
|
events[2].getMDCPropertyMap().get("traceFlags") == span2.context.traceFlags.toLowerBase16()
|
||||||
|
}
|
||||||
|
}
|
|
@ -121,6 +121,8 @@ include ':instrumentation:lettuce:lettuce-4.0'
|
||||||
include ':instrumentation:lettuce:lettuce-5.0'
|
include ':instrumentation:lettuce:lettuce-5.0'
|
||||||
include ':instrumentation:lettuce:lettuce-5.1'
|
include ':instrumentation:lettuce:lettuce-5.1'
|
||||||
include ':instrumentation:log4j:log4j-2.13.2:library'
|
include ':instrumentation:log4j:log4j-2.13.2:library'
|
||||||
|
include ':instrumentation:logback:logback-1.0.0:library'
|
||||||
|
include ':instrumentation:logback:logback-1.0.0:testing'
|
||||||
include ':instrumentation:mongo:mongo-3.1'
|
include ':instrumentation:mongo:mongo-3.1'
|
||||||
include ':instrumentation:mongo:mongo-3.7'
|
include ':instrumentation:mongo:mongo-3.7'
|
||||||
include ':instrumentation:mongo:mongo-async-3.3'
|
include ':instrumentation:mongo:mongo-async-3.3'
|
||||||
|
|
Loading…
Reference in New Issue