-
Notifications
You must be signed in to change notification settings - Fork 853
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 getters/accessors for readable fields in ReadWriteLogRecord. #6924
Merged
jack-berg
merged 11 commits into
open-telemetry:main
from
breedx-splk:add_getters_to_WRLR
Dec 19, 2024
+127
−8
Merged
Changes from 2 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
85b3a60
Add getters/accessors for readable fields in ReadWriteLogRecord.
breedx-splk 23c1c1b
allow nonbreaking interface extension
breedx-splk 84e8910
eliminate conversion in test
breedx-splk daefe10
remove easy way to pick up getters, now with even more duplication!
breedx-splk f251094
remove getResource() because it doesn't exist on ReadableSpan.
breedx-splk 2c68421
spotless
breedx-splk 1440a2e
remove getResource()
breedx-splk d3b5acb
remove direct usage of deprecated body
breedx-splk 26039f4
deprecation
breedx-splk 6a458a2
remove methods
breedx-splk a934242
remove local change that snuck in sorry whoops
breedx-splk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,21 @@ | ||
Comparing source compatibility of opentelemetry-sdk-logs-1.46.0-SNAPSHOT.jar against opentelemetry-sdk-logs-1.45.0.jar | ||
No changes. | ||
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.logs.ReadWriteLogRecord (not serializable) | ||
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0 | ||
+++ NEW INTERFACE: io.opentelemetry.sdk.logs.data.LogRecordData | ||
+++ NEW METHOD: PUBLIC(+) java.lang.Object getAttribute(io.opentelemetry.api.common.AttributeKey<T>) | ||
+++ NEW ANNOTATION: javax.annotation.Nullable | ||
GENERIC TEMPLATES: +++ T:java.lang.Object | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.api.common.Attributes getAttributes() | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.logs.data.Body getBody() | ||
+++ NEW ANNOTATION: java.lang.Deprecated | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.api.common.Value<?> getBodyValue() | ||
+++ NEW ANNOTATION: javax.annotation.Nullable | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.InstrumentationScopeInfo getInstrumentationScopeInfo() | ||
+++ NEW METHOD: PUBLIC(+) long getObservedTimestampEpochNanos() | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.resources.Resource getResource() | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.api.logs.Severity getSeverity() | ||
+++ NEW METHOD: PUBLIC(+) java.lang.String getSeverityText() | ||
+++ NEW ANNOTATION: javax.annotation.Nullable | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.api.trace.SpanContext getSpanContext() | ||
+++ NEW METHOD: PUBLIC(+) long getTimestampEpochNanos() | ||
+++ NEW METHOD: PUBLIC(+) int getTotalAttributeCount() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,14 +7,20 @@ | |
|
||
import io.opentelemetry.api.common.AttributeKey; | ||
import io.opentelemetry.api.common.Attributes; | ||
import io.opentelemetry.api.common.Value; | ||
import io.opentelemetry.api.logs.Severity; | ||
import io.opentelemetry.api.trace.SpanContext; | ||
import io.opentelemetry.sdk.common.InstrumentationScopeInfo; | ||
import io.opentelemetry.sdk.logs.data.LogRecordData; | ||
import io.opentelemetry.sdk.resources.Resource; | ||
import javax.annotation.Nullable; | ||
|
||
/** | ||
* A log record that can be read from and written to. | ||
* | ||
* @since 1.27.0 | ||
*/ | ||
public interface ReadWriteLogRecord { | ||
public interface ReadWriteLogRecord extends LogRecordData { | ||
|
||
/** | ||
* Sets an attribute on the log record. If the log record previously contained a mapping for the | ||
|
@@ -47,7 +53,87 @@ | |
/** Return an immutable {@link LogRecordData} instance representing this log record. */ | ||
LogRecordData toLogRecordData(); | ||
|
||
// TODO: add additional log record accessors. Currently, all fields can be accessed indirectly via | ||
// #toLogRecordData() at the expense of additional allocations. | ||
/** | ||
* Returns the value of a given attribute if it exists. This is the equivalent of calling | ||
* getAttributes().get(key) | ||
*/ | ||
@Nullable | ||
default <T> T getAttribute(AttributeKey<T> key) { | ||
return toLogRecordData().getAttributes().get(key); | ||
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. Maybe it would be worth adding some unit tests for these default methods? It could be beneficial for possible future classes that do not overwrite them (or overwrite them partially) |
||
} | ||
|
||
/** Returns the resource of this log record. */ | ||
@Override | ||
default Resource getResource() { | ||
breedx-splk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return toLogRecordData().getResource(); | ||
} | ||
|
||
/** Returns the instrumentation scope that generated this log. */ | ||
@Override | ||
default InstrumentationScopeInfo getInstrumentationScopeInfo() { | ||
return toLogRecordData().getInstrumentationScopeInfo(); | ||
} | ||
|
||
/** Returns the timestamp at which the log record occurred, in epoch nanos. */ | ||
@Override | ||
default long getTimestampEpochNanos() { | ||
return toLogRecordData().getTimestampEpochNanos(); | ||
} | ||
|
||
/** Returns the timestamp at which the log record was observed, in epoch nanos. */ | ||
@Override | ||
default long getObservedTimestampEpochNanos() { | ||
return toLogRecordData().getTimestampEpochNanos(); | ||
} | ||
|
||
/** Return the span context for this log, or {@link SpanContext#getInvalid()} if unset. */ | ||
@Override | ||
default SpanContext getSpanContext() { | ||
return toLogRecordData().getSpanContext(); | ||
} | ||
|
||
/** Returns the severity for this log, or {@link Severity#UNDEFINED_SEVERITY_NUMBER} if unset. */ | ||
@Override | ||
default Severity getSeverity() { | ||
return toLogRecordData().getSeverity(); | ||
} | ||
|
||
/** Returns the severity text for this log, or null if unset. */ | ||
@Nullable | ||
@Override | ||
default String getSeverityText() { | ||
return toLogRecordData().getSeverityText(); | ||
} | ||
|
||
@Deprecated | ||
@Override | ||
@SuppressWarnings("deprecation") // Implementation of deprecated method | ||
default io.opentelemetry.sdk.logs.data.Body getBody() { | ||
breedx-splk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return toLogRecordData().getBody(); | ||
} | ||
|
||
/** Returns the {@link Value} representation of the log body, of null if unset. */ | ||
@Nullable | ||
@Override | ||
default Value<?> getBodyValue() { | ||
return toLogRecordData().getBodyValue(); | ||
} | ||
|
||
/** Returns the attributes for this log, or {@link Attributes#empty()} if unset. */ | ||
@Override | ||
default Attributes getAttributes() { | ||
breedx-splk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return toLogRecordData().getAttributes(); | ||
} | ||
|
||
/** | ||
* Returns the total number of attributes that were recorded on this log. | ||
* | ||
* <p>This number may be larger than the number of attributes that are attached to this log, if | ||
* the total number recorded was greater than the configured maximum value. See {@link | ||
* LogLimits#getMaxNumberOfAttributes()}. | ||
*/ | ||
@Override | ||
default int getTotalAttributeCount() { | ||
breedx-splk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return toLogRecordData().getTotalAttributeCount(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 not sure what we think about this .... it's not entirely necessary, but felt more like duplication without it. If we remove this extension, then we could remove the deprecated
getBody()
method from this interface, but it feels a tad more fragile to me. If we keep it, then we could probably remove the redundant/duplicated javadoc.Thoughts?
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 not sure about it. On one hand it looks reasonable, on the other hand it seems odd to have toLogRecordData() method in the interface that extends LogRecordData - it is like having a method in subclass that converts it to the superclass. It is a bit confusing to me, so I'd vote for not adding 'extends LogRecordData'.
Actually, maybe we should rename ReadWriteLogRecord to avoid this feeling of parent/child relationship between these two interfaces...
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 don't think this is correct. We should aim to be symmetric to ReadWriteSpan, which inherits its accessors by extending ReadableSpan.
The notable differences between ReadWriteLogRecord / ReadWriteSpan and LogRecordData / SpanData are:
In retrospect, maybe there could have been a single interface instead, but now we have to contend with consistency. I think the way to go is to extend the existing patterns consistently, and independently consider whether there's a better API design. But let's separate the issues.
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.
If we're trying to be forcibly consistent between signals, then I think we'd want
so that we're consistent with
One challenge with that right now is that we have NEITHER
LogRecord
norReadableLogRecord
interfaces. For consistency, we would needLogRecord
to live in theapi
module and provide the "write-only" methods andReadableLogRecord
would live in the sdk and provide the getters.Do we think the the expansion of the logging api is within scope for 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.
We want to be symmetric to the extent it makes sense. Spans are mutable onStart, and read only onEnd, hence the ReadableLogRecord, ReadWriteLogRecord. The separation of start and end also causes spans to have a dedicated API in
Span
. These concepts are relevant for logs: Logs constructed and emitted in an atomic operation so no need for aLogRecord
interface analogous toSpan
. And there is only one processor callback which is able to read and write, so no need for aReadableLogRecord
analogous toReadableSpan
.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 you missed my question, which was "Do we think the expansion of the logging api is within scope for this effort?". This effort being: Adding the ability to get data out of
ReadWriteLogRecord
without having to convert viatoLogRecordData()
. I ask this because it appears that we would need to extend the logging api with new interfaces in order to makeReadWriteLogRecord
consistent withReadWriteSpan
.Right. Sure. I can buy into that idea...but I'm kinda surprised that you started discussing start/end/lifecycle and processor callback concerns as part of this. In this PR I'm only concerned with the data access aspects of these interfaces, and not the lifecycle nor action methods. I think it's super weird that you suggest that
ReadableSpan
exists only in service ofSpanProcessor
....let's not redefine "what it is" with "how it is used". It's right there in the name -- aReadableSpan
is a span whose internal data/state can be read. That's all.I'm open to this, but I think it requires adding interfaces in the logging api module. Do you want that as part of this PR?
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.
No. I think for the purposes of #6895 we want
ReadWriteLogRecord
to have the capabilities ofReadWriteSpan
for reading data without a symmetric interface hierarchy.The lifecycle aspects matter because the design of the span interfaces ReadWriteSpan / ReadableSpan / Span directly fall out of the SpanProcessor callbacks and the Span API requirements. Need to compare the LogRecordProcessor callbacks and log API to understand where symmetry makes sense and does not.
The only place ReadableSpan occurs in the SDK's API is in SpanProcessor#onEnd. If SpanProcessor didn't have a requirement for a read-only span onEnd, ReadableSpan wouldn't exist. And so we named it something appropriate reflecting "what it is", but we can't loose sight of "why it exists". I don't think it makes sense to invent interfaces that don't serve any expressed purpose in the API.
One additional comment on the original proposal: we can't have
ReadWriteLogRecord
extendLogRecordData
becauseLogRecordData
is annotated@Immutable
, andReadWriteLogRecord
violates this.