Files
sendico/api/ledger/internal/service/ledger/metrics.go
Stephan D 62a6631b9a
All checks were successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
service backend
2025-11-07 18:35:26 +01:00

145 lines
4.1 KiB
Go

package ledger
import (
"sync"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var (
metricsOnce sync.Once
// Journal entry operations
journalEntriesTotal *prometheus.CounterVec
journalEntryLatency *prometheus.HistogramVec
journalEntryErrors *prometheus.CounterVec
// Balance operations
balanceQueriesTotal *prometheus.CounterVec
balanceQueryLatency *prometheus.HistogramVec
// Transaction amounts
transactionAmounts *prometheus.HistogramVec
// Account operations
accountOperationsTotal *prometheus.CounterVec
// Idempotency
duplicateRequestsTotal *prometheus.CounterVec
)
func initMetrics() {
metricsOnce.Do(func() {
// Journal entries posted by type
journalEntriesTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "ledger_journal_entries_total",
Help: "Total number of journal entries posted to the ledger",
},
[]string{"entry_type", "status"}, // entry_type: credit, debit, transfer, fx, fee, adjust, reverse
)
// Journal entry processing latency
journalEntryLatency = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "ledger_journal_entry_duration_seconds",
Help: "Duration of journal entry posting operations",
Buckets: []float64{.001, .005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10},
},
[]string{"entry_type"},
)
// Journal entry errors by type
journalEntryErrors = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "ledger_journal_entry_errors_total",
Help: "Total number of journal entry posting errors",
},
[]string{"entry_type", "error_type"}, // error_type: validation, insufficient_funds, db_error, etc.
)
// Balance queries
balanceQueriesTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "ledger_balance_queries_total",
Help: "Total number of balance queries",
},
[]string{"status"}, // success, error
)
// Balance query latency
balanceQueryLatency = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "ledger_balance_query_duration_seconds",
Help: "Duration of balance query operations",
Buckets: prometheus.DefBuckets,
},
[]string{"status"},
)
// Transaction amounts (in normalized form)
transactionAmounts = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "ledger_transaction_amount",
Help: "Distribution of transaction amounts",
Buckets: []float64{1, 10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000},
},
[]string{"currency", "entry_type"},
)
// Account operations
accountOperationsTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "ledger_account_operations_total",
Help: "Total number of account-level operations",
},
[]string{"operation", "status"}, // operation: create, freeze, unfreeze
)
// Duplicate/idempotent requests
duplicateRequestsTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "ledger_duplicate_requests_total",
Help: "Total number of duplicate requests detected via idempotency keys",
},
[]string{"entry_type"},
)
})
}
// Metric recording helpers
func recordJournalEntry(entryType, status string, durationSeconds float64) {
initMetrics()
journalEntriesTotal.WithLabelValues(entryType, status).Inc()
journalEntryLatency.WithLabelValues(entryType).Observe(durationSeconds)
}
func recordJournalEntryError(entryType, errorType string) {
initMetrics()
journalEntryErrors.WithLabelValues(entryType, errorType).Inc()
journalEntriesTotal.WithLabelValues(entryType, "error").Inc()
}
func recordBalanceQuery(status string, durationSeconds float64) {
initMetrics()
balanceQueriesTotal.WithLabelValues(status).Inc()
balanceQueryLatency.WithLabelValues(status).Observe(durationSeconds)
}
func recordTransactionAmount(currency, entryType string, amount float64) {
initMetrics()
transactionAmounts.WithLabelValues(currency, entryType).Observe(amount)
}
func recordAccountOperation(operation, status string) {
initMetrics()
accountOperationsTotal.WithLabelValues(operation, status).Inc()
}
func recordDuplicateRequest(entryType string) {
initMetrics()
duplicateRequestsTotal.WithLabelValues(entryType).Inc()
}