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

Add ability to export attributes corresponding to Logrecord.EventId #4925

Merged
Show file tree
Hide file tree
Changes from 9 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 @@ -19,6 +19,13 @@ attributes will be exported when
variable will be set to `true`.
([#4892](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4892))

* Added ability to export attributes corresponding to `LogRecord.EventId` as
vishweshbankwar marked this conversation as resolved.
Show resolved Hide resolved
`event.id` and `event.name` and `LogRecord.CategoryName` as
`dotnet.ilogger.category`. These attributes will be exported when
`OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_CATEGORY_AND_EVENT_LOG_ATTRIBUTES` will be
set to `true`.
([#4925](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4925))
vishweshbankwar marked this conversation as resolved.
Show resolved Hide resolved

## 1.6.0

Released 2023-Sep-05
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ internal sealed class ExperimentalOptions
{
public const string EMITLOGEXCEPTIONATTRIBUTES = "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES";

public const string EMITCATEGORYANDEVENTATTRIBUTES = "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_CATEGORY_AND_EVENT_LOG_ATTRIBUTES";
utpilla marked this conversation as resolved.
Show resolved Hide resolved

public ExperimentalOptions()
: this(new ConfigurationBuilder().AddEnvironmentVariables().Build())
{
Expand All @@ -36,10 +38,20 @@ public ExperimentalOptions(IConfiguration configuration)
{
this.EmitLogExceptionAttributes = emitLogExceptionAttributes;
}

if (configuration.TryGetBoolValue(EMITCATEGORYANDEVENTATTRIBUTES, out var emitLogEventAndCategoryAttributes))
{
this.EmitLogEventAndCategoryAttributes = emitLogEventAndCategoryAttributes;
}
}

/// <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 and category attributes should be exported.
/// </summary>
public bool EmitLogEventAndCategoryAttributes { get; set; } = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,33 +91,28 @@ 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.EmitLogEventAndCategoryAttributes)
{
// 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 (!string.IsNullOrEmpty(logRecord.CategoryName))
{
// 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.
AddStringAttribute(otlpLogRecord, "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, "event.id", logRecord.EventId.Id, attributeCountLimit);
vishweshbankwar marked this conversation as resolved.
Show resolved Hide resolved
vishweshbankwar marked this conversation as resolved.
Show resolved Hide resolved
}

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, "event.name", 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_AND_EVENT_LOG_ATTRIBUTES`

When set to `true`, it enables export of `LogRecord.EventId` as `event.id` and
vishweshbankwar marked this conversation as resolved.
Show resolved Hide resolved
`event.name` and `LogRecord.CategoryName` as `dotnet.ilogger.category`.

## 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 @@ -209,9 +209,11 @@ public void OtlpLogRecordTestWhenStateValuesArePopulated()
Assert.Equal("Hello from {name} {price}.", attribute.Value.StringValue);
}

/*
[Fact]
public void CheckToOtlpLogRecordLoggerCategory()
[Theory]
[InlineData("true")]
[InlineData("false")]
[InlineData(null)]
public void CheckToOtlpLogRecordLoggerCategory(string emitLogEventAndCategoryAttributes)
{
var logRecords = new List<LogRecord>();
using var loggerFactory = LoggerFactory.Create(builder =>
Expand All @@ -227,27 +229,46 @@ public void CheckToOtlpLogRecordLoggerCategory()
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);
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string> { [ExperimentalOptions.EMITCATEGORYANDEVENTATTRIBUTES] = emitLogEventAndCategoryAttributes })
.Build();

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

var otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord);

if (emitLogEventAndCategoryAttributes == "true")
{
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);
}
else
{
Assert.NotNull(otlpLogRecord);
Assert.Empty(otlpLogRecord.Attributes);
}

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

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

[Fact]
public void CheckToOtlpLogRecordEventId()
[Theory]
[InlineData("true")]
[InlineData("false")]
[InlineData(null)]
public void CheckToOtlpLogRecordEventId(string emitLogEventAndCategoryAttributes)
{
var logRecords = new List<LogRecord>();
using var loggerFactory = LoggerFactory.Create(builder =>
Expand All @@ -264,37 +285,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.EMITCATEGORYANDEVENTATTRIBUTES] = emitLogEventAndCategoryAttributes })
.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 (emitLogEventAndCategoryAttributes == "true")
{
Assert.Contains("event.id", otlpLogRecordAttributes);
Assert.Contains("10", otlpLogRecordAttributes);
}
else
{
Assert.DoesNotContain("event.id", 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 (emitLogEventAndCategoryAttributes == "true")
{
Assert.Contains("event.id", otlpLogRecordAttributes);
Assert.Contains("10", otlpLogRecordAttributes);
Assert.Contains("event.name", otlpLogRecordAttributes);
Assert.Contains("MyEvent10", otlpLogRecordAttributes);
}
else
{
Assert.DoesNotContain("event.id", otlpLogRecordAttributes);
Assert.DoesNotContain("event.name", otlpLogRecordAttributes);
}
}
*/

[Fact]
public void CheckToOtlpLogRecordTimestamps()
Expand Down