-
Notifications
You must be signed in to change notification settings - Fork 40
Fix trace exporter #193
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
Fix trace exporter #193
Conversation
...ters/trace/src/test/java/com/google/cloud/opentelemetry/trace/DeferredTraceExporterTest.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/TraceExporter.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/TraceExporter.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/TraceExporter.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/InternalTraceExporter.java
Outdated
Show resolved
Hide resolved
Map.of("User-Agent", "opentelemetry-operations-java/" + TraceVersions.EXPORTER_VERSION); | ||
private static final HeaderProvider HEADER_PROVIDER = () -> HEADERS; | ||
|
||
private static InternalTraceExporter createWithClient( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this wrapper?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So one thing to note here is that this is the exact same class as the TraceExporter
in the current main.
It has just been renamed to InternalTraceExporter
. In order to minimize the changes, I did not edit any existing logic.
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/InternalTraceExporter.java
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/InternalTraceExporter.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/InternalTraceExporter.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/InternalTraceExporter.java
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/TraceExporter.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/TraceExporter.java
Outdated
Show resolved
Hide resolved
0e740ec
to
9708c01
Compare
281dd19
to
986208e
Compare
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/NoopTraceExporter.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/TraceExporter.java
Outdated
Show resolved
Hide resolved
@@ -60,6 +59,16 @@ public abstract class TraceConfiguration { | |||
.put("thread.id", "/tid") | |||
.build(); | |||
|
|||
private Supplier<String> projectIdProvider; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- An auto-value class should not have concrete fields or setters.
- This should be named
projectIdSupplier
to be consistent with the type name.
One way to do this would be
- Remove the field
- Add an abstract method
getProjectIdSupplier
toTraceConfiguration
- Change the existing abstract method
getProjectId
to a concrete, memoized method that callsgetProjectIdSupplier().get()
. - Add a concrete method
setProjectId
toTraceConfiguration.Bulder
that callssetProjectIdSupplier(() -> id)
.
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/TraceExporter.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/TraceExporter.java
Outdated
Show resolved
Hide resolved
} | ||
|
||
@Override | ||
public CompletableResultCode flush() { | ||
// We do no exporter buffering of spans, so we're always flushed. | ||
return CompletableResultCode.ofSuccess(); | ||
if (internalTraceExporter.get() == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we unconditionally return success for this method, without even checking whether the field has been set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically, we can. But the purpose of this class it defer business logic to the internalTraceExporter
.
However, I was thinking if failure is the correct code here. Any thoughts ? This condition would happen if flush is called before export somehow.
spans.add(translator.generateSpan(spanData, projectId)); | ||
public CompletableResultCode export(@Nonnull Collection<SpanData> spanDataList) { | ||
if (Objects.isNull(internalTraceExporter.get())) { | ||
synchronized (this) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused by the synchronization model here - usually an AtomicReference
is used to avoid synchronization. You might be able to avoid a lot of this complexity by changing internalTraceExporter
to Suppliers.memoized(() -> new InternalTraceExporter(this.config))
and making it do all the work.
projectId, cloudTraceClient, attributeMappings, fixedAttributes); | ||
} | ||
|
||
static SpanExporter createWithConfiguration(TraceConfiguration configuration) throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider wrapping the IOException at this layer and removing it from the signature. Since our external API no longer has an IOException, this just adds noise to our implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this exception may be thrown from two possible places, handling it at the InternalTraceExporter
level makes the implementation a little ugly since both need to be handled. Also, InternalTraceExporter
is the class that actually has the implementation so handling it there would introduce more noise to our implementation detail.
I think it makes sense to keep it in the function signature here since it has a package private access and this exception is handled in TraceExporter
for both cases.
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/TraceExporter.java
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/NoopSpanExporter.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/TraceConfiguration.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/TraceExporter.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/NoopSpanExporter.java
Outdated
Show resolved
Hide resolved
|
||
private TraceExporter(TraceConfiguration configuration) { | ||
this.internalTraceExporterSupplier = | ||
Suppliers.memoize( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice use of the memoize function.
exporters/trace/src/main/java/com/google/cloud/opentelemetry/trace/TraceExporter.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/test/java/com/google/cloud/opentelemetry/trace/TraceConfigurationTest.java
Show resolved
Hide resolved
exporters/trace/src/test/java/com/google/cloud/opentelemetry/trace/TraceExporterTest.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/test/java/com/google/cloud/opentelemetry/trace/TraceExporterTest.java
Outdated
Show resolved
Hide resolved
exporters/trace/src/test/java/com/google/cloud/opentelemetry/trace/TraceExporterTest.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Noop version should not be returning any failures.
PR to attempt fixes for #141.
These changes prevent a crash and tests verified that traces are being generated. This is an early PR for any feedback.
Startup code for testing -
User Facing Changes (NONE)
The way users interact with this API has not changed, but this PR changes the internal implementation of how a
TraceExporter
object is initialized. The initialization is now deferred till the very firstexport
is called.Summary of Changes (Including implementation details) -
TraceExporter
TraceExporter
class has been converted toInternalTraceExporter
- this class contains all the logic required and core logic for exporting traces is unchanged.TraceExporter
is a wrapper around theInternalTraceExporter
and delegates call toInternalTraceExporter
at the appropriate time.TraceExporter
holds a reference toInternalTraceExporter
which gets initialized when the very firstexport
is called. This initialization is only done once in a thread-safe manner (via memoization).TraceConfiguration & its interaction with TraceExporter
project ID
.project ID
is internally used in aSupplier
. If the user does not callsetProjectId
to provide a Project ID, the generatedSupplier
will provide default Project ID viaServiceOptions#getDefaultProjectId
. The supplier used is memoized so the value is retrieved only once.TraceConfiguration
object using the builder no longer attempts to fetch the defaultProjectId immediately viaServiceOptions.getDefaultProjectId()
call.TraceConfiguration
object using builder will now generate aSupplier
which will resolve to a project ID later whenget()
is called on it. This is done byInternalTraceExporter
when it is lazy loaded.TraceConfiguration.getProjectId()
will always return whatever user has specified as project ID.ServiceOptions.getDefaultProjectId()
(for retrieving default project ID) fails, theTraceExporter
implementation will be changed to a noop implementation and the user will be notified of this via logs.