Feat: configure audit fields and metrics for onix adapter and add local configuration for onix adapterZ

This commit is contained in:
Manendra Pal Singh
2026-02-23 16:08:44 +05:30
parent 2745047b27
commit ab89102711
29 changed files with 2167 additions and 441 deletions

View File

@@ -3,23 +3,25 @@ package otelsetup
import (
"context"
"fmt"
"net"
"net/http"
"sync"
"time"
clientprom "github.com/prometheus/client_golang/prometheus"
clientpromhttp "github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/contrib/instrumentation/runtime"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
otelprom "go.opentelemetry.io/otel/exporters/prometheus"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
"time"
"github.com/beckn-one/beckn-onix/pkg/log"
"github.com/beckn-one/beckn-onix/pkg/plugin"
"github.com/beckn-one/beckn-onix/pkg/telemetry"
"go.opentelemetry.io/contrib/instrumentation/runtime"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/log/global"
logsdk "go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
// Setup wires the telemetry provider. This is the concrete implementation
@@ -30,9 +32,16 @@ type Setup struct{}
type Config struct {
ServiceName string `yaml:"serviceName"`
ServiceVersion string `yaml:"serviceVersion"`
EnableMetrics bool `yaml:"enableMetrics"`
Environment string `yaml:"environment"`
MetricsPort string `yaml:"metricsPort"`
Domain string `yaml:"domain"`
DeviceID string `yaml:"deviceID"`
EnableMetrics bool `yaml:"enableMetrics"`
EnableTracing bool `yaml:"enableTracing"`
EnableLogs bool `yaml:"enableLogs"`
OtlpEndpoint string `yaml:"otlpEndpoint"`
TimeInterval int64 `yaml:"timeInterval"`
Producer string `yaml:"producer"`
ProducerType string `yaml:"producerType"`
}
// DefaultConfig returns sensible defaults for telemetry configuration.
@@ -40,9 +49,11 @@ func DefaultConfig() *Config {
return &Config{
ServiceName: "beckn-onix",
ServiceVersion: "dev",
EnableMetrics: true,
Environment: "development",
MetricsPort: "9090",
Domain: "",
DeviceID: "beckn-onix-device",
OtlpEndpoint: "localhost:4317",
TimeInterval: 5,
}
}
@@ -53,9 +64,11 @@ func ToPluginConfig(cfg *Config) *plugin.Config {
Config: map[string]string{
"serviceName": cfg.ServiceName,
"serviceVersion": cfg.ServiceVersion,
"enableMetrics": fmt.Sprintf("%t", cfg.EnableMetrics),
"environment": cfg.Environment,
"metricsPort": cfg.MetricsPort,
"enableMetrics": fmt.Sprintf("%t", cfg.EnableMetrics),
"enableTracing": fmt.Sprintf("%t", cfg.EnableTracing),
"otelEndpoint": cfg.OtlpEndpoint,
"deviceID": cfg.DeviceID,
},
}
}
@@ -78,92 +91,126 @@ func (Setup) New(ctx context.Context, cfg *Config) (*telemetry.Provider, error)
if cfg.Environment == "" {
cfg.Environment = DefaultConfig().Environment
}
if cfg.MetricsPort == "" {
cfg.MetricsPort = DefaultConfig().MetricsPort
if cfg.Domain == "" {
cfg.Domain = DefaultConfig().Domain
}
if cfg.DeviceID == "" {
cfg.DeviceID = DefaultConfig().DeviceID
}
if cfg.TimeInterval == 0 {
cfg.TimeInterval = DefaultConfig().TimeInterval
}
if !cfg.EnableMetrics {
log.Info(ctx, "OpenTelemetry metrics disabled")
if !cfg.EnableMetrics && !cfg.EnableTracing {
log.Info(ctx, "OpenTelemetry metrics and tracing are disabled")
return &telemetry.Provider{
Shutdown: func(context.Context) error { return nil },
}, nil
}
res, err := resource.New(
ctx,
resource.WithAttributes(
attribute.String("service.name", cfg.ServiceName),
attribute.String("service.version", cfg.ServiceVersion),
attribute.String("deployment.environment", cfg.Environment),
),
)
//this will be used by both matric and traces
// to build resource with envelope metadata
baseAttrs := []attribute.KeyValue{
attribute.String("service.name", cfg.ServiceName),
attribute.String("service.version", cfg.ServiceVersion),
attribute.String("environment", cfg.Environment),
attribute.String("domain", cfg.Domain),
attribute.String("device_id", cfg.DeviceID),
attribute.String("producerType", cfg.ProducerType),
attribute.String("producer", cfg.Producer),
}
resMetric, err := resource.New(ctx, resource.WithAttributes(buildAtts(baseAttrs, "METRIC")...))
if err != nil {
return nil, fmt.Errorf("failed to create telemetry resource: %w", err)
return nil, fmt.Errorf("failed to create telemetry resource for matric: %w", err)
}
registry := clientprom.NewRegistry()
exporter, err := otelprom.New(
otelprom.WithRegisterer(registry),
otelprom.WithoutUnits(),
otelprom.WithoutScopeInfo(),
)
if err != nil {
return nil, fmt.Errorf("failed to create prometheus exporter: %w", err)
}
meterProvider := metric.NewMeterProvider(
metric.WithReader(exporter),
metric.WithResource(res),
)
otel.SetMeterProvider(meterProvider)
log.Infof(ctx, "OpenTelemetry metrics initialized for service=%s version=%s env=%s",
cfg.ServiceName, cfg.ServiceVersion, cfg.Environment)
if err := runtime.Start(runtime.WithMinimumReadMemStatsInterval(0)); err != nil {
log.Warnf(ctx, "Failed to start Go runtime instrumentation: %v", err)
}
// Create metrics handler
metricsHandler := clientpromhttp.HandlerFor(registry, clientpromhttp.HandlerOpts{})
// Create and start metrics HTTP server
metricsMux := http.NewServeMux()
metricsMux.Handle("/metrics", metricsHandler)
metricsServer := &http.Server{
Addr: net.JoinHostPort("", cfg.MetricsPort),
Handler: metricsMux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 30 * time.Second,
}
var serverWg sync.WaitGroup
serverWg.Add(1)
go func() {
defer serverWg.Done()
log.Infof(ctx, "Metrics server listening on %s", metricsServer.Addr)
if err := metricsServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Errorf(ctx, fmt.Errorf("metrics server ListenAndServe: %w", err), "error listening and serving metrics")
//OTLP matric
var meterProvider *metric.MeterProvider
if cfg.EnableMetrics {
metricExpoter, err := otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithEndpoint(cfg.OtlpEndpoint),
otlpmetricgrpc.WithDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())))
if err != nil {
return nil, fmt.Errorf("failed to create OTLP metric exporter: %w", err)
}
}()
reader := metric.NewPeriodicReader(metricExpoter, metric.WithInterval(time.Second*time.Duration(cfg.TimeInterval)))
meterProvider = metric.NewMeterProvider(metric.WithReader(reader), metric.WithResource(resMetric))
otel.SetMeterProvider(meterProvider)
log.Infof(ctx, "OpenTelemetry metrics initialized for service=%s version=%s env=%s (OTLP endpoint=%s)",
cfg.ServiceName, cfg.ServiceVersion, cfg.Environment, cfg.OtlpEndpoint)
// for the go runtime matrics
if err := runtime.Start(runtime.WithMinimumReadMemStatsInterval(runtime.DefaultMinimumReadMemStatsInterval)); err != nil {
log.Warnf(ctx, "Failed to start Go runtime instrumentation: %v", err)
}
}
//OTLP traces
restrace, err := resource.New(ctx, resource.WithAttributes(buildAtts(baseAttrs, "API")...))
if err != nil {
return nil, fmt.Errorf("failed to create trace resource: %w", err)
}
var traceProvider *trace.TracerProvider
if cfg.EnableTracing {
traceExpoter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithEndpoint(cfg.OtlpEndpoint), otlptracegrpc.WithDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())))
if err != nil {
return nil, fmt.Errorf("failed to create OTLP trace exporter: %w", err)
}
traceProvider = trace.NewTracerProvider(trace.WithBatcher(traceExpoter), trace.WithResource(restrace)) //TODO: need to add the trace sampleing rate
otel.SetTracerProvider(traceProvider)
log.Infof(ctx, "OpenTelemetry tracing initialized for service=%s (OTLP endpoint=%s)",
cfg.ServiceName, cfg.OtlpEndpoint)
}
resAudit, err := resource.New(ctx, resource.WithAttributes(buildAtts(baseAttrs, "AUDIT")...))
if err != nil {
return nil, fmt.Errorf("failed to create audit resource: %w", err)
}
var logProvider *logsdk.LoggerProvider
if cfg.EnableLogs {
logExporter, err := otlploggrpc.New(ctx, otlploggrpc.WithEndpoint(cfg.OtlpEndpoint), otlploggrpc.WithDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())))
if err != nil {
return nil, fmt.Errorf("failed to create OTLP logs exporter: %w", err)
}
processor := logsdk.NewBatchProcessor(logExporter)
logProvider = logsdk.NewLoggerProvider(logsdk.WithProcessor(processor), logsdk.WithResource(resAudit))
global.SetLoggerProvider(logProvider)
}
return &telemetry.Provider{
MeterProvider: meterProvider,
MetricsHandler: metricsHandler,
MeterProvider: meterProvider,
TraceProvider: traceProvider,
LogProvider: logProvider,
Shutdown: func(shutdownCtx context.Context) error {
log.Infof(ctx, "Shutting down metrics server...")
// Shutdown the metrics server
serverShutdownCtx, cancel := context.WithTimeout(shutdownCtx, 10*time.Second)
defer cancel()
if err := metricsServer.Shutdown(serverShutdownCtx); err != nil {
log.Errorf(ctx, fmt.Errorf("metrics server shutdown: %w", err), "error shutting down metrics server")
var errs []error
if traceProvider != nil {
if err := traceProvider.Shutdown(shutdownCtx); err != nil {
errs = append(errs, fmt.Errorf("tracer shutdown: %w", err))
}
}
serverWg.Wait()
// Shutdown the meter provider
return meterProvider.Shutdown(shutdownCtx)
if meterProvider != nil {
if err := meterProvider.Shutdown(shutdownCtx); err != nil {
errs = append(errs, fmt.Errorf("meter shutdown: %w", err))
}
}
if logProvider != nil {
if err := logProvider.Shutdown(shutdownCtx); err != nil {
errs = append(errs, fmt.Errorf("logs shutdown: %w", err))
}
}
if len(errs) > 0 {
return fmt.Errorf("shutdown errors: %v", errs)
}
return nil
},
}, nil
}
func buildAtts(base []attribute.KeyValue, eid string) []attribute.KeyValue {
atts := make([]attribute.KeyValue, 0, len(base)+1)
atts = append(atts, base...)
atts = append(atts, attribute.String("eid", eid))
return atts
}