Skip to content
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

[otlp] Add log exception attributes under feature flag #4892

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
and `OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT`.
([#4887](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4887))

* Added ability to export attributes corresponding to `LogRecord.Exception` i.e.
`exception.type`, `exception.message` and `exception.stacktrace`. These
attributes will be exported when
`OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES` environment
variable will be set to `true`.
([#4892](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4892))

## 1.6.0

Released 2023-Sep-05
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// <copyright file="ExperimentalOptions.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

#nullable enable

using Microsoft.Extensions.Configuration;
using OpenTelemetry.Internal;

namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation;

internal sealed class ExperimentalOptions
{
public const string EMITLOGEXCEPTIONATTRIBUTES = "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES";

public ExperimentalOptions()
: this(new ConfigurationBuilder().AddEnvironmentVariables().Build())
{
}

public ExperimentalOptions(IConfiguration configuration)
{
if (configuration.TryGetBoolValue(EMITLOGEXCEPTIONATTRIBUTES, out var emitLogExceptionAttributes))
{
this.EmitLogExceptionAttributes = emitLogExceptionAttributes;
}
}

/// <summary>
/// Gets or sets a value indicating whether log exception attributes should be exported.
/// </summary>
public bool EmitLogExceptionAttributes { get; set; } = false;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="LogRecordExtensions.cs" company="OpenTelemetry Authors">
// <copyright file="OtlpLogRecordTransformer.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -16,22 +16,33 @@

using System.Runtime.CompilerServices;
using Google.Protobuf;
using OpenTelemetry.Internal;
using OpenTelemetry.Logs;
using OpenTelemetry.Trace;
using OtlpCollector = OpenTelemetry.Proto.Collector.Logs.V1;
using OtlpCommon = OpenTelemetry.Proto.Common.V1;
using OtlpLogs = OpenTelemetry.Proto.Logs.V1;
using OtlpResource = OpenTelemetry.Proto.Resource.V1;

namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation;

internal static class LogRecordExtensions
internal sealed class OtlpLogRecordTransformer
{
internal static void AddBatch(
this OtlpCollector.ExportLogsServiceRequest request,
SdkLimitOptions sdkLimitOptions,
private readonly SdkLimitOptions sdkLimitOptions;
private readonly ExperimentalOptions experimentalOptions;

public OtlpLogRecordTransformer(SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions)
{
this.sdkLimitOptions = sdkLimitOptions;
this.experimentalOptions = experimentalOptions;
}

internal OtlpCollector.ExportLogsServiceRequest BuildExportRequest(
OtlpResource.Resource processResource,
in Batch<LogRecord> logRecordBatch)
{
var request = new OtlpCollector.ExportLogsServiceRequest();

var resourceLogs = new OtlpLogs.ResourceLogs
{
Resource = processResource,
Expand All @@ -43,16 +54,18 @@ internal static void AddBatch(

foreach (var logRecord in logRecordBatch)
{
var otlpLogRecord = logRecord.ToOtlpLog(sdkLimitOptions);
var otlpLogRecord = this.ToOtlpLog(logRecord);
if (otlpLogRecord != null)
{
scopeLogs.LogRecords.Add(otlpLogRecord);
}
}

return request;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static OtlpLogs.LogRecord ToOtlpLog(this LogRecord logRecord, SdkLimitOptions sdkLimitOptions)
internal OtlpLogs.LogRecord ToOtlpLog(LogRecord logRecord)
{
OtlpLogs.LogRecord otlpLogRecord = null;

Expand All @@ -75,8 +88,8 @@ internal static OtlpLogs.LogRecord ToOtlpLog(this LogRecord logRecord, SdkLimitO
otlpLogRecord.SeverityText = logRecord.Severity.Value.ToShortName();
}

var attributeValueLengthLimit = sdkLimitOptions.LogRecordAttributeValueLengthLimit;
var attributeCountLimit = sdkLimitOptions.LogRecordAttributeCountLimit ?? int.MaxValue;
var attributeValueLengthLimit = this.sdkLimitOptions.LogRecordAttributeValueLengthLimit;
var attributeCountLimit = this.sdkLimitOptions.LogRecordAttributeCountLimit ?? int.MaxValue;

/*
// Removing this temporarily for stable release
Expand Down Expand Up @@ -104,14 +117,14 @@ internal static OtlpLogs.LogRecord ToOtlpLog(this LogRecord logRecord, SdkLimitO
{
otlpLogRecord.AddStringAttribute(nameof(logRecord.EventId.Name), logRecord.EventId.Name, attributeValueLengthLimit, attributeCountLimit);
}
*/

if (logRecord.Exception != null)
if (this.experimentalOptions.EmitLogExceptionAttributes && logRecord.Exception != null)
{
otlpLogRecord.AddStringAttribute(SemanticConventions.AttributeExceptionType, logRecord.Exception.GetType().Name, attributeValueLengthLimit, attributeCountLimit);
otlpLogRecord.AddStringAttribute(SemanticConventions.AttributeExceptionMessage, logRecord.Exception.Message, attributeValueLengthLimit, attributeCountLimit);
otlpLogRecord.AddStringAttribute(SemanticConventions.AttributeExceptionStacktrace, logRecord.Exception.ToInvariantString(), attributeValueLengthLimit, attributeCountLimit);
AddStringAttribute(otlpLogRecord, SemanticConventions.AttributeExceptionType, logRecord.Exception.GetType().Name, attributeValueLengthLimit, attributeCountLimit);
AddStringAttribute(otlpLogRecord, SemanticConventions.AttributeExceptionMessage, logRecord.Exception.Message, attributeValueLengthLimit, attributeCountLimit);
AddStringAttribute(otlpLogRecord, SemanticConventions.AttributeExceptionStacktrace, logRecord.Exception.ToInvariantString(), attributeValueLengthLimit, attributeCountLimit);
}
*/

bool bodyPopulatedFromFormattedMessage = false;
if (logRecord.FormattedMessage != null)
Expand All @@ -133,7 +146,7 @@ internal static OtlpLogs.LogRecord ToOtlpLog(this LogRecord logRecord, SdkLimitO
}
else if (OtlpKeyValueTransformer.Instance.TryTransformTag(attribute, out var result, attributeValueLengthLimit))
{
otlpLogRecord.AddAttribute(result, attributeCountLimit);
AddAttribute(otlpLogRecord, result, attributeCountLimit);
}
}
}
Expand Down Expand Up @@ -183,7 +196,7 @@ void ProcessScope(LogRecordScope scope, OtlpLogs.LogRecord otlpLog)
{
if (OtlpKeyValueTransformer.Instance.TryTransformTag(scopeItem, out var result, attributeValueLengthLimit))
{
otlpLog.AddAttribute(result, attributeCountLimit);
AddAttribute(otlpLog, result, attributeCountLimit);
}
}
}
Expand All @@ -198,7 +211,7 @@ void ProcessScope(LogRecordScope scope, OtlpLogs.LogRecord otlpLog)
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AddAttribute(this OtlpLogs.LogRecord logRecord, OtlpCommon.KeyValue attribute, int maxAttributeCount)
private static void AddAttribute(OtlpLogs.LogRecord logRecord, OtlpCommon.KeyValue attribute, int maxAttributeCount)
{
if (logRecord.Attributes.Count < maxAttributeCount)
{
Expand All @@ -211,22 +224,22 @@ private static void AddAttribute(this OtlpLogs.LogRecord logRecord, OtlpCommon.K
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AddStringAttribute(this OtlpLogs.LogRecord logRecord, string key, string value, int? maxValueLength, int maxAttributeCount)
private static void AddStringAttribute(OtlpLogs.LogRecord logRecord, string key, string value, int? maxValueLength, int maxAttributeCount)
{
var attributeItem = new KeyValuePair<string, object>(key, value);
if (OtlpKeyValueTransformer.Instance.TryTransformTag(attributeItem, out var result, maxValueLength))
{
logRecord.AddAttribute(result, maxAttributeCount);
AddAttribute(logRecord, result, maxAttributeCount);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AddIntAttribute(this OtlpLogs.LogRecord logRecord, string key, int value, int maxAttributeCount)
private static void AddIntAttribute(OtlpLogs.LogRecord logRecord, string key, int value, int maxAttributeCount)
{
var attributeItem = new KeyValuePair<string, object>(key, value);
if (OtlpKeyValueTransformer.Instance.TryTransformTag(attributeItem, out var result))
{
logRecord.AddAttribute(result, maxAttributeCount);
AddAttribute(logRecord, result, maxAttributeCount);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ namespace OpenTelemetry.Exporter;
/// </summary>
internal sealed class OtlpLogExporter : BaseExporter<LogRecord>
{
private readonly SdkLimitOptions sdkLimitOptions;
private readonly IExportClient<OtlpCollector.ExportLogsServiceRequest> exportClient;
private readonly OtlpLogRecordTransformer otlpLogRecordTransformer;

private OtlpResource.Resource processResource;

Expand All @@ -58,8 +58,6 @@ internal OtlpLogExporter(
Debug.Assert(exporterOptions != null, "exporterOptions was null");
Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null");

this.sdkLimitOptions = sdkLimitOptions;

// Each of the Otlp exporters: Traces, Metrics, and Logs set the same value for `OtlpKeyValueTransformer.LogUnsupportedAttributeType`
// and `ConfigurationExtensions.LogInvalidEnvironmentVariable` so it should be fine even if these exporters are used together.
OtlpKeyValueTransformer.LogUnsupportedAttributeType = (string tagValueType, string tagKey) =>
Expand All @@ -80,6 +78,8 @@ internal OtlpLogExporter(
{
this.exportClient = exporterOptions.GetLogExportClient();
}

this.otlpLogRecordTransformer = new OtlpLogRecordTransformer(sdkLimitOptions, new());
}

internal OtlpResource.Resource ProcessResource => this.processResource ??= this.ParentProvider.GetResource().ToOtlpResource();
Expand All @@ -90,11 +90,9 @@ public override ExportResult Export(in Batch<LogRecord> logRecordBatch)
// Prevents the exporter's gRPC and HTTP operations from being instrumented.
using var scope = SuppressInstrumentationScope.Begin();

var request = new OtlpCollector.ExportLogsServiceRequest();

try
{
request.AddBatch(this.sdkLimitOptions, this.ProcessResource, logRecordBatch);
var request = this.otlpLogRecordTransformer.BuildExportRequest(this.ProcessResource, logRecordBatch);

if (!this.exportClient.SendExportRequest(request))
{
Expand Down
11 changes: 11 additions & 0 deletions src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,17 @@ values of the log record limits
* `OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT`
* `OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT`

## Environment Variables for Experimental Features

### Otlp Log Exporter

* `OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES`

When set to `true`, it enables export of attributes corresponding to
`LogRecord.Exception`. The attributes `exception.type`, `exception.message` and
`exception.stacktrace` are defined in
[specification](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/exceptions/exceptions-logs.md#attributes).

## Configure HttpClient

The `HttpClientFactory` option is provided on `OtlpExporterOptions` for users
Expand Down
Loading