fix: update total_recorded_attributes when LogRecord#attributes= is called - #2241
fix: update total_recorded_attributes when LogRecord#attributes= is called#2241ltickett wants to merge 2 commits into
Conversation
|
|
…alled The attr_accessor-generated attributes= setter reassigned the attributes hash but left @total_recorded_attributes at its initialization value, so to_log_record_data reported a stale count after mutation (e.g. in a custom LogRecordProcessor#on_emit). Replace the generated setter with a custom attributes= that recalculates @total_recorded_attributes and reapplies the configured attribute limits, and reuse it from the constructor.
e8b81f9 to
01c94d6
Compare
|
Looking at this change, it improves the situation however it doesn't appear to address adding/removing attributes which is supported in the spec. Chatting with copilot, it came up with: Added a trackedAttributes class class TrackedAttributes
attr_reader :dropped_count
def initialize(initial = {})
@attributes = initial.dup
@dropped_count = 0
end
def delete(key)
existed = @attributes.key?(key)
result = @attributes.delete(key)
@dropped_count += 1 if existed
result
end
def delete_if(&block)
@attributes.delete_if do |k, v|
should_drop = block.call(k, v)
@dropped_count += 1 if should_drop
should_drop
end
end
# Forward everything else to the underlying hash
def method_missing(name, *args, &block)
@attributes.public_send(name, *args, &block)
end
def respond_to_missing?(name, include_private = false)
@attributes.respond_to?(name, include_private)
end
endAnd we change our logrecord to be class LogRecordData
attr_accessor :attributes
def initialize(...)
@attributes = TrackedAttributes.new({})
end
def dropped_attributes_count
@attributes.dropped_count
end
endThat solution can be scaled to support adding/updating of attributes including having the limits enforced on those additional operations. We can also reuse it for spans etc |
@thompson-tomo, thanks for reviewing this with missing features in mind! This PR does a great job resolving the |
| @resource = resource | ||
| @instrumentation_scope = instrumentation_scope | ||
| @log_record_limits = log_record_limits || LogRecordLimits::DEFAULT | ||
| self.attributes = attributes |
There was a problem hiding this comment.
Ah, interesting! Why go with self.attributes here? Just want to make sure I'm following the flow.
|
Thanks @thompson-tomo - I agree with @kaylareopelle - let's look at this in a follow-up and keep this PR scope small.
@kaylareopelle calling self.attributes = attributes in the constructor (log_record.rb:92) routes through the new setter (log_record.rb:102) so that @total_recorded_attributes calculation and trim_attributes happen in exactly one place. Without it you'd duplicate the count + trim logic in both the constructor and the setter, which is what caused issue #2194 in the first place (setter diverged from constructor). So reusing the setter seems like the logical DRY choice. Wdyt? |
|
I have no problem with doing it in small stages however if we do so, we should avoid saying that this pr fixes the issue when it doesn't and ensure that the bug is kept open. In fact on thinking about it, this solution might lead to scenarios which were previously correct now being wrong. For instance create a log record with 5 attributes, then update the attributes to contain 4 using set. With using total when attributes is set you would no longer know about the dropped attribute. |
@thompson-tomo, I'm not sure I'm following. Could you provide a code snippet that creates this situation or write a test that fails with the current code? |
|
@kaylareopelle Here is the test In effect it is the opposite to the test which now passes |
|
@thompson-tomo - Thank you, I see what you're saying. I think in this case though, shouldn't This would return the expected 3 for Does this logic make sense to you, @ltickett? I'll add a code comment with a way we could adjust things if we think the value should resolve to four in @thompson-tomo's previous comment. |
|
I just realized we've strayed from the spec. Here's the definition for Changing attributes during the life of a Log Record isn't enough to make that attribute "dropped" and change the |
|
I agree in my scenario the total could be considered 4 but it is under defined. Looking at https://opentelemetry.io/docs/specs/otel/logs/sdk/#readwritelogrecord is it even supported to be replacing the entire collection? Would it make more sense to track the dropped count directly that we could track all removals. I have asked questions in the spec channel as area feels under defined. |
|
@thompson-tomo @kaylareopelle did we land on a decision regarding the way forward here please? |
|
Hi @ltickett, apologies for the delayed response. We didn't get any feedback in Slack. I'm going to bring this to the SIG meeting on Tuesday to try to get a consensus and we'll have more for you after that. |
|
During the SIG meeting, it was suggested that I look at other implementations to see if there's a consistent approach for handling this value. After reviewing Python, JS, and Java, I think it's safe to say that
@thompson-tomo, what do you think about the spec interpretation given these examples? |
|
I have no issue with dropped being scoped to counting those dropped due to limits. With that being said, i still don't see this change as resolving the issue and not even progressing it. If we were to instead simply track how many attributes have been dropped due to limits, we can then calculate the total. Some test cases i can think of would be:
If we only use the size prior to last limiting we would end up with 1. |
Closes #2194
The attr_accessor-generated attributes= setter reassigned the attributes hash but left @total_recorded_attributes at its initialization value, so to_log_record_data reported a stale count after mutation (e.g. in a custom LogRecordProcessor#on_emit).
Replace the generated setter with a custom attributes= that recalculates @total_recorded_attributes and reapplies the configured attribute limits, and reuse it from the constructor.