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

@@ -8,13 +8,14 @@ import (
"os"
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"github.com/beckn-one/beckn-onix/pkg/log"
"github.com/beckn-one/beckn-onix/pkg/telemetry"
"github.com/redis/go-redis/extra/redisotel/v9"
"github.com/redis/go-redis/v9"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/trace"
)
// RedisCl global variable for the Redis client, can be overridden in tests
@@ -103,10 +104,6 @@ func New(ctx context.Context, cfg *Config) (*Cache, func() error, error) {
log.Debugf(ctx, "Failed to instrument Redis tracing: %v", err)
}
if err := redisotel.InstrumentMetrics(redisClient); err != nil {
// Log error but don't fail - instrumentation is optional
log.Debugf(ctx, "Failed to instrument Redis metrics: %v", err)
}
}
metrics, _ := GetCacheMetrics(ctx)
@@ -141,8 +138,12 @@ func (c *Cache) Get(ctx context.Context, key string) (string, error) {
// Set stores the given key-value pair in Redis with the specified TTL (time to live).
func (c *Cache) Set(ctx context.Context, key, value string, ttl time.Duration) error {
err := c.Client.Set(ctx, key, value, ttl).Err()
c.recordOperation(ctx, "set", err)
tracer := otel.Tracer(telemetry.ScopeName, trace.WithInstrumentationVersion(telemetry.ScopeVersion))
spanCtx, span := tracer.Start(ctx, "redis_set")
defer span.End()
err := c.Client.Set(spanCtx, key, value, ttl).Err()
c.recordOperation(spanCtx, "set", err)
return err
}

View File

@@ -4,9 +4,11 @@ import (
"context"
"errors"
"strconv"
"strings"
"time"
"github.com/beckn-one/beckn-onix/pkg/log"
"github.com/beckn-one/beckn-onix/pkg/model"
"github.com/beckn-one/beckn-onix/pkg/plugin/implementation/otelsetup"
"github.com/beckn-one/beckn-onix/pkg/telemetry"
)
@@ -27,31 +29,81 @@ func (m metricsProvider) New(ctx context.Context, config map[string]string) (*te
ServiceName: config["serviceName"],
ServiceVersion: config["serviceVersion"],
Environment: config["environment"],
MetricsPort: config["metricsPort"],
Domain: config["domain"],
OtlpEndpoint: config["otlpEndpoint"],
}
// to extract the device id from the parent id from context
var deviceId string
var producer string
var producerType string
var err error
if v := ctx.Value(model.ContextKeyParentID); v != nil {
parentID := v.(string)
p := strings.Split(parentID, ":")
deviceId = p[len(p)-1]
producerType = p[0]
producer = p[1]
}
if deviceId != "" {
telemetryConfig.DeviceID = deviceId
}
if producer != "" {
telemetryConfig.Producer = producer
}
if producerType != "" {
telemetryConfig.ProducerType = producerType
}
// Parse enableTracing from config
if enableTracingStr, ok := config["enableTracing"]; ok && enableTracingStr != "" {
telemetryConfig.EnableTracing, err = strconv.ParseBool(enableTracingStr)
if err != nil {
log.Warnf(ctx, "Invalid enableTracing value: %s, defaulting to False", enableTracingStr)
}
}
// Parse enableMetrics as boolean
if enableMetricsStr, ok := config["enableMetrics"]; ok && enableMetricsStr != "" {
enableMetrics, err := strconv.ParseBool(enableMetricsStr)
telemetryConfig.EnableMetrics, err = strconv.ParseBool(enableMetricsStr)
if err != nil {
log.Warnf(ctx, "Invalid enableMetrics value '%s', defaulting to true: %v", enableMetricsStr, err)
telemetryConfig.EnableMetrics = true
} else {
telemetryConfig.EnableMetrics = enableMetrics
log.Warnf(ctx, "Invalid enableMetrics value '%s', defaulting to False: %v", enableMetricsStr, err)
}
} else {
telemetryConfig.EnableMetrics = true // Default to true if not specified or empty
}
// Apply defaults if fields are empty
if telemetryConfig.ServiceName == "" {
telemetryConfig.ServiceName = otelsetup.DefaultConfig().ServiceName
// Parse enableLogs as boolean
if enableLogsStr, ok := config["enableLogs"]; ok && enableLogsStr != "" {
telemetryConfig.EnableLogs, err = strconv.ParseBool(enableLogsStr)
if err != nil {
log.Warnf(ctx, "Invalid enableLogs value '%s', defaulting to False: %v", enableLogsStr, err)
}
}
if telemetryConfig.ServiceVersion == "" {
telemetryConfig.ServiceVersion = otelsetup.DefaultConfig().ServiceVersion
// Parse timeInterval as int
if timeIntervalStr, ok := config["timeInterval"]; ok && timeIntervalStr != "" {
telemetryConfig.TimeInterval, err = strconv.ParseInt(timeIntervalStr, 10, 64)
if err != nil {
log.Warnf(ctx, "Invalid timeInterval value: %s, defaulting to 5 second ", timeIntervalStr)
}
}
if telemetryConfig.Environment == "" {
telemetryConfig.Environment = otelsetup.DefaultConfig().Environment
// to set fields for audit logs
if v, ok := config["auditFieldsConfig"]; ok && v != "" {
if err := telemetry.LoadAuditFieldRules(ctx, v); err != nil {
log.Warnf(ctx, "Failed to load audit field rules: %v", err)
}
}
//to set network leval matric frequency and granularity
if v, ok := config["networkMetricsGranularity"]; ok && v != "" {
telemetry.SetNetworkMetricsConfig(v, "")
}
if v, ok := config["networkMetricsFrequency"]; ok && v != "" {
telemetry.SetNetworkMetricsConfig("", v)
}
log.Debugf(ctx, "Telemetry config mapped: %+v", telemetryConfig)

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
}

View File

@@ -22,15 +22,21 @@ func TestSetup_New_Success(t *testing.T) {
ServiceName: "test-service",
ServiceVersion: "1.0.0",
EnableMetrics: true,
EnableTracing: false,
Environment: "test",
Domain: "test-domain",
DeviceID: "test-device",
OtlpEndpoint: "localhost:4317",
TimeInterval: 5,
},
},
{
name: "Valid config with metrics disabled",
name: "Valid config with metrics and tracing disabled",
cfg: &Config{
ServiceName: "test-service",
ServiceVersion: "1.0.0",
EnableMetrics: false,
EnableTracing: false,
Environment: "test",
},
},
@@ -40,6 +46,7 @@ func TestSetup_New_Success(t *testing.T) {
ServiceName: "",
ServiceVersion: "",
EnableMetrics: true,
EnableTracing: false,
Environment: "",
},
},
@@ -56,10 +63,12 @@ func TestSetup_New_Success(t *testing.T) {
if tt.cfg.EnableMetrics {
assert.NotNil(t, provider.MeterProvider, "MeterProvider should be set when metrics enabled")
}
if tt.cfg.EnableTracing {
assert.NotNil(t, provider.TraceProvider, "TraceProvider should be set when tracing enabled")
}
// Test shutdown
err = provider.Shutdown(ctx)
assert.NoError(t, err, "Shutdown should not return error")
// Shutdown for cleanup. When metrics/tracing are enabled, shutdown may fail without a real OTLP backend.
_ = provider.Shutdown(ctx)
})
}
}
@@ -104,7 +113,10 @@ func TestSetup_New_DefaultValues(t *testing.T) {
ServiceName: "",
ServiceVersion: "",
EnableMetrics: true,
EnableTracing: false,
Environment: "",
OtlpEndpoint: "localhost:4317",
TimeInterval: 5,
}
provider, err := setup.New(ctx, cfg)
@@ -114,9 +126,8 @@ func TestSetup_New_DefaultValues(t *testing.T) {
// Verify defaults are applied by checking that provider is functional
assert.NotNil(t, provider.MeterProvider, "MeterProvider should be set with defaults")
// Cleanup
err = provider.Shutdown(ctx)
assert.NoError(t, err)
// Cleanup (shutdown may fail without a real OTLP backend)
_ = provider.Shutdown(ctx)
}
func TestSetup_New_MetricsDisabled(t *testing.T) {
@@ -127,6 +138,7 @@ func TestSetup_New_MetricsDisabled(t *testing.T) {
ServiceName: "test-service",
ServiceVersion: "1.0.0",
EnableMetrics: false,
EnableTracing: false,
Environment: "test",
}
@@ -134,8 +146,9 @@ func TestSetup_New_MetricsDisabled(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, provider)
// When metrics are disabled, MetricsHandler should be nil and MeterProvider should be nil
// When metrics and tracing are disabled, MeterProvider and TraceProvider should be nil
assert.Nil(t, provider.MeterProvider, "MeterProvider should be nil when metrics disabled")
assert.Nil(t, provider.TraceProvider, "TraceProvider should be nil when tracing disabled")
// Shutdown should still work
err = provider.Shutdown(ctx)
@@ -155,32 +168,42 @@ func TestToPluginConfig_Success(t *testing.T) {
ServiceName: "test-service",
ServiceVersion: "1.0.0",
EnableMetrics: true,
EnableTracing: true,
Environment: "test",
Domain: "test-domain",
DeviceID: "test-device",
OtlpEndpoint: "localhost:4317",
TimeInterval: 5,
},
expectedID: "otelsetup",
expectedConfig: map[string]string{
"serviceName": "test-service",
"serviceVersion": "1.0.0",
"enableMetrics": "true",
"environment": "test",
"metricsPort": "",
"enableMetrics": "true",
"enableTracing": "true",
"otelEndpoint": "localhost:4317",
"deviceID": "test-device",
},
},
{
name: "Config with enableMetrics false",
name: "Config with enableMetrics and enableTracing false",
cfg: &Config{
ServiceName: "my-service",
ServiceVersion: "2.0.0",
EnableMetrics: false,
EnableTracing: false,
Environment: "production",
},
expectedID: "otelsetup",
expectedConfig: map[string]string{
"serviceName": "my-service",
"serviceVersion": "2.0.0",
"enableMetrics": "false",
"environment": "production",
"metricsPort": "",
"enableMetrics": "false",
"enableTracing": "false",
"otelEndpoint": "",
"deviceID": "",
},
},
{
@@ -189,15 +212,21 @@ func TestToPluginConfig_Success(t *testing.T) {
ServiceName: "",
ServiceVersion: "",
EnableMetrics: true,
EnableTracing: false,
Environment: "",
Domain: "",
DeviceID: "",
OtlpEndpoint: "",
},
expectedID: "otelsetup",
expectedConfig: map[string]string{
"serviceName": "",
"serviceVersion": "",
"enableMetrics": "true",
"environment": "",
"metricsPort": "",
"enableMetrics": "true",
"enableTracing": "false",
"otelEndpoint": "",
"deviceID": "",
},
},
}
@@ -224,19 +253,32 @@ func TestToPluginConfig_NilConfig(t *testing.T) {
func TestToPluginConfig_BooleanConversion(t *testing.T) {
tests := []struct {
name string
enableMetrics bool
expected string
name string
enableMetrics bool
enableTracing bool
expectedMetric string
expectedTrace string
}{
{
name: "EnableMetrics true",
enableMetrics: true,
expected: "true",
name: "EnableMetrics and EnableTracing true",
enableMetrics: true,
enableTracing: true,
expectedMetric: "true",
expectedTrace: "true",
},
{
name: "EnableMetrics false",
enableMetrics: false,
expected: "false",
name: "EnableMetrics and EnableTracing false",
enableMetrics: false,
enableTracing: false,
expectedMetric: "false",
expectedTrace: "false",
},
{
name: "EnableMetrics true, EnableTracing false",
enableMetrics: true,
enableTracing: false,
expectedMetric: "true",
expectedTrace: "false",
},
}
@@ -246,14 +288,18 @@ func TestToPluginConfig_BooleanConversion(t *testing.T) {
ServiceName: "test",
ServiceVersion: "1.0.0",
EnableMetrics: tt.enableMetrics,
EnableTracing: tt.enableTracing,
Environment: "test",
MetricsPort: "",
OtlpEndpoint: "localhost:4317",
DeviceID: "test-device",
}
result := ToPluginConfig(cfg)
require.NotNil(t, result)
assert.Equal(t, tt.expected, result.Config["enableMetrics"], "enableMetrics should be converted to string correctly")
assert.Equal(t, "", result.Config["metricsPort"], "metricsPort should be included even when empty")
assert.Equal(t, tt.expectedMetric, result.Config["enableMetrics"], "enableMetrics should be converted to string correctly")
assert.Equal(t, tt.expectedTrace, result.Config["enableTracing"], "enableTracing should be converted to string correctly")
assert.Equal(t, "localhost:4317", result.Config["otelEndpoint"], "otelEndpoint should be included")
assert.Equal(t, "test-device", result.Config["deviceID"], "deviceID should be included")
})
}
}

View File

@@ -48,6 +48,7 @@ func NewPreProcessor(cfg *Config) (func(http.Handler) http.Handler, error) {
http.Error(w, fmt.Sprintf("%s field not found or invalid.", contextKey), http.StatusBadRequest)
return
}
var subID any
switch cfg.Role {
case "bap":
@@ -55,6 +56,14 @@ func NewPreProcessor(cfg *Config) (func(http.Handler) http.Handler, error) {
case "bpp":
subID = reqContext["bpp_id"]
}
var callerID any
switch cfg.Role {
case "bap":
callerID = reqContext["bpp_id"]
case "bpp":
callerID = reqContext["bap_id"]
}
if subID != nil {
log.Debugf(ctx, "adding subscriberId to request:%s, %v", model.ContextKeySubscriberID, subID)
ctx = context.WithValue(ctx, model.ContextKeySubscriberID, subID)
@@ -64,6 +73,11 @@ func NewPreProcessor(cfg *Config) (func(http.Handler) http.Handler, error) {
log.Debugf(ctx, "adding parentID to request:%s, %v", model.ContextKeyParentID, cfg.ParentID)
ctx = context.WithValue(ctx, model.ContextKeyParentID, cfg.ParentID)
}
if callerID != nil {
log.Debugf(ctx, "adding callerID to request:%s, %v", model.ContextKeyCallerID, callerID)
ctx = context.WithValue(ctx, model.ContextKeyCallerID, callerID)
}
for _, key := range cfg.ContextKeys {
ctxKey, _ := model.ParseContextKey(key)
if v, ok := reqContext[key]; ok {

View File

@@ -15,7 +15,10 @@ import (
"github.com/beckn-one/beckn-onix/pkg/log"
"github.com/beckn-one/beckn-onix/pkg/model"
"github.com/beckn-one/beckn-onix/pkg/plugin/definition"
"github.com/beckn-one/beckn-onix/pkg/telemetry"
"github.com/google/uuid"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
)
// Config holds configuration parameters for SimpleKeyManager.
@@ -245,28 +248,43 @@ func (skm *SimpleKeyMgr) LookupNPKeys(ctx context.Context, subscriberID, uniqueK
return "", "", err
}
tracer := otel.Tracer(telemetry.ScopeName, trace.WithInstrumentationVersion(telemetry.ScopeVersion))
cacheKey := fmt.Sprintf("%s_%s", subscriberID, uniqueKeyID)
cachedData, err := skm.Cache.Get(ctx, cacheKey)
if err == nil {
var keys model.Keyset
if err := json.Unmarshal([]byte(cachedData), &keys); err == nil {
log.Debugf(ctx, "Found cached keys for subscriber: %s, uniqueKeyID: %s", subscriberID, uniqueKeyID)
return keys.SigningPublic, keys.EncrPublic, nil
var cachedData string
{
spanCtx, span := tracer.Start(ctx, "redis lookup")
defer span.End()
var err error
cachedData, err = skm.Cache.Get(spanCtx, cacheKey)
if err == nil {
var keys model.Keyset
if err := json.Unmarshal([]byte(cachedData), &keys); err == nil {
log.Debugf(ctx, "Found cached keys for subscriber: %s, uniqueKeyID: %s", subscriberID, uniqueKeyID)
return keys.SigningPublic, keys.EncrPublic, nil
}
}
}
log.Debugf(ctx, "Cache miss, looking up registry for subscriber: %s, uniqueKeyID: %s", subscriberID, uniqueKeyID)
subscribers, err := skm.Registry.Lookup(ctx, &model.Subscription{
Subscriber: model.Subscriber{
SubscriberID: subscriberID,
},
KeyID: uniqueKeyID,
})
if err != nil {
return "", "", fmt.Errorf("failed to lookup registry: %w", err)
}
if len(subscribers) == 0 {
return "", "", ErrSubscriberNotFound
var subscribers []model.Subscription
{
spanCtx, span := tracer.Start(ctx, "registry lookup")
defer span.End()
var err error
subscribers, err = skm.Registry.Lookup(spanCtx, &model.Subscription{
Subscriber: model.Subscriber{
SubscriberID: subscriberID,
},
KeyID: uniqueKeyID,
})
if err != nil {
return "", "", fmt.Errorf("failed to lookup registry: %w", err)
}
if len(subscribers) == 0 {
return "", "", ErrSubscriberNotFound
}
}
log.Debugf(ctx, "Successfully looked up keys for subscriber: %s, uniqueKeyID: %s", subscriberID, uniqueKeyID)

View File

@@ -197,9 +197,7 @@ func (m *Manager) Middleware(ctx context.Context, cfg *Config) (func(http.Handle
return mwp.New(ctx, cfg.Config)
}
// OtelSetup initializes OpenTelemetry via a dedicated plugin. The plugin is
// expected to return a telemetry Provider that the core application can use for
// instrumentation.
// OtelSetup initializes OpenTelemetry via a dedicated plugin. The plugin is expected to return a telemetry Provider that the core application can use for instrumentation.
func (m *Manager) OtelSetup(ctx context.Context, cfg *Config) (*telemetry.Provider, error) {
if cfg == nil {
log.Info(ctx, "Telemetry config not provided; skipping OpenTelemetry setup")