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.1'
|
||||
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.7'
|
||||
include ':instrumentation:mongo:mongo-async-3.3'
|
||||
|
|
Loading…
Reference in New Issue