-
Notifications
You must be signed in to change notification settings - Fork 772
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
[sdk-1.5.0-hotfix] Fix LogRecord.State being null when TState matches known interfaces #4609
[sdk-1.5.0-hotfix] Fix LogRecord.State being null when TState matches known interfaces #4609
Conversation
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main-1.5.0 #4609 +/- ##
===========================================
Coverage 85.57% 85.57%
===========================================
Files 320 320
Lines 12615 12620 +5
===========================================
+ Hits 10795 10800 +5
Misses 1820 1820
|
bool parseStateValues) | ||
{ | ||
iLoggerData.State = null; | ||
if (!includeAttributes) |
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.
Add check for state == null
here and remove the else if
condition here?
if (!includeAttributes) | |
if (!includeAttributes || state is 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.
I think the else if could only be removed if we also take your suggestion:
How about we also remove the support for parsing custom typed objects as well in 1.5.1? This PR #4560 is doing that anyway?
Right?
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.
My thinking here was most states will not be null
so better to not spend cycles checking for uncommon case until it is needed. But I moved it up and rewrote the check so that the JIT should elide it for structs completely so there shouldn't be any impact.
How about we also remove the support for parsing custom typed objects as well in 1.5.1? This PR #4560 is doing that anyway? |
I think for a hotfix we should keep it small and focused. But if you want to also do that in there we can do on a follow-up. |
Also agree should be a separate PR. Though, is there a strong reason #4560 should ship in 1.5.1? |
I agree to that as well.
I think it's better to treat that as an unwanted change (bug?) added in 1.5.0 and remove it for 1.5.1 instead of making a breaking change in 1.6.0. |
👍 I support this reasoning. |
+1 Good judgement! |
test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs
Show resolved
Hide resolved
I just pushed a change...
It seems the compiler is not smart enough to know when it has boxed some struct. The code was flowing like this: logRecord.State = state; // boxing 1
if (state is IReadOnlyList<KeyValuePair<string, object?>> stateList)) // boxing 2 This PR makes the memory perf twice as bad and the CPU has to do more work 🤮 I changed it to do this... if (state is IReadOnlyList<KeyValuePair<string, object?>> stateList))
{
logRecord.State = stateList; // reuse boxing of state to IReadOnlyList
}
else
{
logRecord.State = state; // box directly if all else failed
} It is more code but the perf justifies it IMO. Benchmarks[MemoryDiagnoser]
public class BoxingBenchmarks
{
[Benchmark]
public (object State, int Count) AssignToObjectFirstAndThenCastAsInterface()
{
var data = default(TestStruct);
var state = (object)data;
if (data is IReadOnlyList<KeyValuePair<string, object>> list)
{
return (state, list.Count);
}
return (state, -1);
}
[Benchmark]
public (object State, int Count) CastAsInterfaceAndThenAssignAsState()
{
var data = default(TestStruct);
if (data is IReadOnlyList<KeyValuePair<string, object>> list)
{
return (list, list.Count);
}
return ((object)data, -1);
}
public struct TestStruct : IReadOnlyList<KeyValuePair<string, object>>
{
public int IntValue;
public double DoubleValue;
public object RefValue;
public readonly int Count => 0;
public readonly KeyValuePair<string, object> this[int index] => throw new NotImplementedException();
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
}
|
Note: This PR is targeting main-1.5.0 branch.
Changes
LogRecord.State
handling.Details
1.5.0 tweaked
OpenTelemetryLoggerOptions.ParseStateValues
so that if loggedTState
isIReadOnlyList
orIEnumerable
ofKeyValuePair<string, object>
we automatically setLogRecord.Attributes
\LogRecord.StateValues
and leaveLogRecord.State
=null
. The idea with that is exporters were coded like this...What was reported is an exporter doing this...
Which throws a NRE with the new logic.
In order to preserve the above logic this PR puts back handling of
LogRecord.State
so that it is always set so long asOpenTelemetryLoggerOptions.ParseStateValues == false
(the previous behavior).Merge requirement checklist
CHANGELOG.md
files updated for non-trivial changes