118 lines
2.9 KiB
Go
118 lines
2.9 KiB
Go
package handler
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/beckn-one/beckn-onix/pkg/telemetry"
|
|
"go.opentelemetry.io/otel"
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/metric"
|
|
)
|
|
|
|
type HTTPMetrics struct {
|
|
HttpRequestCount metric.Int64Counter
|
|
}
|
|
|
|
var (
|
|
httpMetricsInstance *HTTPMetrics
|
|
httpMetricsOnce sync.Once
|
|
httpMetricsErr error
|
|
)
|
|
|
|
func newHTTPMetrics() (*HTTPMetrics, error) {
|
|
|
|
meter := otel.GetMeterProvider().Meter(telemetry.ScopeName,
|
|
metric.WithInstrumentationVersion(telemetry.ScopeVersion))
|
|
m := &HTTPMetrics{}
|
|
var err error
|
|
|
|
if m.HttpRequestCount, err = meter.Int64Counter(
|
|
"onix_http_request_count",
|
|
metric.WithDescription("Total HTTP requests by status, route, method, role and caller"),
|
|
metric.WithUnit("1"),
|
|
); err != nil {
|
|
return nil, fmt.Errorf("onix_http_request_count: %w", err)
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
func GetHTTPMetrics(ctx context.Context) (*HTTPMetrics, error) {
|
|
httpMetricsOnce.Do(func() {
|
|
httpMetricsInstance, httpMetricsErr = newHTTPMetrics()
|
|
})
|
|
return httpMetricsInstance, httpMetricsErr
|
|
}
|
|
|
|
// StatusClass returns the HTTP status class string (e.g. 200 -> "2xx").
|
|
func StatusClass(statusCode int) string {
|
|
switch {
|
|
case statusCode >= 100 && statusCode < 200:
|
|
return "1xx"
|
|
case statusCode >= 200 && statusCode < 300:
|
|
return "2xx"
|
|
case statusCode >= 300 && statusCode < 400:
|
|
return "3xx"
|
|
case statusCode >= 400 && statusCode < 500:
|
|
return "4xx"
|
|
default:
|
|
return "5xx"
|
|
}
|
|
}
|
|
|
|
func RecordHTTPRequest(ctx context.Context, statusCode int, action, role, senderID, recipientID string) {
|
|
m, err := GetHTTPMetrics(ctx)
|
|
if err != nil || m == nil {
|
|
return
|
|
}
|
|
status := StatusClass(statusCode)
|
|
attributes := []attribute.KeyValue{
|
|
telemetry.AttrHTTPStatus.String(status),
|
|
telemetry.AttrAction.String(action),
|
|
telemetry.AttrRole.String(role),
|
|
telemetry.AttrSenderID.String(senderID),
|
|
telemetry.AttrRecipientID.String(recipientID),
|
|
}
|
|
|
|
metric_code := action + "_api_total_count"
|
|
category := "NetworkHealth"
|
|
if strings.HasSuffix(action, "/search") || strings.HasSuffix(action, "/discovery") {
|
|
category = "Discovery"
|
|
}
|
|
attributes = append(attributes, specHttpMetricAttr(metric_code, category)...)
|
|
m.HttpRequestCount.Add(ctx, 1, metric.WithAttributes(attributes...))
|
|
}
|
|
|
|
type responseRecorder struct {
|
|
http.ResponseWriter
|
|
statusCode int
|
|
written bool
|
|
record func()
|
|
}
|
|
|
|
func (r *responseRecorder) WriteHeader(statusCode int) {
|
|
if !r.written {
|
|
r.written = true
|
|
r.statusCode = statusCode
|
|
if r.record != nil {
|
|
r.record()
|
|
}
|
|
}
|
|
r.ResponseWriter.WriteHeader(statusCode)
|
|
}
|
|
|
|
func specHttpMetricAttr(metricCode, category string) []attribute.KeyValue {
|
|
|
|
granularity, frequency := telemetry.GetNetworkMetricsConfig()
|
|
return []attribute.KeyValue{
|
|
telemetry.AttrMetricCode.String(metricCode),
|
|
telemetry.AttrMetricCategory.String(category),
|
|
telemetry.AttrMetricGranularity.String(granularity),
|
|
telemetry.AttrMetricFrequency.String(frequency),
|
|
}
|
|
}
|