Skip to content

feat(pkg): add Service Discovery metrics via MetricsFactory#2196

Merged
andreimatiazi merged 8 commits into
developfrom
feature/sd-metrics
Jul 10, 2026
Merged

feat(pkg): add Service Discovery metrics via MetricsFactory#2196
andreimatiazi merged 8 commits into
developfrom
feature/sd-metrics

Conversation

@andreimatiazi

Copy link
Copy Markdown
Contributor

Description

Adds Service Discovery metrics via the existing lib-observability MetricsFactory, in the shared pkg/servicediscovery, wired into both the ledger and crm bootstraps. Follow-up to the SD adoption (#2191).

No-op when Service Discovery is disabled (SD_ENABLED=false, the default) — zero overhead, no metric registration. Emission is a side-channel: recording failures are logged at Warn and never affect the request/boot path.

Metrics emitted (bounded label cardinality — no host/address/error-detail labels):

Metric Type Labels Unit
sd_register_total Counter — (registration initiations) 1
sd_deregister_total Counter result (ok|error) 1
sd_resolve_total Counter service, result (resolved|fallback|error) 1
sd_resolve_duration_milliseconds Histogram service ms

Design notes:

  • A MetricsRecorder interface (decoupled from OTel) + a NopMetricsRecorder default keep the SD call sites unit-testable and zero-cost when disabled; a MetricsFactory-backed recorder satisfies it.
  • sd_register_total counts registration initiations only — RegisterAsync is fire-and-forget with no success hook, so a success result would be dishonest.
  • ResolveAuthHost now passes an empty fallback to the registry so a consul hit, a static fallback, and an error are distinguishable (resolved/fallback/error). The returned auth host is byte-identical to prior behavior.
  • The resolve histogram sets explicit millisecond buckets — the factory's name-based auto-bucketing yields seconds-scaled buckets for *duration* metrics, which would otherwise send ms values to the overflow bucket.

Type of Change

  • feat: New feature or capability

Breaking Changes

None. Opt-in / no-op by default; the auth-host resolution result is byte-identical to prior behavior.

Testing

  • make test passes
  • make test-int passes (except 1 pre-existing, unrelated failure: TestIntegration_AuditRepo_Create_BuildsAllSixIndexes in crm/mongodb/audit, a mongo-driver-v2 bson-decode issue — verified failing identically on clean develop; that package is not in this diff)
  • make lint passes (golangci-lint v2.4.0, 0 issues)
  • go vet / go mod verify clean
  • e2e (Postman collection) run against a rebuilt stack — no regression from this branch (diff touches no HTTP/route/handler/DTO code)

Test evidence / Actions run: Full Ring dev-cycle — Gate 0 (TDD RED→GREEN, coverage 88.9–91.1%), Gate 8 (10 reviewers incl. obs), Gate 9 (acceptance).

Architectural Checklist

  • No panic() in production paths
  • Errors wrapped with %w
  • Handlers stay thin (parse, validate, call service)
  • Infrastructure concerns kept in internal/bootstrap

Related Issues

Closes #

Add a MetricsRecorder interface (decoupled from OTel), a no-op default, and an orNop nil-guard in pkg/servicediscovery, to be satisfied by a MetricsFactory-backed recorder and wired into the SD call sites in follow-up tasks.

X-Lerian-Ref: 0x1
Implement MetricsRecorder over lib-observability MetricsFactory, emitting sd_register_total, sd_deregister_total{result}, sd_resolve_total{service,result} and sd_resolve_duration_milliseconds{service}. A nil factory yields the no-op recorder; record/build errors are logged at Warn and never propagated.

X-Lerian-Ref: 0x1
sd_resolve_duration_milliseconds declared unit ms but relied on the factory's name-based bucket auto-selection, which returns seconds-scaled DefaultLatencyBuckets for duration metrics, sending ms values to the overflow bucket. Set explicit ms bucket boundaries. Strengthen the recorder tests to observe emitted metric names, values, labels and histogram bounds via a manual reader, and track stub invocations per method.

X-Lerian-Ref: 0x1
NewRunnable takes a MetricsRecorder; Run records one register-initiated on RegisterAsync and a deregister{result} outcome on shutdown. Call sites pass nil for now (guarded by orNop) until the bootstraps thread the real recorder.

X-Lerian-Ref: 0x1
Refactor ResolveAuthHost to pass an empty fallback to the registry so a consul hit, a static fallback and an error are distinguishable, and record sd_resolve_total{service,result} plus sd_resolve_duration_milliseconds{service}. The returned auth host is byte-identical to prior behavior. Call sites pass nil until the bootstraps thread the real recorder.

X-Lerian-Ref: 0x1
Build the Service Discovery metrics recorder gated on SD-enabled (no-op when disabled) and thread it through wireServiceDiscovery/ResolveAuthHost and NewRunnable, so register/deregister/resolve metrics flow only when discovery is on. Extract metricsFactory before the SD wiring.

X-Lerian-Ref: 0x1
Build the Service Discovery metrics recorder gated on SD-enabled (no-op when disabled) and thread it through wireServiceDiscovery/ResolveAuthHost and NewRunnable, so register/deregister/resolve metrics flow only when discovery is on. Move telemetry init above the SD wiring, preserving the SD-before-streaming ordering.

X-Lerian-Ref: 0x1
@andreimatiazi
andreimatiazi requested a review from a team as a code owner July 9, 2026 00:37
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3b3ae769-ff73-485d-bed9-5ae5424ab915

📥 Commits

Reviewing files that changed from the base of the PR and between e8ee9cb and 9e3b876.

📒 Files selected for processing (2)
  • components/crm/internal/bootstrap/config.go
  • components/ledger/internal/bootstrap/config.go

📝 Walkthrough

Walkthrough

Adds service-discovery metrics primitives, records resolve and lifecycle outcomes, and threads a recorder through CRM and Ledger bootstrap wiring into the discovery runnable.

Changes

Service discovery metrics instrumentation

Layer / File(s) Summary
Core recorder interface and no-op implementation
pkg/servicediscovery/metrics.go, pkg/servicediscovery/metrics_test.go
Adds result constants, MetricsRecorder, NopMetricsRecorder, and orNop, with tests for the interface and helper behavior.
OTel-backed metrics recorder
pkg/servicediscovery/metrics_factory.go, pkg/servicediscovery/metrics_factory_test.go
Adds metric descriptors and an OpenTelemetry-backed recorder for register, deregister, and resolve outcomes, with tests for emission, descriptors, buckets, and logging behavior.
ResolveAuthHost metrics-aware classification
pkg/servicediscovery/auth_host_resolver.go, pkg/servicediscovery/auth_host_resolver_test.go
ResolveAuthHost now accepts a MetricsRecorder, measures resolution duration, classifies outcomes, and records the result.
Runnable lifecycle metrics
pkg/servicediscovery/runnable.go, pkg/servicediscovery/runnable_test.go
NewRunnable accepts a MetricsRecorder, and Run records registration initiation and deregistration outcomes.
CRM bootstrap wiring
components/crm/internal/bootstrap/config.go, components/crm/internal/bootstrap/service.go, components/crm/internal/bootstrap/service_discovery_test.go
Threads a metrics factory through CRM service-discovery wiring, exposes the recorder on Service, passes it into the runnable, and updates the bootstrap tests.
Ledger bootstrap wiring
components/ledger/internal/bootstrap/config.go, components/ledger/internal/bootstrap/service.go, components/ledger/internal/bootstrap/service_discovery_test.go
Threads a metrics factory through Ledger service-discovery wiring, exposes the recorder on Service, passes it into the runnable, and updates the bootstrap tests.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feature/sd-metrics

Comment @coderabbitai help to get the list of available commands.

@lerian-studio

Copy link
Copy Markdown
Contributor

Lerian Library Version Check

Library Current Latest Status
lib-auth/v2 v2.9.0 v2.9.0 Current
lib-commons/v5 v5.8.0 v5.8.0 Current
lib-observability v1.1.0 v1.1.0 Current
lib-service-discovery v0.6.0 v0.6.0 Current
lib-streaming v1.7.0 v1.7.0 Current

0 outdated | 5 current | 0 skipped | 0 unknown

@lerian-studio lerian-studio added size/XL PR changes >= 1000 lines area: tests Unit, integration and end-to-end tests area: ledger Ledger component area: pkg Reusable public packages area: crm CRM component labels Jul 9, 2026
@lerian-studio

lerian-studio commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Validation Summary

✅ PR Mergeable — no blocking failures

Check Status Blocking
Source Branch ✅ success yes
PR Title ✅ success yes
PR Description ✅ success yes
PR Size ✅ success no
Auto Labels ✅ success no
PR Metadata ✅ success no

🔍 View workflow run

@lerian-studio

lerian-studio commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🔒 Security Scan Results — crm

✅ PR Mergeable — no blocking findings

Stage Status Blocking?
Filesystem Scan ✅ Clean
Docker Image Scan ✅ Clean
Docker Hub Health Score ✅ Clean
Pre-release Version Check ⚠️ 1 finding 🟡 No (advisory)

Trivy

Filesystem Scan

✅ No vulnerabilities or secrets found.

Docker Image Scan

✅ No vulnerabilities found.


Docker Hub Health Score Compliance

✅ Policies — 4/4 met

Policy Status
Default non-root user ✅ Passed
No fixable critical/high CVEs ✅ Passed
No high-profile vulnerabilities ✅ Passed
No AGPL v3 licenses ✅ Passed

Pre-release Version Check

🚫 Found 1 unstable version pin(s). Only stable releases (x.y.z) and SHA-based pins are allowed.

File Line Content
./go.mod 152 github.com/hashicorp/hcl v1.0.1-vault-7 // indirect

Replace pre-release suffixes (-alpha, -beta, -rc, -dev, etc.) with stable releases.


🔍 View full scan logs

@lerian-studio

lerian-studio commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

📊 Unit Test Coverage Report: midaz-ledger

Metric Value
Overall Coverage 85.3% ✅ PASS
Threshold 80%

Coverage by Package

Package Coverage
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/http/in 86.2%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/mongodb/onboarding 66.7%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/mongodb/transaction 66.7%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/account 100.0%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/accounttype 66.7%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/asset 100.0%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/assetrate 100.0%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/balance 97.5%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/ledger 100.0%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/operation 90.7%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/operationroute 100.0%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/organization 100.0%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/portfolio 100.0%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/segment 100.0%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/transaction 96.6%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/postgres/transactionroute 100.0%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/rabbitmq 91.5%
github.com/LerianStudio/midaz/v3/components/ledger/internal/adapters/redis/transaction/balance 99.0%
github.com/LerianStudio/midaz/v3/components/ledger/internal/services/command 85.9%
github.com/LerianStudio/midaz/v3/components/ledger/internal/services/query 93.2%
github.com/LerianStudio/midaz/v3/components/ledger/internal/services 0.0%

Generated by Go PR Analysis workflow

@lerian-studio

Copy link
Copy Markdown
Contributor

📊 Unit Test Coverage Report: midaz-crm

Metric Value
Overall Coverage 89.2% ✅ PASS
Threshold 80%

Coverage by Package

Package Coverage
github.com/LerianStudio/midaz/v3/components/crm/internal/adapters/http/in 88.5%
github.com/LerianStudio/midaz/v3/components/crm/internal/adapters/mongodb/alias 73.5%
github.com/LerianStudio/midaz/v3/components/crm/internal/adapters/mongodb/audit 71.4%
github.com/LerianStudio/midaz/v3/components/crm/internal/adapters/mongodb/encryption 100.0%
github.com/LerianStudio/midaz/v3/components/crm/internal/adapters/mongodb/holder 78.7%
github.com/LerianStudio/midaz/v3/components/crm/internal/services/encryption 94.5%
github.com/LerianStudio/midaz/v3/components/crm/internal/services 97.0%

Generated by Go PR Analysis workflow

@lerian-studio

lerian-studio commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🔒 Security Scan Results — ledger

✅ PR Mergeable — no blocking findings

Stage Status Blocking?
Filesystem Scan ✅ Clean
Docker Image Scan ✅ Clean
Docker Hub Health Score ✅ Clean
Pre-release Version Check ⚠️ 1 finding 🟡 No (advisory)

Trivy

Filesystem Scan

✅ No vulnerabilities or secrets found.

Docker Image Scan

✅ No vulnerabilities found.


Docker Hub Health Score Compliance

✅ Policies — 4/4 met

Policy Status
Default non-root user ✅ Passed
No fixable critical/high CVEs ✅ Passed
No high-profile vulnerabilities ✅ Passed
No AGPL v3 licenses ✅ Passed

Pre-release Version Check

🚫 Found 1 unstable version pin(s). Only stable releases (x.y.z) and SHA-based pins are allowed.

File Line Content
./go.mod 152 github.com/hashicorp/hcl v1.0.1-vault-7 // indirect

Replace pre-release suffixes (-alpha, -beta, -rc, -dev, etc.) with stable releases.


🔍 View full scan logs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/crm/internal/bootstrap/config.go`:
- Around line 312-328: The comment above wireServiceDiscovery is describing
internal behavior of NewMetricsFactoryRecorder rather than the contract of this
function. Rewrite the comment to focus only on what wireServiceDiscovery
guarantees about metricsRecorder selection based on discovery enabled/disabled,
and remove the parenthetical about nil factory fallback; keep the reference to
wireServiceDiscovery and pkgsd.NewMetricsFactoryRecorder limited to the
observable behavior at this call site.

In `@components/ledger/internal/bootstrap/config.go`:
- Around line 856-872: The comment in wireServiceDiscovery should not describe
the internal behavior of NewMetricsFactoryRecorder or other downstream
dependencies. Rewrite it to focus only on what this function does: build the
manager, choose a no-op recorder when discovery is disabled, and use the metrics
factory path when enabled. Keep the reference to pkgsd.BuildManager and
pkgsd.NewMetricsFactoryRecorder, but remove the call-graph-style parenthetical
about a nil factory degrading to no-op.

In `@pkg/servicediscovery/metrics_factory_test.go`:
- Around line 386-403: The test in TestSDResolveDurationBuckets is duplicating
the bucket slice literal that already exists as sdResolveDurationMsBuckets;
update the assertion to compare sdResolveDurationMs.Buckets against that shared
package variable instead of a re-declared literal. Keep the counter-descriptor
nil-bucket checks as-is, and use the existing symbol sdResolveDurationMsBuckets
so the expected values stay in sync automatically.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9ad86564-928e-42e0-b948-37af6f0a0be6

📥 Commits

Reviewing files that changed from the base of the PR and between 6f65f2d and e8ee9cb.

📒 Files selected for processing (14)
  • components/crm/internal/bootstrap/config.go
  • components/crm/internal/bootstrap/service.go
  • components/crm/internal/bootstrap/service_discovery_test.go
  • components/ledger/internal/bootstrap/config.go
  • components/ledger/internal/bootstrap/service.go
  • components/ledger/internal/bootstrap/service_discovery_test.go
  • pkg/servicediscovery/auth_host_resolver.go
  • pkg/servicediscovery/auth_host_resolver_test.go
  • pkg/servicediscovery/metrics.go
  • pkg/servicediscovery/metrics_factory.go
  • pkg/servicediscovery/metrics_factory_test.go
  • pkg/servicediscovery/metrics_test.go
  • pkg/servicediscovery/runnable.go
  • pkg/servicediscovery/runnable_test.go

Comment thread components/crm/internal/bootstrap/config.go
Comment thread components/ledger/internal/bootstrap/config.go
Comment thread pkg/servicediscovery/metrics_factory_test.go
The wireServiceDiscovery comment described NewMetricsFactoryRecorder's internal nil-factory fallback; keep the comment to the function's own contract per the repo comment guidelines.

X-Lerian-Ref: 0x1
@andreimatiazi
andreimatiazi merged commit 98fac32 into develop Jul 10, 2026
33 checks passed
@andreimatiazi
andreimatiazi deleted the feature/sd-metrics branch July 10, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: crm CRM component area: ledger Ledger component area: pkg Reusable public packages area: tests Unit, integration and end-to-end tests size/XL PR changes >= 1000 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants