[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.
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.
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.
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 2This 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.Statehandling.Details
1.5.0 tweaked
OpenTelemetryLoggerOptions.ParseStateValuesso that if loggedTStateisIReadOnlyListorIEnumerableofKeyValuePair<string, object>we automatically setLogRecord.Attributes\LogRecord.StateValuesand 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.Stateso that it is always set so long asOpenTelemetryLoggerOptions.ParseStateValues == false(the previous behavior).Merge requirement checklist
CHANGELOG.mdfiles updated for non-trivial changes