MonixOne.OpenObservability 1.0.3

dotnet add package MonixOne.OpenObservability --version 1.0.3
                    
NuGet\Install-Package MonixOne.OpenObservability -Version 1.0.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MonixOne.OpenObservability" Version="1.0.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MonixOne.OpenObservability" Version="1.0.3" />
                    
Directory.Packages.props
<PackageReference Include="MonixOne.OpenObservability" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MonixOne.OpenObservability --version 1.0.3
                    
#r "nuget: MonixOne.OpenObservability, 1.0.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MonixOne.OpenObservability@1.0.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MonixOne.OpenObservability&version=1.0.3
                    
Install as a Cake Addin
#tool nuget:?package=MonixOne.OpenObservability&version=1.0.3
                    
Install as a Cake Tool

MonixOne.OpenObservability

MonixOne.OpenObservability

Центральная телеметрия для ASP.NET Core и .NET 9/10: OpenTelemetry, Sentry, корреляция запросов, HTTP-логирование с маскированием тел и диагностика исключений.

Репозиторий · Сообщить об ошибке

Содержание

Возможности

Задача Реализация
Логи, трассировки и метрики OpenTelemetry с опциональным OTLP-экспортом
Входящие HTTP-запросы и ответы Штатный ASP.NET Core HTTP Logging
Исходящие HTTP-метаданные Microsoft.Extensions.Http.Diagnostics
Захват исходящих тел Пассивное наблюдение через Nerdbank.Streams.MonitoringStream
Маскирование JSON и форм JsonMasking и штатный парсер QueryHelpers.ParseQuery
Диагностика исключений Автоматическое обогащение обычных вызовов ILogger
События ошибок Официальный Sentry SDK

Пакет связывает HttpContext.TraceIdentifier, заголовок x-correlation-id, ILogger scopes и Sentry с W3C Activity.TraceId. ASP.NET Core и HttpClient используют стандартный traceparent.

Подключение

NuGet

При центральном управлении версиями добавьте в Directory.Packages.props:

<ItemGroup>
    <PackageVersion Include="MonixOne.OpenObservability" Version="1.0.3" />
</ItemGroup>

В проект приложения добавьте ссылку без версии:

<ItemGroup>
    <PackageReference Include="MonixOne.OpenObservability" />
</ItemGroup>

ASP.NET Core

Зарегистрируйте пакет перед Build(). В HTTP pipeline подключите его после routing, перед существующей обработкой ошибок и аутентификацией приложения.

using MonixOne.OpenObservability;

builder.AddOpenObservability();

app.UseRouting();
app.UseOpenObservability();

UseOpenObservability() подключает корреляцию и штатный HTTP Logging. Повторный AddOpenObservability() не дублирует обработчики и exporters. Ранее зарегистрированные ILogger providers сохраняются.

Generic host

Для worker services и консольных приложений достаточно регистрации в host:

using Microsoft.Extensions.Hosting;
using MonixOne.OpenObservability;

var builder = Host.CreateApplicationBuilder(args);
builder.AddOpenObservability();

using var host = builder.Build();
await host.RunAsync();

Lifecycle host управляет запуском и завершением SDK, включая flush при остановке.

Конфигурация OpenTelemetry

appsettings.json

{
  "Service": {
    "Name": "identity-service"
  },
  "Observability": {
    "Logging": {
      "Enabled": true,
      "OtlpEndpoint": "http://otel-collector:4318",
      "OtlpProtocol": "http/protobuf",
      "ConsoleEnabled": false
    }
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning"
    }
  }
}
Параметр Назначение По умолчанию
Service:Name Имя сервиса в телеметрии Имя приложения из host environment
Observability:Logging:Enabled Включение OTLP-экспорта logs, traces и metrics false
Observability:Logging:OtlpEndpoint Базовый HTTP/HTTPS endpoint коллектора Не задан
Observability:Logging:OtlpProtocol http/protobuf, http или grpc http/protobuf
Observability:Logging:OtlpHeaders Заголовки авторизации OTLP-экспорта OTEL_EXPORTER_OTLP_HEADERS, если задан
Observability:Logging:ConsoleEnabled Включение штатного console provider true для Development/Local, иначе false

Секция Observability:Logging сохранена для совместимости существующих приложений. Enabled управляет OTLP-экспортом всех трёх сигналов. Невалидный или пустой endpoint отключает этот экспорт; локальные логи продолжают работать. Sentry включается отдельно через DSN.

Версия сервиса определяется по entry assembly, окружение — по host environment.

Переменные окружения

В именах вложенных параметров используется __:

Service__Name=identity-service
Observability__Logging__Enabled=true
Observability__Logging__OtlpEndpoint=http://otel-collector:4318
Observability__Logging__OtlpProtocol=http/protobuf
Observability__Logging__ConsoleEnabled=false

Заголовки OTLP задавайте через Observability__Logging__OtlpHeaders или OTEL_EXPORTER_OTLP_HEADERS. Секретные значения храните вне репозитория.

Сигналы и метрики

При http/protobuf пакет добавляет к базовому endpoint путь соответствующего сигнала:

Сигнал Путь
Логи /v1/logs
Трассировки /v1/traces
Метрики /v1/metrics

Собираются стандартные метрики .NET 9/10 из meters:

  • Microsoft.AspNetCore.Hosting;
  • Microsoft.AspNetCore.Server.Kestrel;
  • System.Net.Http;
  • System.Net.NameResolution;
  • System.Runtime.

Уровни HTTP-логирования

Пакет задаёт Information для своих HTTP-категорий, в том числе при общем Microsoft=Warning. Явные правила для этих категорий в Logging:LogLevel сохраняют приоритет.

Входящие запросы логируются для всех HTTP-методов и итоговых статусов, включая 4xx/5xx. Отдельно включать HTTP-логи не требуется. Исключены /api/health и вложенные маршруты; явные фильтры конкретного provider сохраняют приоритет. OTLP-экспорт требует Observability:Logging:Enabled=true и корректного OtlpEndpoint.

Например, исходящий захват тел можно отключить отдельно:

{
  "Logging": {
    "LogLevel": {
      "MonixOne.OpenObservability.HttpBodyLoggingHandler": "None"
    }
  }
}

HTTP-логирование

Входящие и исходящие вызовы

Входящие запросы и ответы захватывает штатный AddHttpLogging() / UseHttpLogging(). Запрос и ответ объединяются в одну запись с итоговым status, route template и duration.

Исходящие метаданные логирует AddExtendedHttpClientLogging() для всех клиентов IHttpClientFactory, включая Refit и OAuth backchannel. Тела передаются в лог через адаптер HttpContent и библиотечный MonitoringStream.

HTTP-метаданные, body-события и исключения Refit содержат безопасный контекст зависимости:

Поле Содержимое
dependency_address, dependency_port Host и port без userinfo, path, query и fragment
dependency_http_method Метод исходящего запроса
dependency_http_route Шаблон маршрута из Refit, если доступен
dependency_client Полное имя интерфейса Refit, если доступно
upstream_status_code Полученный HTTP status; при транспортном сбое без ответа отсутствует или null

ApiRequestException также сохраняет контекст зависимости при DNS, TLS и других транспортных сбоях. Из HttpRequestException без Refit нельзя восстановить адрес зависимости: используйте связанное исходящее HTTP-событие или scope приложения.

Транспортные HTTP-клиенты OTLP-exporters исключены из HTTP-логирования приложения и захвата тел. Это сохраняет синхронную отправку и настройки транспорта SDK и не создаёт HTTP-логи о передаче самой телеметрии.

Захват пассивный: учитываются только отправленные или прочитанные байты. Сохраняются cancellation, клиентские таймауты, заголовки, Content-Length и владение потоком. Дополнительного чтения и отдельных body-таймаутов нет.

Форматы и маскирование

Поддерживаются:

  • application/json;
  • application/*+json;
  • application/x-www-form-urlencoded.

Рекурсивное маскирование выполняет JsonMasking. Правила работают без учёта регистра, на любой глубине объектов и массивов.

Маскируются пароли, токены, client secrets, API keys, OTP, authorization codes/verifiers, контакты, credentials/assertions/challenges, SMS text/destination и персональные поля. Поля detail, message и error_description скрываются целиком: внешние сервисы могут отражать в них секреты из запроса.

Пример очищенного тела:

{
  "request_id": "req-42",
  "access_token": "[REDACTED]",
  "profile": {
    "email": "[REDACTED]",
    "safe": "visible"
  }
}

Формы декодируются через QueryHelpers.ParseQuery и логируются как очищенный JSON, включая повторяющиеся и percent-encoded поля. Очищенные body-атрибуты и текст получают все providers: Console, OpenTelemetry и дополнительные провайдеры приложения.

Лимиты и неполные тела

Ограничение Значение
Лимит захвата тела 16 КБ
Лимит очищенного тела 16 КБ
Максимальная глубина JSON 32

Пустое тело остаётся пустым. Невалидные, обрезанные, слишком большие и неподдерживаемые тела получают [omitted: reason]. Неполное чтение получает incomplete_body вместо захваченного префикса. Бинарные потоки, файлы и неструктурированный текст не включаются в содержимое body-логов.

Маскирование зависит от имён полей. Секрет в произвольно названном поле не может надёжно распознаваться автоматически; новые контракты нужно сверять с политикой пакета. Сообщения и структурированные поля бизнес-логов также должны исключать секреты.

Заголовки и маршруты

Направление Разрешённые значения заголовков
Запрос Accept, Content-Type, Content-Length, Content-Encoding, traceparent, x-correlation-id, x-request-id
Ответ Content-Type, Content-Length, Content-Encoding, Retry-After, traceparent, x-correlation-id, x-request-id

Значения Authorization, Cookie, Set-Cookie и API keys не выводятся. Query string и значения route-параметров скрываются; входящие маршруты представлены шаблонами, исходящие параметры редактируются в режиме Strict. Refit дополнительно предоставляет http_route.

Штатные enrichment callbacks OpenTelemetry удаляют raw URL/path/query из HTTP spans, сохраняя method, route template, status и корреляцию. Адаптер ILogger очищает URL-компоненты сообщений Hosting.Diagnostics и RequestPath в его scopes, включая уровень Information.

Входящие /api/health и его вложенные маршруты исключены из HTTP Logging и ASP.NET Core tracing.

Потоковые ответы и ограничения клиентов

ResponseHeadersRead возвращает ответ после заголовков. Содержимое тела появляется в логе после чтения, копирования или освобождения content/потока. Исходящие body-события сохраняют trace_id и span_id исходного HTTP-запроса, включая чтение после завершения его Activity.

Для захвата исходящих тел используйте IHttpClientFactory и SendAsync(). Прямой new HttpClient() обходит регистрацию захвата тел. Штатный Microsoft HTTP logger отклоняет синхронный HttpClient.Send(); синхронное чтение ответа после SendAsync() поддерживается.

Диагностика исключений

Обычные вызовы ILogger

Передайте исключение в стандартный вызов — пакет добавит диагностику автоматически:

logger.LogError(
    exception,
    "External service request failed for {request_id}",
    requestId);

Для стабильного имени бизнес-события используйте LogEvent():

logger.LogEvent(
    LogLevel.Error,
    "external.request.failed",
    exception,
    "External service request failed for {request_id}",
    requestId);

Диагностические поля

Поле Содержимое
exception_type Тип переданного исключения
exception_inner_type Тип непосредственного InnerException
exception_root_type Тип корневого исключения
exception.type Полное имя типа переданного исключения
exception_stack_trace Типы и стеки цепочки без сообщений исключений
failure_kind Классификация сбоя
status_code, upstream_status_code HTTP status зависимости из Refit ApiException или HttpRequestException; status_code сохранён для совместимости
http_request_error Категория HttpRequestError из HttpRequestException или HttpIOException, если известна
socket_error_code Код SocketException, если найден
cancellation_source Для timeout/cancellation: timeout, request_aborted или unknown
json_error_line Строка ошибки JSON
json_error_byte_position Позиция ошибки JSON в байтах
http_method, http_route Метод и шаблон входящего маршрута при доступном HTTP-контексте
http_response_status_code_at_log Снимок status входящего ответа в момент логирования исключения
http_response_started Были ли уже отправлены заголовки входящего ответа

http_status_code в событии http.request.completed — итоговый статус запроса в пределах HTTP Logging middleware. Обработчик ошибок приложения должен находиться внутри него, как в примере подключения. Лог исключения не публикует этот статус: его http_response_status_code_at_log ещё может быть 200, хотя обработчик затем вернёт 500. Статус зависимости хранится отдельно в upstream_status_code.

Отмена входящего запроса определяется по совпадению отменённого token исключения с HttpContext.RequestAborted. Для linked/пользовательского token и остановки host, происхождение которых нельзя доказать по доступному контексту, сохраняется unknown. Наличие TimeoutException или Polly timeout классифицируется как timeout, в том числе внутри TaskCanceledException от HttpClient.

JsonException распознаётся во всей InnerException-цепочке и во всех ветках AggregateException, в том числе при вложенном FormatException. JsonException.Path не экспортируется: имена динамических JSON-ключей могут содержать персональные данные. Статическое имя контракта/DTO при необходимости добавляйте в структурированные поля бизнес-лога.

Классификация сбоев

Классификация выбирается в следующем порядке:

failure_kind Условие
response_deserialization Найден ApiException, содержащий JsonException в собственной цепочке, в том числе при HTTP 200
json Остальные JSON-сбои: направление и источник не предполагаются
timeout Найден TimeoutException или Polly TimeoutRejectedException
canceled Найден OperationCanceledException
http_status HTTP status не меньше 400
transport Найден HttpRequestException, HttpIOException или SocketException без предыдущих условий
application Остальные исключения

HTTP-исключение и JSON-сбой в разных ветках AggregateException не считаются доказательством ошибки десериализации HTTP-ответа.

Сырые объекты Exception не передаются providers: ApiException.ToString() может содержать полное HTTP-тело. Экспортируются типы, классификация и стеки без Exception.Message / ToString().

Дополнительные расширения

Если исключение не передаётся в ILogger, диагностику можно добавить через scope:

using var scope = logger.BeginExceptionDiagnostics(exception);
logger.LogWarning("Operation rejected");

exception.GetHttpStatusCode() доступен отдельно для интеграционного кода.

Sentry

Включение через DSN

Для включения Sentry задайте DSN проекта через Sentry:Dsn или переменную окружения Sentry__Dsn. Также поддерживается SENTRY_DSN:

Sentry__Dsn=https://public-key@sentry.example.com/1

Это пример формата DSN; подставьте значение из настроек своего Sentry-проекта. Для SENTRY_DSN действует fallback: пакет использует его, если Sentry:Dsn не задан или пуст. При непустом Sentry:Dsn приоритет остаётся за ним.

Если оба значения пусты, Sentry не подключается. OpenTelemetry и локальные логи продолжают работать согласно своим настройкам.

appsettings.json

DSN — единственный обязательный параметр для включения интеграции. Уровень событий и sampling можно задать дополнительно:

{
  "Sentry": {
    "Dsn": "",
    "MinimumEventLevel": "Error",
    "TracesSampleRate": 0.0
  }
}

В этом примере DSN приходит из окружения, Sentry получает события уровня Error и выше, а performance tracing Sentry выключен.

Параметр Назначение
Dsn Адрес проекта для отправки событий
MinimumEventLevel Минимальный уровень события ILogger, например Error или Critical
TracesSampleRate Доля Sentry transactions: 0.0 — tracing выключен, 0.1 — sampling 10%, 1.0 — sampling 100%

Событие должно пройти и фильтр категории ILogger, и Sentry:MinimumEventLevel. TracesSampleRate не меняет отправку ошибок и настройки трассировок OpenTelemetry.

Эквивалентные настройки через окружение:

Sentry__Dsn=https://public-key@sentry.example.com/1
Sentry__MinimumEventLevel=Error
Sentry__TracesSampleRate=0.0

Отправка событий и очистка данных

Для ASP.NET Core пакет регистрирует официальный UseSentry(), для generic host — Sentry logging provider. Обычные LogError() / LogCritical() отправляют очищенные события; ручной CaptureException() в сервисах не требуется.

Штатные exception processors SDK сохраняют типы исключений и stack frames. Перед отправкой пакет очищает:

  • request/user данные и сообщения исключений; сохраняются только HTTP method и route template в request.other.route, когда он доступен из контекста пакета;
  • значения локальных переменных stack frames;
  • Exception.Data, включая проекции в mechanism, tags и contexts;
  • HTTP URL в span data/tags, HTTP span descriptions и URL transaction names.

Пакет задаёт следующие настройки поверх секции Sentry:

Настройка Значение
Environment Окружение приложения из host environment
SendDefaultPii false
MaxBreadcrumbs 30
MaxRequestBodySize для ASP.NET Core None
ShutdownTimeout 5 секунд

HTTP-тела доступны в очищенных OpenTelemetry-логах. Завершение SDK и flush выполняются через lifecycle host.

Безопасная история перед ошибкой

Sentry хранит до 30 последних breadcrumbs. Штатный BeforeBreadcrumb callback создаёт их заново из разрешённых структурированных полей; форматированное сообщение не сохраняется, исходное время шага сохраняется при копировании scopes. HTTP-запросы получают отдельный Sentry scope, чтобы контекст и история параллельных запросов не смешивались. В worker services задавайте отдельный scope для каждой обработки сообщения/задачи.

Разрешены:

  • event_name, operation, error_code, failure_kind, exception.type;
  • HTTP method/route/status, снимок статуса ответа и http_response_started;
  • dependency_address, dependency_port, dependency_http_method, dependency_http_route, dependency_client, upstream_status_code;
  • http_request_error, socket_error_code, cancellation_source, JSON line/byte position;
  • duration_ms, retry_attempt, retry_delay_ms, timeout_ms, circuit_state;
  • trace_id, span_id.

Значения должны быть строками или простыми числовыми/boolean значениями; строки длиннее 256 символов пропускаются. URL, заголовки, payload, сообщения исключений, произвольные объекты и HTTP body-события в breadcrumbs не включаются. Шаги без разрешённых данных, кроме одной корреляции, отбрасываются.

LogEvent() передаёт стабильное имя события. Остальные поля breadcrumbs берутся из состояния конкретного вызова ILogger; scopes продолжают обогащать само событие Sentry. Используйте имена операций, коды ошибок, route templates и значения состояния, заданные кодом, без пользовательского содержимого:

logger.LogEvent(
    LogLevel.Warning,
    "dependency.retry.scheduled",
    "Retry {retry_attempt} for {operation} after {retry_delay_ms} ms; timeout {timeout_ms} ms",
    attempt,
    "profile.exchange",
    delay.TotalMilliseconds,
    timeout.TotalMilliseconds);

Retry/timeout/circuit поля записывайте там, где настроена соответствующая политика. Пакет не создаёт retries и не предполагает их наличие.

Расследование инцидентов

  1. Найдите бизнес-ошибку по event_name и определите её trace_id.
  2. Найдите входящий HTTP-лог и spans с тем же trace_id.
  3. Сопоставьте исходящие HTTP-метаданные и body-события.
  4. Сопоставьте итоговый http_status_code входящего запроса с upstream_status_code зависимости; снимок http_response_status_code_at_log может отличаться от итогового.
  5. Проверьте dependency_*, failure_kind, http_request_error, socket_error_code, content type/length, RequestBody и ResponseBody.
  6. Для JSON проверьте категорию и line/byte position; для отмены — cancellation_source; для остальных сбоев — типы и стеки. В Sentry используйте безопасные breadcrumbs для последовательности операций и повторов перед ошибкой.
event_name Данные
http.request.completed Входящий запрос/ответ, итоговый status и duration_ms
http.client.request.body Очищенное тело исходящего запроса
http.client.response.body Очищенное тело исходящего ответа

Разработка и сборка

Структура решения

src/MonixOne.OpenObservability/        Исходный код пакета
tests/MonixOne.OpenObservability.Tests/ Проверки .NET 9/10
assets/                              Иконка пакета
Directory.Build.props                Общие настройки сборки и анализаторов
Directory.Packages.props             Центральные версии зависимостей
global.json                          SDK 10 и Microsoft.Testing.Platform
.github/workflows/                   CI и публикация

Все PackageReference используют центральные версии через ManagePackageVersionsCentrally. В Solution Items собраны настройки editor/git, build/package props, SDK, README, лицензия, иконка и workflows.

Локальные проверки

Команды выполняются из корня решения:

dotnet restore MonixOne.OpenObservability.sln

dotnet build MonixOne.OpenObservability.sln --configuration Release --no-restore \
  -p:GeneratePackageOnBuild=false

dotnet test \
  --project tests/MonixOne.OpenObservability.Tests/MonixOne.OpenObservability.Tests.csproj \
  --configuration Release --no-build --no-restore

Сборка NuGet

dotnet pack src/MonixOne.OpenObservability/MonixOne.OpenObservability.csproj \
  --configuration Release --no-build --no-restore \
  --output artifacts/nuget -p:GeneratePackageOnBuild=false

Результат — .nupkg и .snupkg в artifacts/nuget.

CI и публикация

CI собирает Release, запускает проверки на .NET 9/10 и создаёт NuGet-артефакты. Publish workflow запускается после merge pull request в main и использует GitHub environment production, NuGet trusted publishing и секрет NUGET_USER.

Для публикации должны быть настроены trusted publisher в NuGet и соответствующее окружение GitHub.

Лицензия

MIT.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.3 0 10/17/2026
1.0.2 63 10/16/2026
1.0.1 54 10/16/2026
1.0.0 39 10/16/2026

Improve JSON failure classification and HTTP status semantics. Add safe dependency, network error and cancellation context, plus sanitized Sentry breadcrumbs with request isolation and preserved timestamps.