-
Notifications
You must be signed in to change notification settings - Fork 129
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
Add LogRecordProcessor to record event log records as span events #1551
base: main
Are you sure you want to change the base?
Add LogRecordProcessor to record event log records as span events #1551
Conversation
@breedx-splk, @LikeTheSalad - I'm happy to become a component owner for |
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.
Thanks. This is helpful and I didn't expect it to show up so soon! I had a few small questions/comments but this seems solid.
processors/README.md
Outdated
- event_to_span_event_bridge: {} | ||
``` | ||
// TODO(jack-berg): remove "{}" after merging / rle https://github.com/open-telemetry/opentelemetry-java/pull/6891/files |
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 think this comment (assuming it stays here temporarily) ought to be inside the yaml block.
|
||
// For EventToSpanEventBridge | ||
implementation("io.opentelemetry:opentelemetry-exporter-otlp-common") | ||
implementation("com.fasterxml.jackson.core:jackson-core") |
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.
Where is jackson used? I don't see it. Is it a transitive runtime-only dependency maybe?
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.
Yeah its required to call Marshaler#writeJsonTo(OutputStream)
opentelemetry-exporter-logging-otlp
has the same dependency here: https://github.com/open-telemetry/opentelemetry-java/blob/main/exporters/logging-otlp/build.gradle.kts#L19
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.
Ok, that's what I was guessing. Can we change this to runtimeOnly()
then?
* <li>{@link LogRecordData#getObservedTimestampEpochNanos()} is mapped to span event attribute | ||
* with key {@code log.record.observed_timestamp} | ||
* <li>{@link LogRecordData#getSeverity()} is mapped to span event attribute with key {@code | ||
* log.record.severity_number} |
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.
Do you like log.record.severity_number
better than simply log.record.severity
? Maybe you're just trying to maintain consistency with the protos?
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.
Maybe you're just trying to maintain consistency with the protos?
Exactly... if / when the spec comes out and tell us how to translate between events and span events, we'll of course follow those instructions. But until then, the less opinionated we can be the better, and following the proto field naming is a way to avoid having an opinion.
*/ | ||
public final class EventToSpanEventBridge implements LogRecordProcessor { | ||
|
||
private static final Logger LOGGER = Logger.getLogger(EventToSpanEventBridge.class.getName()); |
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.
We use lowercase for these in almost every other place. Also, I know it's inconsistent with the convention of naming static final constants all uppercase....conflicting conventions are at play here.
private static final Logger LOGGER = Logger.getLogger(EventToSpanEventBridge.class.getName()); | |
private static final Logger logger = Logger.getLogger(EventToSpanEventBridge.class.getName()); |
Value<?> body = logRecord.getBodyValue(); | ||
if (body != null) { | ||
MarshalerWithSize marshaler = AnyValueMarshaler.create(body); | ||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
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'd prefer to use the more general type on the left-hand side and then the variable name also gets less cryptic.
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
OutputStream out = new ByteArrayOutputStream(); |
@@ -56,6 +56,7 @@ components: | |||
processors: | |||
- LikeTheSalad | |||
- breedx-splk | |||
- jack-berg |
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.
Thanks for the help!
LogRecordData logRecordData = logRecord.toLogRecordData(); | ||
String eventName = logRecordData.getAttributes().get(EVENT_NAME); | ||
if (eventName == null) { | ||
return; | ||
} |
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.
Doesn't have to be addressed in this PR, but it's a little disappointing that we have to do the toLogRecordData()
conversion first in order to check to see if an attribute exists on the ReadWriteLogRecord
. In fact, the ReadWriteLogRecord
doesn't allow much reading at all, does it...? /rant
At least we're within onEmit
which should only be relevant for events (so that event.name
is never actually null
) but still... :)
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.
It is disappointing and needlessly wasteful. We have a todo to add more getters for directly accessing bits of info in ReadWriteLogRecord
. May be time to pick that up..
Separately, I wonder if we can / should add a helper method boolean isEvent()
since we anticipate this being a common thing to access.
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.
Yeah, I like that idea. We can revisit this here later when that happens.
span.end(); | ||
} | ||
|
||
assertThat(spanExporter.getFinishedSpanItems()).isEmpty(); |
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.
It feels like the goal was to test the isRecording()
check/functionality but instead it seems like this is just testing out the sampler. Maybe it's the case that the SamplingResult
determines what the implementation of isRecording()
returns? In any case, it's not super obvious to the reader why we would be testing that sampling works here.
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.
Maybe it's the case that the SamplingResult determines what the implementation of isRecording() returns?
Yes that's right. I can add a comment to help the reader connect these dots.
Resolves open-telemetry/opentelemetry-java#6880.
Supersedes open-telemetry/opentelemetry-java#6650.
The event {@link LogRecordData} is converted to attributes on the span event as follows:
event.name
attribute is mapped to span event nameLogRecordData#getTimestampEpochNanos()
is mapped to span event timestampLogRecordData#getAttributes()
are mapped to span event attributes, excludingevent.name
LogRecordData#getObservedTimestampEpochNanos()
is mapped to span event attribute with keylog.record.observed_timestamp
LogRecordData#getSeverity()
is mapped to span event attribute with keylog.record.severity_number
LogRecordData#getBodyValue()
is mapped to span event attribute with keylog.record.body
, as an escaped JSON string following the standard protobuf JSON encoding. NOTE: this results in an encoding that feels more verbose than necessary, but allows us to avoid having to define a new encoding.LogRecordData#getTotalAttributeCount() - LogRecordData#getAttributes().size()
is mapped to span event attributelog.record.dropped_attributes_count