-
Notifications
You must be signed in to change notification settings - Fork 583
fix: Ensure InstrumentationScope is properly propagated. #3175
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
base: main
Are you sure you want to change the base?
fix: Ensure InstrumentationScope is properly propagated. #3175
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3175 +/- ##
=====================================
Coverage 80.6% 80.7%
=====================================
Files 126 126
Lines 22331 22424 +93
=====================================
+ Hits 18019 18112 +93
Misses 4312 4312 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
5e7f8da
to
ebe6e6e
Compare
With the current implementation, there is no way for an InstrumentationScope to export its version and attributes correctly. The reason why this is happening is because when the InstrumentationScope is exported, the target key is always `Some`. This is because when the code creates the `scope_map`, the target key always has a value, either comming from the log record's target, or from the name of the instrumentation scope. That means that when the code calls `InstrumentationScope::from((&InstrumentationScope, Option<Cow<'static, str>>))`, the option is always `Some`. This causes the new InstrumentationScope to always from version and attributes from the incoming scope. This change assumes that if the target and the instrumentation scope name match, the user wants to preserve the version and attributes of the incoming scope. There are other alternatives implementations that check whether the log record target is None that could also be valid, but I think both would have the same outcome.
ebe6e6e
to
c5cd7a9
Compare
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.
Pull Request Overview
This PR fixes an issue where InstrumentationScope version and attributes were not being properly propagated during log export. The problem was that the target key in the scope_map always had a value, causing the InstrumentationScope to lose its original metadata when exported.
- Extracts scope creation logic into a dedicated
new_instrumentation_scope
function with conditional logic - Preserves original scope attributes and version when the target matches the scope name
- Adds comprehensive test coverage for different scenarios of scope attribute preservation
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
let logs = [(&log_record1, &instrum_lib1), (&log_record2, &instrum_lib2)]; | ||
let log_batch = LogBatch::new(&logs); | ||
let resource: ResourceAttributesWithSchema = (&resource).into(); // Convert Resource to ResourceAttributesWithSchema | ||
let grouped_logs = | ||
crate::transform::logs::tonic::group_logs_by_resource_and_scope(log_batch, &resource); | ||
|
||
// This makes the grouping to match the existent InstrumentationScope, preserving the scope attributes. | ||
log_record1.set_target("lib1"); | ||
log_record2.set_target("lib2"); | ||
|
Copilot
AI
Oct 2, 2025
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 log record targets are being set after the log batch is created and grouped. This means the test is not actually testing the scenario described in the comment. Move these lines before creating the log batch to test the intended behavior.
let logs = [(&log_record1, &instrum_lib1), (&log_record2, &instrum_lib2)]; | |
let log_batch = LogBatch::new(&logs); | |
let resource: ResourceAttributesWithSchema = (&resource).into(); // Convert Resource to ResourceAttributesWithSchema | |
let grouped_logs = | |
crate::transform::logs::tonic::group_logs_by_resource_and_scope(log_batch, &resource); | |
// This makes the grouping to match the existent InstrumentationScope, preserving the scope attributes. | |
log_record1.set_target("lib1"); | |
log_record2.set_target("lib2"); | |
// This makes the grouping to match the existent InstrumentationScope, preserving the scope attributes. | |
log_record1.set_target("lib1"); | |
log_record2.set_target("lib2"); | |
let logs = [(&log_record1, &instrum_lib1), (&log_record2, &instrum_lib2)]; | |
let log_batch = LogBatch::new(&logs); | |
let resource: ResourceAttributesWithSchema = (&resource).into(); // Convert Resource to ResourceAttributesWithSchema | |
let grouped_logs = | |
crate::transform::logs::tonic::group_logs_by_resource_and_scope(log_batch, &resource); |
Copilot uses AI. Check for mistakes.
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.
Might be missing something, but I think copilot is right on this one - its not clear to me why the target is set after the call to group_logs_by_resource_and_scope
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.
Hey @calavera thanks for opening this, and sorry for the slow turn-around here!
Some comments inline; lmk what you think.
/// | ||
/// 1. If the log scope name matches the key, return a new scope with all the fields from the original scope. | ||
/// 2. Otherwise, return a new scope with the key as the name of the scope, and the rest of the fields empty. | ||
fn new_instrumentation_scope( |
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.
Did you consider pushing this down into InstrumentationScope::from
? I've not dug into it in depth but it looks like the only place it is being used is the code you're touching, and i'm wondering if all in cases, the callers are going to expect this behaviour anyway.
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 also think it would be great to document, somewhere, exactly how target relates to instrumentation scope. It took me a while to grok what's happening here:
This could be on the fn that does applies the relevant logic. Perhaps something like (overly detailed description to make sure i've not missed something 😄 ):
When you explicitly specify target in a log (e.g., error!(target: "security.audit", ...)), it overrides the default module path and becomes the InstrumentationScope.name
when the log is exported to OpenTelemetry backends.
If the target differs from the InstrumentationScope name, the exported scope will contain only that target
string as the name, with empty version and attributes.
This allows you to logically group logs under custom identifiers (like "postgres", "authentication", etc.) rather
than module paths, but at the cost of losing the instrumentation library's metadata.
let logs = [(&log_record1, &instrum_lib1), (&log_record2, &instrum_lib2)]; | ||
let log_batch = LogBatch::new(&logs); | ||
let resource: ResourceAttributesWithSchema = (&resource).into(); // Convert Resource to ResourceAttributesWithSchema | ||
let grouped_logs = | ||
crate::transform::logs::tonic::group_logs_by_resource_and_scope(log_batch, &resource); | ||
|
||
// This makes the grouping to match the existent InstrumentationScope, preserving the scope attributes. | ||
log_record1.set_target("lib1"); | ||
log_record2.set_target("lib2"); | ||
|
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.
Might be missing something, but I think copilot is right on this one - its not clear to me why the target is set after the call to group_logs_by_resource_and_scope
Changes
With the current implementation, there is no way for an InstrumentationScope to export its version and attributes correctly.
The reason why this is happening is because when the InstrumentationScope is exported, the target key is always
Some
. This is because when the code creates thescope_map
, the target key always has a value, either comming from the log record's target, or from the name of the instrumentation scope.That means that when the code calls
InstrumentationScope::from((&InstrumentationScope, Option<Cow<'static, str>>))
, the option is alwaysSome
. This causes the new InstrumentationScope to always from version and attributes from the incoming scope.This change assumes that if the target and the instrumentation scope name match, the user wants to preserve the version and attributes of the incoming scope.
There are other alternatives implementations that check whether the log record target is None that could also be valid, but I think both would have the same outcome.
Please provide a brief description of the changes here.
Merge requirement checklist
CHANGELOG.md
files updated for non-trivial, user-facing changes