Отслеживание ошибок Datadog не работает. Атрибуты ошибок не были экспортированы или проанализированы правильно

У меня есть проект API .net6. Я использую открытую телеметрию (отель) для ее инструментирования и отправки трассировок в datadog. Я использую это изображение otel/opentelemetry-collector-contrib:latest для получения телеметрии из моего приложения и отправки в datadog.

Я настроил ExceptionMiddleware для захвата всех исключений и создания атрибутов диапазона.

public async Task InvokeAsync(HttpContext context)
    {
        string userId = GetUserId(context);
        try
        {
            await this._next(context);
        }
        catch (BusinessException bex)
        {
            LogWarning(bex);
            UpdateSpan(bex, userId);
            await this.HandleBadRequest(bex.Message, bex.StackTrace, context);
        }
        catch (Exception ex)
        {
            LogError(ex);
            string errorId = UpdateSpan(ex, userId);
    
            await this.HandleInternalServerError($"ErrorId: {errorId}", ex.StackTrace, context);
        }
    }

    private static string UpdateSpan(Exception ex, string userId)
    {
        Activity activity = Activity.Current;
        if (activity == null)
            return _defaultActivityId;
    
        string errorId = activity.Id.ToString();
        activity?.SetStatus(ActivityStatusCode.Error, ex.Source);
    
        activity?.SetTag("exception.message", ex.Message);
        activity?.SetTag("exception.stacktrace", ex.StackTrace);
        activity?.SetTag("exception.type", ex.Source);
    
        return errorId;
    }

    private void LogWarning(BusinessException bex)
    {
        Dictionary<string, object> errorDict = BuildExceptionAtributes(bex);
    
        using (_logger.BeginScope(errorDict))
        {
            _logger.LogWarning(bex.Message);
    
        };
    }
    
    private void LogError(Exception ex)
    {
        Dictionary<string, object> errorDict = BuildExceptionAtributes(ex);
    
        using (_logger.BeginScope(errorDict))
        {
            _logger.LogError(ex.Message);
        };
    }
    
    
    private static Dictionary<string, object> BuildExceptionAtributes(Exception ex)
    {
        var errorDict = new Dictionary<string, object>()
        {
            // otel
            ["exception.message"] = ex.Message,
            ["exception.stacktrace"] = ex.StackTrace,
            ["exception.type"] = ex.Source,
        };
        return errorDict;
    }

Я создал процессор для преобразования атрибутов исключений в атрибуты ошибок в соответствии с этой документацией datadog

Атрибуты исключений экспортируются правильно, а атрибуты ошибок — нет.

Пример:

  • exception.type становится error.message,
  • exception.stacktrace становится error.stack (ок)
  • exception.message игнорируется
  • error.type исчезнуть
{
    "exception": {
        "message": "Object reference not set to an instance of an object.",
        "stacktrace": "at blablabla.GetConnection(Int64 userId, Nullable`1 version) in blablabla.cs:line 36",
        "type": "blablabla.WebApi"
    },
    "error": {
        "message": "blablabla.WebApi",
        "stack": "at blablabla.GetConnection(Int64 userId, Nullable`1 version) in blablabla.cs:line 36",
        "type": "does-not-show"
    }
}

Это мой конфигурационный файл сборщика отелей.yaml

# This is a configuration file for the OpenTelemetry Collector intended to be
# used in conjunction with the OTLP Exporter example (see ../TestOtlpExporter.cs)
#
# For more information about the OpenTelemetry Collector see:
#   https://github.com/open-telemetry/opentelemetry-collector
#
receivers:
  otlp:
    protocols:
      grpc:
      http:

exporters:
  logging:
    verbosity: detailed
  datadog:
    api:
      key: some-key
      # site: 

processors:
  # https://github.com/open-telemetry/opentelemetry-collector/blob/main/processor/batchprocessor/README.md
  batch:
  attributes/insert:
    actions:
      - key: "error.message"
        action: "insert"
        from_attribute: "exception.message"
  attributes/insert2:
    actions:
      - key: "error.stack"
        action: "insert"
        from_attribute: "exception.stacktrace"
  attributes/insert3:
    actions:
      - key: "error.type"
        action: "insert"
        from_attribute: "exception.type"
      

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch, attributes/insert, attributes/insert2, attributes/insert3]
      exporters: [logging, datadog]
    metrics:
      receivers: [otlp]
      exporters: [logging, datadog]
    logs:
      receivers: [otlp]
      exporters: [logging, datadog]

===

Я попробовал отправить атрибуты ошибок напрямую, без процессора отель-сборщика, и получил точно такую ​​же проблему, как описано выше.

private static string UpdateSpan(Exception ex, string userId)
{
    Activity activity = Activity.Current;
    if (activity == null)
        return _defaultActivityId;

    string errorId = activity.Id.ToString();
    activity?.SetStatus(ActivityStatusCode.Error, ex.Source);
    activity?.SetTag("kinvo.user.id", userId);

    activity?.SetTag("exception.message", ex.Message);
    activity?.SetTag("exception.stacktrace", ex.StackTrace);
    activity?.SetTag("exception.type", ex.Source);

    // sending directly
    activity?.SetTag("error.message", ex.Message);
    activity?.SetTag("error.stack", ex.StackTrace);
    activity?.SetTag("error.type", ex.Source);

    return errorId;
}

Привет, Витор, можешь ли ты попробовать это без переназначения коллектора и ручного подхода? У OTel есть метод span.recordException(), который должен делать все автоматически, а DD должен уметь правильно его сопоставить. Вот страница документа RecordException: opentelemetry.io/docs/languages/java/instrumentation/… Дайте мне знать, если это работает.

Juliano Costa 18.04.2024 09:19

Привет Джулиано. В C# OpenTelemetry зависит от класса Activity из System.Diagnostics. Таким образом, реализация span.recordException() — это всего лишь обертка вокруг методов этого класса, что я и делаю в приведенном мной примере.

Vitor Ribeiro 24.04.2024 14:17
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
114
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я нашел проблему. Это в этой строке кода

activity?.SetStatus(ActivityStatusCode.Error, ex.Source);

Когда я использую второй параметр SetStatus, он переопределяет error.message при экспорте в Datadog.

Поэтому мне просто нужно использовать его следующим образом:

activity?.SetStatus(ActivityStatusCode.Error);

Другие вопросы по теме