Skip to content

Commit

Permalink
[otlp] Add log exception attributes under feature flag (#4892)
Browse files Browse the repository at this point in the history
Co-authored-by: Cijo Thomas <cijo.thomas@gmail.com>
  • Loading branch information
vishweshbankwar and cijothomas authored Oct 3, 2023
1 parent b0038ae commit c18ff65
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 64 deletions.
7 changes: 7 additions & 0 deletions src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
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

0 comments on commit c18ff65

Please sign in to comment.