Skip to content

Commit

Permalink
Add ability to export attributes corresponding to Logrecord.EventId (
Browse files Browse the repository at this point in the history
  • Loading branch information
vishweshbankwar authored Oct 16, 2023
1 parent 037cda8 commit 33fdd47
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 86 deletions.
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

0 comments on commit 33fdd47

Please sign in to comment.