diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index bdb8aeb04a8..f20beb26dba 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -2,6 +2,14 @@ ## Unreleased +* **Breaking Change** Removed support for parsing `TState` types passed to the + `ILogger.Log` API when `ParseStateValues` is true and `TState` does + not implement either `IReadOnlyList>` or + `IEnumerable>`. This was done to make logging AOT + compatible. This feature was first introduced in `1.5.0` stable release with + [#4334](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4334). + ([#4560](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4560)) + * Add back support for Exemplars. See [exemplars](../../docs/metrics/customizing-the-sdk/README.md#exemplars) for instructions to enable exemplars. ([#4553](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4553)) diff --git a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs index 02bdaf69957..a9f5d4a38e2 100644 --- a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs +++ b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs @@ -141,20 +141,20 @@ public void DroppedExportProcessorItems(string exportProcessorName, string expor } [NonEvent] - public void LoggerParseStateException(Exception exception) + public void LoggerProviderException(string methodName, Exception ex) { - if (this.IsEnabled(EventLevel.Warning, EventKeywords.All)) + if (this.IsEnabled(EventLevel.Error, EventKeywords.All)) { - this.LoggerParseStateException(typeof(TState).FullName!, exception.ToInvariantString()); + this.LoggerProviderException(methodName, ex.ToInvariantString()); } } [NonEvent] - public void LoggerProviderException(string methodName, Exception ex) + public void LoggerProcessStateSkipped() { - if (this.IsEnabled(EventLevel.Error, EventKeywords.All)) + if (this.IsEnabled(EventLevel.Warning, EventKeywords.All)) { - this.LoggerProviderException(methodName, ex.ToInvariantString()); + this.LoggerProcessStateSkipped(typeof(TState).FullName!, "This can be fixed by updating the state to be a type that implements either IReadOnlyList> or IEnumerable>."); } } @@ -301,12 +301,6 @@ public void InvalidEnvironmentVariable(string key, string? value) this.WriteEvent(47, key, value); } - [Event(48, Message = "Exception thrown parsing log state of type '{0}'. Exception: '{1}'", Level = EventLevel.Warning)] - public void LoggerParseStateException(string type, string error) - { - this.WriteEvent(48, type, error); - } - [Event(49, Message = "LoggerProviderSdk event: '{0}'", Level = EventLevel.Verbose)] public void LoggerProviderSdkEvent(string message) { @@ -319,6 +313,12 @@ public void LoggerProviderException(string methodName, string ex) this.WriteEvent(50, methodName, ex); } + [Event(51, Message = "Skip processing log state of type '{0}' because it does not implement either IReadOnlyList> or IEnumerable>. Suggested action: '{1}'.", Level = EventLevel.Warning)] + public void LoggerProcessStateSkipped(string type, string fix) + { + this.WriteEvent(51, type, fix); + } + #if DEBUG public class OpenTelemetryEventListener : EventListener { diff --git a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs index 732795729eb..69ce780b492 100644 --- a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs +++ b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs @@ -16,7 +16,6 @@ #nullable enable -using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -194,36 +193,8 @@ internal static void SetLogRecordSeverityFields(ref LogRecordData logRecordData, } else { - try - { - PropertyDescriptorCollection itemProperties = TypeDescriptor.GetProperties(state); - - var attributeStorage = logRecord.AttributeStorage ??= new List>(itemProperties.Count); - - foreach (PropertyDescriptor? itemProperty in itemProperties) - { - if (itemProperty == null) - { - continue; - } - - object? value = itemProperty.GetValue(state); - if (value == null) - { - continue; - } - - attributeStorage.Add(new KeyValuePair(itemProperty.Name, value)); - } - - return attributeStorage; - } - catch (Exception parseException) - { - OpenTelemetrySdkEventSource.Log.LoggerParseStateException(parseException); - - return Array.Empty>(); - } + OpenTelemetrySdkEventSource.Log.LoggerProcessStateSkipped(); + return Array.Empty>(); } } diff --git a/test/OpenTelemetry.AotCompatibility.Tests/AotCompatibilityTests.cs b/test/OpenTelemetry.AotCompatibility.Tests/AotCompatibilityTests.cs index 4c44b738444..248494307bb 100644 --- a/test/OpenTelemetry.AotCompatibility.Tests/AotCompatibilityTests.cs +++ b/test/OpenTelemetry.AotCompatibility.Tests/AotCompatibilityTests.cs @@ -85,7 +85,7 @@ public void EnsureAotCompatibility() Assert.True(process.ExitCode == 0, "Publishing the AotCompatibility app failed. See test output for more details."); var warnings = expectedOutput.ToString().Split('\n', '\r').Where(line => line.Contains("warning IL")); - Assert.Equal(37, warnings.Count()); + Assert.Equal(36, warnings.Count()); } } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs index cdd73675b9c..a3aa0e88c96 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs @@ -101,7 +101,6 @@ public void AddOtlpLogExporterParseStateValueCanBeTurnedOff(bool parseState) { Assert.Null(logRecord.State); Assert.NotNull(logRecord.Attributes); - Assert.Contains(logRecord.Attributes, kvp => kvp.Key == "propertyA" && (string)kvp.Value == "valueA"); } else { @@ -141,7 +140,6 @@ public void AddOtlpLogExporterParseStateValueCanBeTurnedOffHosting(bool parseSta { Assert.Null(logRecord.State); Assert.NotNull(logRecord.Attributes); - Assert.Contains(logRecord.Attributes, kvp => kvp.Key == "propertyA" && (string)kvp.Value == "valueA"); } else { diff --git a/test/OpenTelemetry.Tests/Logs/LogRecordTest.cs b/test/OpenTelemetry.Tests/Logs/LogRecordTest.cs index f9f6e6cb038..81aff45f4ac 100644 --- a/test/OpenTelemetry.Tests/Logs/LogRecordTest.cs +++ b/test/OpenTelemetry.Tests/Logs/LogRecordTest.cs @@ -804,37 +804,6 @@ public void ParseStateValuesUsingIEnumerableTest() Assert.Equal(new KeyValuePair("Key1", "Value1"), logRecord.StateValues[0]); } - [Fact] - public void ParseStateValuesUsingCustomTest() - { - using var loggerFactory = InitializeLoggerFactory(out List exportedItems, configure: options => options.ParseStateValues = true); - var logger = loggerFactory.CreateLogger(); - - // Tests unknown state parse path. - - CustomState state = new CustomState - { - Property = "Value", - }; - - logger.Log( - LogLevel.Information, - 0, - state, - null, - (s, e) => "OpenTelemetry!"); - var logRecord = exportedItems[0]; - - Assert.Null(logRecord.State); - Assert.NotNull(logRecord.StateValues); - Assert.Equal(1, logRecord.StateValues.Count); - - KeyValuePair actualState = logRecord.StateValues[0]; - - Assert.Equal("Property", actualState.Key); - Assert.Equal("Value", actualState.Value); - } - [Fact] public void DisposingStateTest() {