-
Notifications
You must be signed in to change notification settings - Fork 0
feat(metrics): native RED/runtime metrics + DB/cache/queue instrumentation helpers #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
3d29125
feat(metrics): native RED/runtime metrics + DB/cache/queue instrument…
gauchito91 d9755d0
refactor(core)!: decouple Fiber v3 from tracing core, gRPC and system…
gauchito91 6b544a2
fix(deps,docs): bump grpc for CVE + clarify sqlobs pool + gRPC doc ref
gauchito91 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| # O que está subindo — Native Metrics + Auto-Instrumentation (lib-observability) | ||
|
|
||
| > Registro vivo do que este trabalho adiciona à lib. Vira o corpo do PR único p/ develop. | ||
| > Branch: `feat/native-metrics-phase1` (base develop pós-#30). Atualizar a cada feature. | ||
| > Ref: pre-dev `docs/pre-dev/observability-metrics-standardization/` · contrato `docs/metrics-contract.md` | ||
|
|
||
| ## Objetivo | ||
| Padronizar a emissão de métricas transversais (a lib emite por default, nome/unidade/labels iguais p/ todo serviço) e oferecer helpers de auto-instrumentação de DB/cache/fila — para acabar com a bagunça de métricas por-app e aposentar spans manuais de infra (origem de ~27k séries de alta cardinalidade). | ||
|
|
||
| ## Não confundir | ||
| - **PR #30 (já mergeado)** = fix de span.name (route template). Pré-requisito, NÃO faz parte deste trabalho. | ||
| - **Adoção nas apps (midaz/ledger)** = outro repo, outro PR (Fase 3). Este PR é SÓ a lib. | ||
|
|
||
| --- | ||
|
|
||
| ## FASE 1 — Métricas nativas (✅ implementada, testada) | ||
|
|
||
| | Feature | Métrica | Tipo | Detalhe | | ||
| |---|---|---|---| | ||
| | T1 Contrato | — | doc | `docs/metrics-contract.md`: nomes/unidade(s)/buckets/labels permitidos×proibidos/no-op. Fundação. | | ||
| | T2 RED gRPC server | `rpc.server.duration` | Float64 Histogram (s) | hook no interceptor server existente. Labels: rpc.system=grpc, rpc.method, rpc.grpc.status_code, error.type (só !=OK), tenant.id | | ||
| | T2 RED gRPC client | `rpc.client.duration` | Float64 Histogram (s) | NOVO UnaryClientInterceptor (não existia). Labels iguais, sem tenant.id. Propaga trace. | | ||
| | T3 Runtime Go | `go.*` (memory/goroutine/gc) | contrib/runtime | opt-in via `TelemetryConfig.EnableRuntimeMetrics` (default-off). MinReadMemStats 15s. | | ||
| | T4 In-flight HTTP | `http.server.active_requests` | Int64 UpDownCounter ({request}) | inc antes / dec depois. Label http.request.method. Detecta saturação. | | ||
|
|
||
| Arquivos: middleware/telemetry.go, tracing/otel.go, go.mod (+contrib/instrumentation/runtime v0.69.0), 3 test files (16 testes). Verificado: go test unit PASS, vet OK, lint 0, ManualReader (emissão real), zero label proibido, no-op safe. | ||
|
|
||
| ## DESACOPLAMENTO Fiber v3 ↔ core (✅ implementado, testado) — BREAKING | ||
|
|
||
| Objetivo: tirar a dependência de `github.com/gofiber/fiber/v3` do núcleo da lib para que apps ainda em **Fiber v2** possam usar TUDO menos o middleware HTTP (core `NewTelemetry`, runtime metrics, messaging, gRPC, DB/cache). | ||
|
|
||
| Causa raiz: `tracing/otel.go` importava `fiber/v3` só por 2 helpers HTTP. Isso contaminava o pacote `tracing` inteiro e, transitivamente, `messagingobs` (importa tracing), `runtime` e os interceptors gRPC — `go list -deps ./messagingobs` mostrava 13 deps de fiber. | ||
|
|
||
| ### O que mudou | ||
| - **`tracing` agora é fiber-free.** Removido o import de `fiber/v3` (e `redaction`/`observability`, que só existiam por causa das 2 funções movidas). | ||
| - **Novo pacote `grpcmiddleware/`** (fiber-free): interceptors gRPC saíram de `middleware` (que importa fiber). HTTP (`WithTelemetry`/`EndTracingSpans`) fica onde estava (fiber, correto). | ||
| - **Novo pacote `telemetrycore/`** (fiber-free): coletor de métricas de sistema (singleton único), compartilhado por HTTP e gRPC — evita duas goroutines de coleta quando a app usa os dois transportes. | ||
|
|
||
| ### Símbolos movidos (BREAKING — ajustar import path nos callers) | ||
| | Símbolo | Antes | Depois | | ||
| |---|---|---| | ||
| | `SetSpanAttributeForParam(c fiber.Ctx, ...)` | `tracing` (`.../v2/tracing`) | `middleware` (`.../v2/middleware`) | | ||
| | `ExtractHTTPContext(ctx, c fiber.Ctx)` | `tracing` | `middleware` | | ||
| | `WithTelemetryInterceptor` (gRPC) | `middleware.TelemetryMiddleware` | `grpcmiddleware.TelemetryMiddleware` | | ||
| | `EndTracingSpansInterceptor` (gRPC) | `middleware.TelemetryMiddleware` | `grpcmiddleware.TelemetryMiddleware` | | ||
| | `UnaryClientInterceptor` (gRPC) | `middleware.TelemetryMiddleware` | `grpcmiddleware.TelemetryMiddleware` | | ||
| | `ResolveTenantIDFromGRPC` | `middleware` (mantido lá tb.) | também em `grpcmiddleware` | | ||
| | `StopMetricsCollector` / `DefaultMetricsCollectionInterval` | `middleware` (mantidos p/ compat) | fonte agora em `telemetrycore` | | ||
|
|
||
| Novo construtor gRPC: `grpcmiddleware.NewTelemetryMiddleware(tl)` (mesma assinatura de `middleware.NewTelemetryMiddleware`). | ||
|
|
||
| ### Fix externo pendente (midaz — outro repo, outro PR) | ||
| `midaz` `pkg/net/http/withBody.go:235` usa `SetSpanAttributeForParam` importando de `tracing`. Trocar para o pacote `middleware`: | ||
| `github.com/LerianStudio/lib-observability/v2/middleware.SetSpanAttributeForParam`. | ||
| (Apps que consomem os interceptors gRPC via `middleware` também precisam trocar para `grpcmiddleware`.) | ||
|
|
||
| ### Verificação (`go list -deps`, deps de gofiber) | ||
| | Pacote | Antes | Depois | | ||
| |---|---|---| | ||
| | `tracing` | 13 | **0** | | ||
| | `messagingobs` | 13 | **0** | | ||
| | `runtime` | 0 | 0 | | ||
| | `grpcmiddleware` (gRPC) | (era `middleware`=13) | **0** | | ||
| | `telemetrycore` | — | **0** | | ||
| | `middleware` (HTTP `WithTelemetry`) | 13 | 13 (correto, inalterado) | | ||
|
|
||
| Comportamento 100% preservado (só MOVE código; nenhuma lógica de métrica/telemetria alterada). `go test -tags=unit ./...` PASS em todos os pacotes; golangci-lint (wsl_v5) 0 issues. Core OTel mantido em v1.44.0. | ||
|
|
||
| ## FASE 2 — Wrappers de auto-instrumentação (a implementar) | ||
|
|
||
| | Feature | Métrica | Helper | Cobre | | ||
| |---|---|---|---| | ||
| | SQL | `db.client.operation.duration` (s) | `InstrumentSQLDB(*sql.DB)` (otelsql v0.43.0) | Postgres + MySQL/MariaDB (database/sql) | | ||
| | Cache | `db.client.operation.duration` (s) | `WrapRedis(client)` (redisotel v9.17.2) | Redis + Valkey (mesmo driver go-redis) | | ||
| | ~~Doc-DB (Mongo)~~ | — | — | **ADIADO** — otelmongo v2 sem release oficial (só pseudo-version que arrastaria otel core >v1.44.0); v1 deprecated. Entra quando v2 for tagueado. Ver BACKLOG. | | ||
| | Mensageria | `messaging.client.operation.duration` / `messaging.process.duration` (s) | InstrumentPublish/Consume (hand-roll s/ helpers de propagação existentes) | RabbitMQ (contrato compartilhado c/ lib-streaming p/ RedPanda) | | ||
|
|
||
| Guardrails (todos): unidade s, sem query text/params/PII como label (contrato §PROIBIDO), no-op safe, instrumento 1×. | ||
| Boundary: lib expõe HELPER; conexão/dbresolver fica na app/lib-commons. Para SQL, aplicar em cada *sql.DB ANTES do dbresolver. | ||
|
|
||
| ## FORA deste PR | ||
| - Adoção no ledger + aposentar spans manuais (Fase 3, repo midaz). | ||
| - Dashboard genérico + rollout (Fase 4). | ||
| - Business metrics (skill Ring dev-sre, frente paralela). | ||
| - IBM MQ (backlog isolado). RedPanda/Kafka (lib-streaming, repo separado). | ||
|
|
||
| ## BACKLOG (fora deste PR, rastreado) | ||
| - **Mongo helper (`mongoobs`):** implementar quando `otelmongo v2` (`go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/v2/mongo/otelmongo`) tiver release oficial que resolva p/ otel core v1.44.x. Hoje: v2 sem tag (só pseudo-version → arrastaria core p/ pré-release, viola pin); v1 v0.69.0 existe mas deprecated + driver v1. Padrão: mirror dos outros helpers, `SetMonitor` no `*options.ClientOptions` v2. Verificado no proxy Go 2026-07-22. | ||
|
|
||
| ## Riscos registrados | ||
| - **otelmongo v2** exige mongo-driver v2 na APP — não na lib (a lib só provê o helper). Migração do driver é Fase 3. | ||
| - **gRPC naming:** mantido experimental `rpc.server.duration`+`rpc.grpc.status_code` p/ consistir c/ span; revisar em lockstep quando semconv migrar. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| # Metric Contract — lib-observability | ||
|
|
||
| > Fonte canônica dos nomes/unidade/buckets/labels das métricas emitidas pela lib. | ||
| > Toda métrica nativa DEVE seguir este contrato. Testes validam contra ele. | ||
| > Base: OpenTelemetry Semantic Conventions. Ver pre-dev `docs/pre-dev/observability-metrics-standardization/`. | ||
|
|
||
| ## Princípios | ||
|
|
||
| 1. **Unidade = segundos (`s`)** para toda duração (semconv). NUNCA milissegundos na lib. | ||
| - Leitura em ms é responsabilidade do PAINEL: no Grafana, unidade do campo = `seconds (s)` → formata "50 ms"/"1.2 s" automaticamente. Fallback em query: `... * 1000` (fator 1000, multiplica; NÃO converte o label `le` de heatmap/bucket). | ||
| - NÃO dual-emitir (ms+s) na lib. NÃO converter no collector. Compat legacy de dashboards antigos migra p/ unidade-do-painel. | ||
| 2. **Instrumento criado UMA vez** na construção (nunca por request). Record é **no-op** quando o instrumento é nil (telemetria desabilitada) — chamável incondicionalmente, nunca panic, nunca afeta o request path. | ||
| 3. **Só labels de baixa cardinalidade bounded.** Ver lista PROIBIDA abaixo. Cada valor distinto de um label multiplica séries. | ||
| 4. **Nomes = OTel semconv estável.** Não inventar chaves; reusar `constants/opentelemetry.go`. | ||
| 5. **tenant.id** auto em RED (HTTP/gRPC) via resolver existente; MANUAL em métrica de negócio. | ||
|
|
||
| ## Buckets advisory (por sinal) | ||
|
|
||
| | Sinal | Buckets (segundos) | | ||
| |---|---| | ||
| | HTTP / RPC / Messaging | `0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10` | | ||
| | Database | `0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10` (mais fino no low-end) | | ||
|
|
||
| ## Catálogo (nome / tipo / unidade / labels) | ||
|
|
||
| | Métrica | Tipo | Unidade | Labels permitidos | Estabilidade | | ||
| |---|---|---|---|---| | ||
| | `http.server.request.duration` | Histogram | s | http.request.method, http.response.status_code, http.route, error.type, tenant.id | STABLE (já existe) | | ||
| | `http.client.request.duration` | Histogram | s | http.request.method, http.response.status_code, server.address, error.type | STABLE (já existe) | | ||
| | `http.server.active_requests` | UpDownCounter | {request} | http.request.method, http.route (opcional) | (T4) | | ||
| | `rpc.server.duration`¹ | Histogram | s | rpc.system, rpc.method, rpc.grpc.status_code, error.type, tenant.id | (T2) | | ||
| | `rpc.client.duration`¹ | Histogram | s | rpc.system, rpc.method, rpc.grpc.status_code, error.type | (T2) | | ||
| | `db.client.operation.duration` | Histogram | s | db.system.name, db.operation.name, db.collection.name, db.namespace, error.type | STABLE (Fase 2) | | ||
| | `messaging.client.operation.duration` (produce) | Histogram | s | messaging.system, messaging.operation.name, messaging.destination.template, error.type | (Fase 2) | | ||
| | `messaging.process.duration` (consume) | Histogram | s | messaging.system, messaging.operation.name, messaging.destination.template, messaging.consumer.group.name, error.type | (Fase 2) | | ||
| | `go.*` (runtime) | Gauge/Counter/Hist | várias | (dimensões fixas do contrib/runtime) | (T3) | | ||
|
|
||
| ¹ **Nota gRPC (validar na T2):** o train contrib atual pode emitir `rpc.server.duration` (experimental, este contrato) ou `rpc.server.call.duration` (RC, semconv). Confirmar o nome contra o pacote pinado no momento da T2 e alinhar. Manter `rpc.grpc.status_code` OU `rpc.response.status_code` conforme o que o interceptor da lib emite (hoje o span usa `rpc.grpc.status_code`, telemetry.go:549). | ||
|
|
||
| ## PROIBIDO como label (PII / cardinalidade ilimitada) | ||
|
|
||
| - query text / SQL / bind params / valores de coluna | ||
| - `db.query.text` → só em SPAN, opt-in; NUNCA em métrica | ||
| - routing key / message id / partition com id | ||
| - `url.path` com id/uuid; path resolvido concreto (span.name — raiz do problema original) | ||
| - pix key, document (cpf/cnpj), email, qualquer PII | ||
| - request/response payload | ||
|
|
||
| ## Habilitação / no-op | ||
|
|
||
| - Métrica emitida quando telemetria habilitada (provider+MetricsFactory não-nil — sinal existente na lib) e o subsistema ligado. | ||
| - Toggles opt-in em `TelemetryConfig` p/ subsistemas novos (runtime, db-instrumentation), default-safe, degradam p/ no-op quando off. Nunca erro que quebre a app. | ||
|
|
||
| ## Referências | ||
| - Padrão de implementação (template): `middleware/telemetry.go` — `newHTTPServerDurationHistogram` (:45-61), `recordHTTPServerDuration` (:386+). | ||
| - semconv: opentelemetry.io/docs/specs/semconv/{http,rpc,database,messaging}/* | ||
| - Decisões: `docs/pre-dev/observability-metrics-standardization/trd.md` (ADRs), `dependency-map.md` (versões). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.