food-market/Directory.Packages.props
nns 824ef8279c feat(observability): Prometheus метрики /metrics + бизнес-счётчики (P1-17)
prometheus-net.AspNetCore@8.2.1 + EF Core DbCommandInterceptor.

Endpoint: GET /metrics (text exposition, без auth — типичная практика;
на prod закроем nginx allow private-network).

Стандартные метрики (через UseHttpMetrics):
- http_requests_received_total (code/method/controller/action)
- http_request_duration_seconds (histogram, p50/p95/p99 SLO)
- process_cpu_seconds_total / dotnet_total_memory_bytes / GC counters

Кастомные бизнес-метрики (AppMetrics):
- food_market_documents_posted_total{type} — все типы документов
- food_market_sales_posted_total — alias по retail-sale (явно в SLO)
- food_market_supplies_posted_total — alias по supply
- food_market_documents_error_total{type, reason} — ошибки проведения
  с разбивкой по причине (serialization=40001, insufficient_stock,
  number_conflict, validation, other)
- food_market_db_query_duration_seconds{kind} — гистограмма SQL через
  DbMetricsInterceptor (kind=query для SELECT, command для CUD)

Tenant-меток в кастомных метриках НЕТ сознательно: на multi-tenant хосте
раздуло бы cardinality. Per-org разрез — через /api/reports/*.

Counters добавлены в:
- SuppliesController.Post (success + serialization-error)
- RetailSalesController.Post (success)
- PosController.CreateAndPostSaleAsync (success + number_conflict)

docs/observability.md — scrape-конфиг prometheus.yml, образец Grafana
dashboard (4 ряда: Health/Business/Database/Runtime), prometheus rules
с alert'ами (HighErrorRate, DbSerializationContention, NoSalesIn30Min).

Тесты: 3 интеграционных (endpoint доступен и возвращает text/plain с
встроенными метриками; sales counter инкрементится после Post; db_query
гистограмма накапливается).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 12:20:01 +05:00

71 lines
3.7 KiB
XML

<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<!-- ASP.NET Core 8 -->
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="8.0.11" />
<PackageVersion Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.9.0" />
<!-- EF Core 8 + PostgreSQL -->
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.11" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
<!-- OpenIddict 5 (aligned to .NET 8 LTS) -->
<PackageVersion Include="OpenIddict.AspNetCore" Version="5.8.0" />
<PackageVersion Include="OpenIddict.EntityFrameworkCore" Version="5.8.0" />
<!-- Identity -->
<PackageVersion Include="Microsoft.AspNetCore.Identity" Version="2.3.1" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.11" />
<!-- App services -->
<PackageVersion Include="CsvHelper" Version="33.0.1" />
<PackageVersion Include="ClosedXML" Version="0.104.2" />
<PackageVersion Include="MailKit" Version="4.10.0" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection" Version="8.0.11" />
<PackageVersion Include="MediatR" Version="12.4.1" />
<PackageVersion Include="FluentValidation" Version="11.11.0" />
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.11.0" />
<PackageVersion Include="Mapster" Version="7.4.0" />
<PackageVersion Include="Mapster.DependencyInjection" Version="1.0.1" />
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
<!-- Background jobs -->
<PackageVersion Include="Hangfire.AspNetCore" Version="1.8.17" />
<PackageVersion Include="Hangfire.PostgreSql" Version="1.20.10" />
<!-- Observability / Prometheus -->
<PackageVersion Include="prometheus-net.AspNetCore" Version="8.2.1" />
<!-- POS: local storage + API client -->
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.11" />
<PackageVersion Include="Refit" Version="7.2.22" />
<PackageVersion Include="Refit.HttpClientFactory" Version="7.2.22" />
<PackageVersion Include="Polly" Version="8.5.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageVersion Include="System.IO.Ports" Version="8.0.0" />
<!-- Testing -->
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageVersion Include="FluentAssertions" Version="7.0.0" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.3" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.11" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.1.0" />
</ItemGroup>
</Project>