Skip to content

Commit

Permalink
OTLP LogExporter modified to not drop whole batch if a single log is …
Browse files Browse the repository at this point in the history
…invalid (#2934)
  • Loading branch information
cijothomas authored Feb 23, 2022
1 parent cbc5172 commit 72ade32
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 56 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 @@ -14,6 +14,13 @@
are now configurable via the `MetricReaderOptions`.
([#2717](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2717))

* Exporter bug fix to not throw exceptions from Export method.
([#2915](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2915))

* OTLP LogExporter modified to not drop the whole batch if a single log from the
batch is invalid.
([#2934](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2934))

## 1.2.0-rc2

Released 2022-Feb-02
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// limitations under the License.
// </copyright>

using System;
using System.Runtime.CompilerServices;
using Google.Protobuf;
using Google.Protobuf.Collections;
Expand Down Expand Up @@ -43,80 +44,84 @@ internal static void AddBatch(
var instrumentationLibraryLogs = new OtlpLogs.InstrumentationLibraryLogs();
resourceLogs.InstrumentationLibraryLogs.Add(instrumentationLibraryLogs);

foreach (var item in logRecordBatch)
foreach (var logRecord in logRecordBatch)
{
var logRecord = item.ToOtlpLog();
if (logRecord == null)
var otlpLogRecord = logRecord.ToOtlpLog();
if (otlpLogRecord != null)
{
OpenTelemetryProtocolExporterEventSource.Log.CouldNotTranslateLogRecord(
nameof(LogRecordExtensions),
nameof(AddBatch));
continue;
instrumentationLibraryLogs.Logs.Add(otlpLogRecord);
}

instrumentationLibraryLogs.Logs.Add(logRecord);
}
}

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

try
{
TimeUnixNano = (ulong)logRecord.Timestamp.ToUnixTimeNanoseconds(),
Name = logRecord.CategoryName,
otlpLogRecord = new OtlpLogs.LogRecord
{
TimeUnixNano = (ulong)logRecord.Timestamp.ToUnixTimeNanoseconds(),
Name = logRecord.CategoryName,

// TODO: Devise mapping of LogLevel to SeverityNumber
// See: https://github.com/open-telemetry/opentelemetry-proto/blob/bacfe08d84e21fb2a779e302d12e8dfeb67e7b86/opentelemetry/proto/logs/v1/logs.proto#L100-L102
SeverityText = logRecord.LogLevel.ToString(),
};
// TODO: Devise mapping of LogLevel to SeverityNumber
// See: https://github.com/open-telemetry/opentelemetry-proto/blob/bacfe08d84e21fb2a779e302d12e8dfeb67e7b86/opentelemetry/proto/logs/v1/logs.proto#L100-L102
SeverityText = logRecord.LogLevel.ToString(),
};

if (logRecord.FormattedMessage != null)
{
otlpLogRecord.Body = new OtlpCommon.AnyValue { StringValue = logRecord.FormattedMessage };
}
if (logRecord.FormattedMessage != null)
{
otlpLogRecord.Body = new OtlpCommon.AnyValue { StringValue = logRecord.FormattedMessage };
}

if (logRecord.StateValues != null)
{
foreach (var stateValue in logRecord.StateValues)
if (logRecord.StateValues != null)
{
var otlpAttribute = stateValue.ToOtlpAttribute();
otlpLogRecord.Attributes.Add(otlpAttribute);
foreach (var stateValue in logRecord.StateValues)
{
var otlpAttribute = stateValue.ToOtlpAttribute();
otlpLogRecord.Attributes.Add(otlpAttribute);
}
}
}

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

if (!string.IsNullOrEmpty(logRecord.EventId.Name))
{
otlpLogRecord.Attributes.AddStringAttribute(nameof(logRecord.EventId.Name), logRecord.EventId.Name);
}
if (!string.IsNullOrEmpty(logRecord.EventId.Name))
{
otlpLogRecord.Attributes.AddStringAttribute(nameof(logRecord.EventId.Name), logRecord.EventId.Name);
}

if (logRecord.Exception != null)
{
otlpLogRecord.Attributes.AddStringAttribute(SemanticConventions.AttributeExceptionType, logRecord.Exception.GetType().Name);
otlpLogRecord.Attributes.AddStringAttribute(SemanticConventions.AttributeExceptionMessage, logRecord.Exception.Message);
otlpLogRecord.Attributes.AddStringAttribute(SemanticConventions.AttributeExceptionStacktrace, logRecord.Exception.ToInvariantString());
}
if (logRecord.Exception != null)
{
otlpLogRecord.Attributes.AddStringAttribute(SemanticConventions.AttributeExceptionType, logRecord.Exception.GetType().Name);
otlpLogRecord.Attributes.AddStringAttribute(SemanticConventions.AttributeExceptionMessage, logRecord.Exception.Message);
otlpLogRecord.Attributes.AddStringAttribute(SemanticConventions.AttributeExceptionStacktrace, logRecord.Exception.ToInvariantString());
}

if (logRecord.TraceId != default && logRecord.SpanId != default)
{
byte[] traceIdBytes = new byte[16];
byte[] spanIdBytes = new byte[8];
if (logRecord.TraceId != default && logRecord.SpanId != default)
{
byte[] traceIdBytes = new byte[16];
byte[] spanIdBytes = new byte[8];

logRecord.TraceId.CopyTo(traceIdBytes);
logRecord.SpanId.CopyTo(spanIdBytes);
logRecord.TraceId.CopyTo(traceIdBytes);
logRecord.SpanId.CopyTo(spanIdBytes);

otlpLogRecord.TraceId = UnsafeByteOperations.UnsafeWrap(traceIdBytes);
otlpLogRecord.SpanId = UnsafeByteOperations.UnsafeWrap(spanIdBytes);
otlpLogRecord.Flags = (uint)logRecord.TraceFlags;
}
otlpLogRecord.TraceId = UnsafeByteOperations.UnsafeWrap(traceIdBytes);
otlpLogRecord.SpanId = UnsafeByteOperations.UnsafeWrap(spanIdBytes);
otlpLogRecord.Flags = (uint)logRecord.TraceFlags;
}

// TODO: Add additional attributes from scope and state
// Might make sense to take an approach similar to https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/897b734aa5ea9992538f04f6ea6871fe211fa903/src/OpenTelemetry.Contrib.Preview/Internal/DefaultLogStateConverter.cs
// TODO: Add additional attributes from scope and state
// Might make sense to take an approach similar to https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/897b734aa5ea9992538f04f6ea6871fe211fa903/src/OpenTelemetry.Contrib.Preview/Internal/DefaultLogStateConverter.cs
}
catch (Exception ex)
{
OpenTelemetryProtocolExporterEventSource.Log.CouldNotTranslateLogRecord(ex.Message);
}

return otlpLogRecord;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ public void UnsupportedProtocol(string protocol)
this.WriteEvent(8, protocol);
}

[Event(9, Message = "Could not translate LogRecord from class '{0}' and method '{1}', log will not be exported.", Level = EventLevel.Informational)]
public void CouldNotTranslateLogRecord(string className, string methodName)
[Event(9, Message = "Could not translate LogRecord due to Exception: '{0}'. Log will not be exported.", Level = EventLevel.Warning)]
public void CouldNotTranslateLogRecord(string exceptionMessage)
{
this.WriteEvent(9, className, methodName);
this.WriteEvent(9, exceptionMessage);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
using Xunit;
using OtlpCollector = Opentelemetry.Proto.Collector.Trace.V1;

namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.Implementation.ExportClient
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
{
public class OtlpHttpTraceExportClientTests
{
Expand Down

0 comments on commit 72ade32

Please sign in to comment.