Files
satusehat-service/internal/infrastructure/monitoring/metrics.go
T
2026-04-14 01:23:34 +00:00

420 lines
12 KiB
Go

// File: /home/meninjar/goprint/service/internal/infrastructure/monitoring/metrics.go
package monitoring
import (
"time"
"service/pkg/logger"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
// Metrics defines all Prometheus metrics for the GoPrint service
type Metrics struct {
// HTTP metrics
HttpRequestsTotal *prometheus.CounterVec
HttpRequestDuration *prometheus.HistogramVec
HttpResponseSize *prometheus.HistogramVec
HttpRequestsInFlight prometheus.Gauge
// Database metrics
DbConnections prometheus.Gauge
DbQueryDuration *prometheus.HistogramVec
DbQueryErrors *prometheus.CounterVec
DbActiveConnections *prometheus.GaugeVec
// Cache metrics
CacheHits prometheus.Counter
CacheMisses prometheus.Counter
CacheOperations *prometheus.CounterVec
CacheDuration *prometheus.HistogramVec
// Business logic metrics
EthnicOperations *prometheus.CounterVec
AuthOperations *prometheus.CounterVec
PersonOperations *prometheus.CounterVec
// Service health metrics
ServiceUptime prometheus.Counter
ServiceHealthStatus prometheus.Gauge
ExternalServiceStatus *prometheus.GaugeVec
// Resource usage metrics
MemoryUsage prometheus.Gauge
CPUUsage prometheus.Gauge
Goroutines prometheus.Gauge
// Logger untuk monitoring
logger logger.Logger
serviceName string
startTime time.Time
}
// NewMetrics creates a new instance of Prometheus metrics
func NewMetrics(serviceName string) *Metrics {
log := logger.Default().WithFields(
logger.String("component", "monitoring"),
logger.String("service", serviceName),
)
labels := []string{"method", "endpoint", "status"}
dbLabels := []string{"operation", "table", "database"}
cacheLabels := []string{"operation", "result"}
businessLabels := []string{"operation", "status", "entity"}
externalLabels := []string{"service", "status"}
metrics := &Metrics{
serviceName: serviceName,
logger: log,
startTime: time.Now(),
// HTTP metrics
HttpRequestsTotal: promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
ConstLabels: prometheus.Labels{"service": serviceName},
},
labels,
),
HttpRequestDuration: promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP request duration in seconds",
ConstLabels: prometheus.Labels{"service": serviceName},
Buckets: prometheus.DefBuckets,
},
labels,
),
HttpResponseSize: promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_response_size_bytes",
Help: "HTTP response size in bytes",
ConstLabels: prometheus.Labels{"service": serviceName},
Buckets: prometheus.ExponentialBuckets(100, 10, 8),
},
labels,
),
HttpRequestsInFlight: promauto.NewGauge(
prometheus.GaugeOpts{
Name: "http_requests_in_flight",
Help: "Number of HTTP requests currently being processed",
ConstLabels: prometheus.Labels{"service": serviceName},
},
),
// Database metrics
DbConnections: promauto.NewGauge(
prometheus.GaugeOpts{
Name: "db_connections_active",
Help: "Number of active database connections",
ConstLabels: prometheus.Labels{"service": serviceName},
},
),
DbActiveConnections: promauto.NewGaugeVec(
prometheus.GaugeOpts{
Name: "db_connections_active_by_db",
Help: "Number of active database connections by database",
ConstLabels: prometheus.Labels{"service": serviceName},
},
[]string{"database"},
),
DbQueryDuration: promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "db_query_duration_seconds",
Help: "Database query duration in seconds",
ConstLabels: prometheus.Labels{"service": serviceName},
Buckets: prometheus.DefBuckets,
},
dbLabels,
),
DbQueryErrors: promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "db_query_errors_total",
Help: "Total number of database query errors",
ConstLabels: prometheus.Labels{"service": serviceName},
},
dbLabels,
),
// Cache metrics
CacheHits: promauto.NewCounter(
prometheus.CounterOpts{
Name: "cache_hits_total",
Help: "Total number of cache hits",
ConstLabels: prometheus.Labels{"service": serviceName},
},
),
CacheMisses: promauto.NewCounter(
prometheus.CounterOpts{
Name: "cache_misses_total",
Help: "Total number of cache misses",
ConstLabels: prometheus.Labels{"service": serviceName},
},
),
CacheOperations: promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "cache_operations_total",
Help: "Total number of cache operations",
ConstLabels: prometheus.Labels{"service": serviceName},
},
cacheLabels,
),
CacheDuration: promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "cache_operation_duration_seconds",
Help: "Cache operation duration in seconds",
ConstLabels: prometheus.Labels{"service": serviceName},
Buckets: prometheus.DefBuckets,
},
cacheLabels,
),
// Business logic metrics
EthnicOperations: promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "ethnic_operations_total",
Help: "Total number of ethnic operations",
ConstLabels: prometheus.Labels{"service": serviceName},
},
businessLabels,
),
AuthOperations: promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "auth_operations_total",
Help: "Total number of authentication operations",
ConstLabels: prometheus.Labels{"service": serviceName},
},
businessLabels,
),
PersonOperations: promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "person_operations_total",
Help: "Total number of person operations",
ConstLabels: prometheus.Labels{"service": serviceName},
},
businessLabels,
),
// Service health metrics
ServiceUptime: promauto.NewCounter(
prometheus.CounterOpts{
Name: "service_uptime_seconds",
Help: "Service uptime in seconds",
ConstLabels: prometheus.Labels{"service": serviceName},
},
),
ServiceHealthStatus: promauto.NewGauge(
prometheus.GaugeOpts{
Name: "service_health_status",
Help: "Service health status (1 = healthy, 0 = unhealthy)",
ConstLabels: prometheus.Labels{"service": serviceName},
},
),
ExternalServiceStatus: promauto.NewGaugeVec(
prometheus.GaugeOpts{
Name: "external_service_status",
Help: "External service status (1 = up, 0 = down)",
ConstLabels: prometheus.Labels{"service": serviceName},
},
externalLabels,
),
// Resource usage metrics
MemoryUsage: promauto.NewGauge(
prometheus.GaugeOpts{
Name: "memory_usage_bytes",
Help: "Memory usage in bytes",
ConstLabels: prometheus.Labels{"service": serviceName},
},
),
CPUUsage: promauto.NewGauge(
prometheus.GaugeOpts{
Name: "cpu_usage_percent",
Help: "CPU usage percentage",
ConstLabels: prometheus.Labels{"service": serviceName},
},
),
Goroutines: promauto.NewGauge(
prometheus.GaugeOpts{
Name: "goroutines_count",
Help: "Number of goroutines",
ConstLabels: prometheus.Labels{"service": serviceName},
},
),
}
log.Info("Prometheus metrics initialized successfully",
logger.String("service", serviceName),
)
return metrics
}
// RecordHTTPRequest records HTTP request metrics
func (m *Metrics) RecordHTTPRequest(method, endpoint, status string, duration time.Duration, responseSize int64) {
labels := prometheus.Labels{
"method": method,
"endpoint": endpoint,
"status": status,
}
m.HttpRequestsTotal.With(labels).Inc()
m.HttpRequestDuration.With(labels).Observe(duration.Seconds())
m.HttpResponseSize.With(labels).Observe(float64(responseSize))
m.logger.Debug("HTTP request recorded",
logger.String("method", method),
logger.String("endpoint", endpoint),
logger.String("status", status),
logger.Float64("duration_seconds", duration.Seconds()),
logger.Int64("response_size", responseSize),
)
}
// RecordDBQuery records database query metrics
func (m *Metrics) RecordDBQuery(operation, table, database string, duration time.Duration, err error) {
labels := prometheus.Labels{
"operation": operation,
"table": table,
"database": database,
}
m.DbQueryDuration.With(labels).Observe(duration.Seconds())
if err != nil {
m.DbQueryErrors.With(labels).Inc()
m.logger.Error("Database query error recorded",
logger.String("operation", operation),
logger.String("table", table),
logger.String("database", database),
logger.ErrorField(err),
)
} else {
m.logger.Debug("Database query recorded",
logger.String("operation", operation),
logger.String("table", table),
logger.String("database", database),
logger.Float64("duration_seconds", duration.Seconds()),
)
}
}
// RecordCacheOperation records cache operation metrics
func (m *Metrics) RecordCacheOperation(operation string, hit bool, duration time.Duration) {
if hit {
m.CacheHits.Inc()
} else {
m.CacheMisses.Inc()
}
result := "hit"
if !hit {
result = "miss"
}
labels := prometheus.Labels{
"operation": operation,
"result": result,
}
m.CacheOperations.With(labels).Inc()
m.CacheDuration.With(labels).Observe(duration.Seconds())
m.logger.Debug("Cache operation recorded",
logger.String("operation", operation),
logger.String("result", result),
logger.Float64("duration_seconds", duration.Seconds()),
)
}
// RecordBusinessOperation records business logic operation metrics
func (m *Metrics) RecordBusinessOperation(operationType, operation, status string) {
var counter *prometheus.CounterVec
switch operationType {
case "ethnic":
counter = m.EthnicOperations
case "auth":
counter = m.AuthOperations
case "person":
counter = m.PersonOperations
default:
m.logger.Warn("Unknown business operation type",
logger.String("type", operationType),
logger.String("operation", operation),
)
return
}
labels := prometheus.Labels{
"operation": operation,
"status": status,
"entity": operationType,
}
counter.With(labels).Inc()
m.logger.Debug("Business operation recorded",
logger.String("type", operationType),
logger.String("operation", operation),
logger.String("status", status),
)
}
// UpdateServiceHealth updates service health status
func (m *Metrics) UpdateServiceHealth(healthy bool) {
if healthy {
m.ServiceHealthStatus.Set(1)
m.logger.Info("Service health status updated to healthy")
} else {
m.ServiceHealthStatus.Set(0)
m.logger.Error("Service health status updated to unhealthy")
}
}
// UpdateExternalServiceStatus updates external service status
func (m *Metrics) UpdateExternalServiceStatus(service string, status bool) {
value := 0.0
statusStr := "down"
if status {
value = 1.0
statusStr = "up"
}
labels := prometheus.Labels{
"service": service,
"status": statusStr,
}
m.ExternalServiceStatus.With(labels).Set(value)
m.logger.Info("External service status updated",
logger.String("service", service),
logger.String("status", statusStr),
)
}
// UpdateResourceUsage updates resource usage metrics
func (m *Metrics) UpdateResourceUsage(memory, cpu float64, goroutines int) {
m.MemoryUsage.Set(memory)
m.CPUUsage.Set(cpu)
m.Goroutines.Set(float64(goroutines))
m.logger.Debug("Resource usage updated",
logger.Float64("memory_mb", memory),
logger.Float64("cpu_percent", cpu),
logger.Int("goroutines", goroutines),
)
}
// IncrementUptime increments service uptime
func (m *Metrics) IncrementUptime(seconds float64) {
m.ServiceUptime.Add(seconds)
}
// GetUptime returns current uptime in seconds
func (m *Metrics) GetUptime() float64 {
return time.Since(m.startTime).Seconds()
}