From bd8f3ecad5bb2c1f76ad36a1fac57cbd5f3e0682 Mon Sep 17 00:00:00 2001 From: Edoardo Tenani <526307+endorama@users.noreply.github.com> Date: Mon, 20 Nov 2023 12:12:10 +0100 Subject: [PATCH] Require only one of exception.{type,message} for log exceptions (#182) * otlp: follow semconv for exception fields otel semconv require that at lease one field between exception.message or exception.type to be present on an application exception. To ensure we respect them we default on adding exception.type only if has a value and defaulting exception.message to a constant string when empty and no exception.type is present. * otlp: add exception when message or type is present Trigger exception creation for any log message with either message or type. This aligns to otel semconv requirements for exceptions. --- input/otlp/exceptions.go | 21 +++++++++++++++++++-- input/otlp/logs.go | 8 +++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/input/otlp/exceptions.go b/input/otlp/exceptions.go index 37a90a4e..b044d465 100644 --- a/input/otlp/exceptions.go +++ b/input/otlp/exceptions.go @@ -52,19 +52,36 @@ var ( javaStacktraceMoreRegexp = regexp.MustCompile(`\.\.\. ([0-9]+) more`) ) +const ( + emptyExceptionMsg = "[EMPTY]" +) + +// convertOpenTelemetryExceptionSpanEvent creates an otel Exception event +// from the specified arguments. +// +// OpenTelemetry semantic convention require the presence of at least one +// of the following attributes: +// - exception.type +// - exception.message +// https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/#attributes +// +// To fulfill this requirement we do not set exception.type if empty but +// we always set exception.message defaulting to a constant value if empty. func convertOpenTelemetryExceptionSpanEvent( exceptionType, exceptionMessage, exceptionStacktrace string, exceptionEscaped bool, language string, ) *modelpb.Error { if exceptionMessage == "" { - exceptionMessage = "[EMPTY]" + exceptionMessage = emptyExceptionMsg } exceptionHandled := !exceptionEscaped exceptionError := modelpb.ErrorFromVTPool() exceptionError.Exception = modelpb.ExceptionFromVTPool() exceptionError.Exception.Message = exceptionMessage - exceptionError.Exception.Type = exceptionType + if exceptionType != "" { + exceptionError.Exception.Type = exceptionType + } exceptionError.Exception.Handled = &exceptionHandled if id, err := newUniqueID(); err == nil { exceptionError.Id = id diff --git a/input/otlp/logs.go b/input/otlp/logs.go index 29de04ba..a7d47744 100644 --- a/input/otlp/logs.go +++ b/input/otlp/logs.go @@ -180,11 +180,9 @@ func (c *Consumer) convertLogRecord( return true }) - if exceptionMessage != "" && exceptionType != "" { - // Per OpenTelemetry semantic conventions: - // `At least one of the following sets of attributes is required: - // - exception.type - // - exception.message` + // NOTE: we consider an error anything that contains an exception type + // or message, indipendently from the severity level. + if exceptionMessage != "" || exceptionType != "" { event.Error = convertOpenTelemetryExceptionSpanEvent( exceptionType, exceptionMessage, exceptionStacktrace, exceptionEscaped, event.Service.Language.Name,