Skip to content
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
16 changes: 15 additions & 1 deletion src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,21 @@
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))

**NOTE**: These attributes were removed in [1.6.0-rc.1](#160-rc1) release in
order to support stable release of OTLP Log Exporter. The attributes will now be
available via environment variable mentioned above.
([#4892](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4892))

* Added ability to export attributes corresponding to `LogRecord.EventId.Id` as
`logrecord.event.id` and `LogRecord.EventId.Name` as `logrecord.event.name`. The
attributes will be exported when
`OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES` will be set to `true`.

**NOTE**: These attributes were removed in [1.6.0-rc.1](#160-rc1) release in
order to support stable release of OTLP Log Exporter. The attributes will now
be available via environment variable mentioned above.
([#4925](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4925))

* `LogRecord.CategoryName` will now be exported as
[InstrumentationScope](https://github.com/open-telemetry/opentelemetry-dotnet/blob/3c2bb7c93dd2e697636479a1882f49bb0c4a362e/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/common/v1/common.proto#L71-L81)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation;

internal sealed class ExperimentalOptions
{
public const string EMITLOGEXCEPTIONATTRIBUTES = "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES";
public const string LogRecordEventIdAttribute = "logrecord.event.id";

public const string LogRecordEventNameAttribute = "logrecord.event.name";

public const string EmitLogExceptionEnvVar = "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES";

public const string EmitLogEventEnvVar = "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES";

public ExperimentalOptions()
: this(new ConfigurationBuilder().AddEnvironmentVariables().Build())
Expand All @@ -32,14 +38,24 @@ public ExperimentalOptions()

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

if (configuration.TryGetBoolValue(EmitLogEventEnvVar, out var emitLogEventAttributes))
{
this.EmitLogEventAttributes = emitLogEventAttributes;
}
}

/// <summary>
/// Gets or sets a value indicating whether log exception attributes should be exported.
/// </summary>
public bool EmitLogExceptionAttributes { get; set; } = false;

/// <summary>
/// Gets or sets a value indicating whether log event attributes should be exported.
/// </summary>
public bool EmitLogEventAttributes { get; set; } = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,33 +140,18 @@ internal OtlpLogs.LogRecord ToOtlpLog(LogRecord logRecord)
var attributeValueLengthLimit = this.sdkLimitOptions.LogRecordAttributeValueLengthLimit;
var attributeCountLimit = this.sdkLimitOptions.LogRecordAttributeCountLimit ?? int.MaxValue;

/*
// Removing this temporarily for stable release
// https://github.com/open-telemetry/opentelemetry-dotnet/issues/4776
// https://github.com/open-telemetry/opentelemetry-dotnet/issues/3491
// First add the generic attributes like Category, EventId and Exception,
// so they are less likely being dropped because of AttributeCountLimit.

if (!string.IsNullOrEmpty(logRecord.CategoryName))
if (this.experimentalOptions.EmitLogEventAttributes)
{
// TODO:
// 1. Track the following issue, and map CategoryName to Name
// if it makes it to log data model.
// https://github.com/open-telemetry/opentelemetry-specification/issues/2398
// 2. Confirm if this name for attribute is good.
otlpLogRecord.AddStringAttribute("dotnet.ilogger.category", logRecord.CategoryName, attributeValueLengthLimit, attributeCountLimit);
}

if (logRecord.EventId.Id != default)
{
otlpLogRecord.AddIntAttribute(nameof(logRecord.EventId.Id), logRecord.EventId.Id, attributeCountLimit);
}
if (logRecord.EventId.Id != default)
{
AddIntAttribute(otlpLogRecord, ExperimentalOptions.LogRecordEventIdAttribute, logRecord.EventId.Id, attributeCountLimit);
}

if (!string.IsNullOrEmpty(logRecord.EventId.Name))
{
otlpLogRecord.AddStringAttribute(nameof(logRecord.EventId.Name), logRecord.EventId.Name, attributeValueLengthLimit, attributeCountLimit);
if (!string.IsNullOrEmpty(logRecord.EventId.Name))
{
AddStringAttribute(otlpLogRecord, ExperimentalOptions.LogRecordEventNameAttribute, logRecord.EventId.Name, attributeValueLengthLimit, attributeCountLimit);
}
}
*/

if (this.experimentalOptions.EmitLogExceptionAttributes && logRecord.Exception != null)
{
Expand Down
5 changes: 5 additions & 0 deletions src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,11 @@ When set to `true`, it enables export of attributes corresponding to
`exception.stacktrace` are defined in
[specification](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/exceptions/exceptions-logs.md#attributes).

* `OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_CATEGORY_EVENT_ATTRIBUTES`

When set to `true`, it enables export of `LogRecord.EventId.Id` as
`logrecord.event.id` and `LogRecord.EventId.Name` to `logrecord.event.name`.

## Configure HttpClient

The `HttpClientFactory` option is provided on `OtlpExporterOptions` for users
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,6 @@ public void OtlpLogRecordTestWhenStateValuesArePopulated()
var index = 0;
var attribute = otlpLogRecord.Attributes[index];

/*
Assert.Equal("dotnet.ilogger.category", attribute.Key);
Assert.Equal("OtlpLogExporterTests", attribute.Value.StringValue);
attribute = otlpLogRecord.Attributes[++index];
*/

Assert.Equal("name", attribute.Key);
Assert.Equal("tomato", attribute.Value.StringValue);

Expand All @@ -210,45 +204,11 @@ public void OtlpLogRecordTestWhenStateValuesArePopulated()
Assert.Equal("Hello from {name} {price}.", attribute.Value.StringValue);
}

/*
[Fact]
public void CheckToOtlpLogRecordLoggerCategory()
{
var logRecords = new List<LogRecord>();
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(options =>
{
options.AddInMemoryExporter(logRecords);
});
});

var logger1 = loggerFactory.CreateLogger("CategoryA");
logger1.LogInformation("Hello");
Assert.Single(logRecords);

var logRecord = logRecords[0];
var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions, new());
Assert.NotNull(otlpLogRecord);
Assert.Single(otlpLogRecord.Attributes);

var attribute = otlpLogRecord.Attributes[0];
Assert.Equal("dotnet.ilogger.category", attribute.Key);
Assert.Equal("CategoryA", attribute.Value.StringValue);

logRecords.Clear();
var logger2 = loggerFactory.CreateLogger(string.Empty);
logger2.LogInformation("Hello");
Assert.Single(logRecords);

logRecord = logRecords[0];
otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions, new());
Assert.NotNull(otlpLogRecord);
Assert.Empty(otlpLogRecord.Attributes);
}

[Fact]
public void CheckToOtlpLogRecordEventId()
[Theory]
[InlineData("true")]
[InlineData("false")]
[InlineData(null)]
public void CheckToOtlpLogRecordEventId(string emitLogEventAttributes)
{
var logRecords = new List<LogRecord>();
using var loggerFactory = LoggerFactory.Create(builder =>
Expand All @@ -265,37 +225,56 @@ public void CheckToOtlpLogRecordEventId()
logger.LogInformation(new EventId(10, null), "Hello from {name} {price}.", "tomato", 2.99);
Assert.Single(logRecords);

var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string> { [ExperimentalOptions.EmitLogEventEnvVar] = emitLogEventAttributes })
.Build();

var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new(configuration));

var logRecord = logRecords[0];
var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions, new());

var otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord);

Assert.NotNull(otlpLogRecord);
Assert.Equal("Hello from tomato 2.99.", otlpLogRecord.Body.StringValue);

var otlpLogRecordAttributes = otlpLogRecord.Attributes.ToString();

// Event
Assert.Contains("Id", otlpLogRecordAttributes);
Assert.Contains("10", otlpLogRecordAttributes);
var otlpLogRecordAttributes = otlpLogRecord.Attributes.ToString();
if (emitLogEventAttributes == "true")
{
Assert.Contains(ExperimentalOptions.LogRecordEventIdAttribute, otlpLogRecordAttributes);
Assert.Contains("10", otlpLogRecordAttributes);
}
else
{
Assert.DoesNotContain(ExperimentalOptions.LogRecordEventIdAttribute, otlpLogRecordAttributes);
}

logRecords.Clear();

logger.LogInformation(new EventId(10, "MyEvent10"), "Hello from {name} {price}.", "tomato", 2.99);
Assert.Single(logRecords);

logRecord = logRecords[0];
otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions, new());
otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord);
Assert.NotNull(otlpLogRecord);
Assert.Equal("Hello from tomato 2.99.", otlpLogRecord.Body.StringValue);

otlpLogRecordAttributes = otlpLogRecord.Attributes.ToString();

// Event
Assert.Contains("Id", otlpLogRecordAttributes);
Assert.Contains("10", otlpLogRecordAttributes);
Assert.Contains("Name", otlpLogRecordAttributes);
Assert.Contains("MyEvent10", otlpLogRecordAttributes);
otlpLogRecordAttributes = otlpLogRecord.Attributes.ToString();
if (emitLogEventAttributes == "true")
{
Assert.Contains(ExperimentalOptions.LogRecordEventIdAttribute, otlpLogRecordAttributes);
Assert.Contains("10", otlpLogRecordAttributes);
Assert.Contains(ExperimentalOptions.LogRecordEventNameAttribute, otlpLogRecordAttributes);
Assert.Contains("MyEvent10", otlpLogRecordAttributes);
}
else
{
Assert.DoesNotContain(ExperimentalOptions.LogRecordEventIdAttribute, otlpLogRecordAttributes);
Assert.DoesNotContain(ExperimentalOptions.LogRecordEventNameAttribute, otlpLogRecordAttributes);
}
}
*/

[Fact]
public void CheckToOtlpLogRecordTimestamps()
Expand Down Expand Up @@ -508,6 +487,7 @@ public void CheckToOtlpLogRecordBodyIsPopulated(bool includeFormattedMessage)
[Theory]
[InlineData("true")]
[InlineData("false")]
[InlineData(null)]
public void CheckToOtlpLogRecordExceptionAttributes(string emitExceptionAttributes)
{
var logRecords = new List<LogRecord>();
Expand All @@ -525,7 +505,7 @@ public void CheckToOtlpLogRecordExceptionAttributes(string emitExceptionAttribut
var logRecord = logRecords[0];
var loggedException = logRecord.Exception;
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string> { [ExperimentalOptions.EMITLOGEXCEPTIONATTRIBUTES] = emitExceptionAttributes })
.AddInMemoryCollection(new Dictionary<string, string> { [ExperimentalOptions.EmitLogExceptionEnvVar] = emitExceptionAttributes })
.Build();

var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new(configuration));
Expand Down