-
Notifications
You must be signed in to change notification settings - Fork 39
feat(logging): OpenTelemetry trace/span ID integration for Java logging library #1596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
da8bd86
ab65496
246c1f5
107144b
b4d3ee7
4808c99
8416b66
7ca6f19
72aa518
db7f163
e67c907
42a49e1
e4c3f06
9f09a85
cefcd73
c600f0d
1d48166
0e61c7d
9cf24ab
e59fd6b
4ff6738
d560806
c8e4eca
ae60311
43d69d0
2d8345e
a8bba3c
e035733
4270847
e4b8de1
fe58cd8
ba86d25
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,69 +16,73 @@ | |
|
||
package com.google.cloud.logging; | ||
|
||
/** | ||
* Class provides a per-thread storage of the {@see Context} instances. | ||
*/ | ||
/** Class provides a per-thread storage of the {@see Context} instances. */ | ||
public class ContextHandler { | ||
|
||
public enum ContextPriority { | ||
NO_INPUT, XCLOUD_HEADER, W3_HEADER, OTEL_EXTRACTED | ||
} | ||
|
||
private static final ThreadLocal<Context> contextHolder = initContextHolder(); | ||
private static final ThreadLocal<ContextPriority> currentPriority = new ThreadLocal<ContextPriority>(); | ||
public enum ContextPriority { | ||
NO_INPUT, | ||
XCLOUD_HEADER, | ||
W3_HEADER, | ||
OTEL_EXTRACTED | ||
} | ||
|
||
/** | ||
* Initializes the context holder to {@link InheritableThreadLocal} if {@link LogManager} | ||
* configuration property {@code com.google.cloud.logging.ContextHandler.useInheritedContext} is | ||
* set to {@code true} or to {@link ThreadLocal} otherwise. | ||
* | ||
* @return instance of the context holder. | ||
*/ | ||
private static ThreadLocal<Context> initContextHolder() { | ||
LoggingConfig config = new LoggingConfig(ContextHandler.class.getName()); | ||
if (config.getUseInheritedContext()) { | ||
return new InheritableThreadLocal<>(); | ||
} else { | ||
return new ThreadLocal<>(); | ||
} | ||
} | ||
private static final ThreadLocal<Context> contextHolder = initContextHolder(); | ||
private static final ThreadLocal<ContextPriority> currentPriority = | ||
new ThreadLocal<ContextPriority>(); | ||
|
||
public Context getCurrentContext() { | ||
return contextHolder.get(); | ||
/** | ||
* Initializes the context holder to {@link InheritableThreadLocal} if {@link LogManager} | ||
* configuration property {@code com.google.cloud.logging.ContextHandler.useInheritedContext} is | ||
* set to {@code true} or to {@link ThreadLocal} otherwise. | ||
* | ||
* @return instance of the context holder. | ||
*/ | ||
private static ThreadLocal<Context> initContextHolder() { | ||
LoggingConfig config = new LoggingConfig(ContextHandler.class.getName()); | ||
if (config.getUseInheritedContext()) { | ||
return new InheritableThreadLocal<>(); | ||
} else { | ||
return new ThreadLocal<>(); | ||
} | ||
} | ||
|
||
public Context getCurrentContext() { | ||
return contextHolder.get(); | ||
} | ||
|
||
public void setCurrentContext(Context context) { | ||
contextHolder.set(context); | ||
} | ||
|
||
/** | ||
* Sets the context based on the priority. Overrides traceId, spanId and TraceSampled if the passed priority is higher. | ||
* HttpRequest values will be retrieved and combined from existing context if HttpRequest in the new context is empty . | ||
*/ | ||
public void setCurrentContext(Context context) { | ||
contextHolder.set(context); | ||
} | ||
|
||
public void setCurrentContext(Context context, ContextPriority priority) { | ||
if ((currentPriority.get() == null || priority.compareTo(currentPriority.get()) >= 0) && context != null) { | ||
Context.Builder combinedContextBuilder = Context.newBuilder().setTraceId(context.getTraceId()).setSpanId(context.getSpanId()).setTraceSampled(context.getTraceSampled()); | ||
Context currentContext = getCurrentContext(); | ||
/** | ||
* Sets the context based on the priority. Overrides traceId, spanId and TraceSampled if the | ||
* passed priority is higher. HttpRequest values will be retrieved and combined from existing | ||
* context if HttpRequest in the new context is empty . | ||
*/ | ||
public void setCurrentContext(Context context, ContextPriority priority) { | ||
if ((currentPriority.get() == null || priority.compareTo(currentPriority.get()) >= 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added |
||
&& context != null) { | ||
Context.Builder combinedContextBuilder = | ||
Context.newBuilder() | ||
.setTraceId(context.getTraceId()) | ||
.setSpanId(context.getSpanId()) | ||
.setTraceSampled(context.getTraceSampled()); | ||
Context currentContext = getCurrentContext(); | ||
|
||
if (context.getHttpRequest() != null) | ||
{ | ||
combinedContextBuilder.setRequest(context.getHttpRequest()); | ||
} | ||
// Combines HttpRequest from the existing context if HttpRequest in new context is empty. | ||
else if (currentContext != null && currentContext.getHttpRequest() != null ){ | ||
combinedContextBuilder.setRequest(currentContext.getHttpRequest()); | ||
} | ||
if (context.getHttpRequest() != null) { | ||
combinedContextBuilder.setRequest(context.getHttpRequest()); | ||
} | ||
// Combines HttpRequest from the existing context if HttpRequest in new context is empty. | ||
else if (currentContext != null && currentContext.getHttpRequest() != null) { | ||
combinedContextBuilder.setRequest(currentContext.getHttpRequest()); | ||
} | ||
|
||
contextHolder.set(combinedContextBuilder.build()); | ||
currentPriority.set(priority); | ||
} | ||
contextHolder.set(combinedContextBuilder.build()); | ||
currentPriority.set(priority); | ||
} | ||
} | ||
|
||
|
||
public void removeCurrentContext() { | ||
contextHolder.remove(); | ||
} | ||
public void removeCurrentContext() { | ||
contextHolder.remove(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ | |
import com.google.cloud.MonitoredResourceDescriptor; | ||
import com.google.cloud.PageImpl; | ||
import com.google.cloud.Tuple; | ||
import com.google.cloud.logging.ContextHandler.ContextPriority; | ||
import com.google.cloud.logging.spi.v2.LoggingRpc; | ||
import com.google.common.annotations.VisibleForTesting; | ||
import com.google.common.base.Ascii; | ||
|
@@ -89,6 +90,7 @@ | |
import com.google.logging.v2.WriteLogEntriesResponse; | ||
import com.google.protobuf.Empty; | ||
import com.google.protobuf.util.Durations; | ||
import io.opentelemetry.api.trace.Span; | ||
import java.text.ParseException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
@@ -98,8 +100,6 @@ | |
import java.util.concurrent.ExecutionException; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.TimeoutException; | ||
import io.opentelemetry.api.trace.Span; | ||
import com.google.cloud.logging.ContextHandler.ContextPriority; | ||
|
||
class LoggingImpl extends BaseService<LoggingOptions> implements Logging { | ||
protected static final String RESOURCE_NAME_FORMAT = "projects/%s/traces/%s"; | ||
|
@@ -839,8 +839,7 @@ public Iterable<LogEntry> populateMetadata( | |
|
||
ContextHandler contextHandler = new ContextHandler(); | ||
// Populate trace/span ID from OpenTelemetry span context to logging context. | ||
if (Span.current().getSpanContext().isValid()) | ||
{ | ||
if (Span.current().getSpanContext().isValid()) { | ||
Context.Builder contextBuilder = Context.newBuilder().loadOpenTelemetryContext(); | ||
contextHandler.setCurrentContext(contextBuilder.build(), ContextPriority.OTEL_EXTRACTED); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you set the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unlike other logging libraries, java-logging library doesn't call other setCurrentContext() within the library itself. It's usually delegated to customers or integration library. An example is mentioned here (2nd approach to set trace for log entries): RequestContextFilter in java-logging-servlet-initializer repository will call setCurrentContext () and load tracing context from cloud tracing or W3C headers. And we do have a plan mentioned here to switch to the new setCurrentContext(Context context, ContextPriority priority) in java-logging-servlet-initializer after the change in this repository is released. I have also added some unit tests here for |
||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.