Skip to content

Commit

Permalink
Merge branch 'master' into pulak/fix-k8s-notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
pulak-opti committed Nov 2, 2023
2 parents 0ec42f2 + 280940c commit d42c1b4
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 134 deletions.
5 changes: 2 additions & 3 deletions cmd/optimizely/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import (

"github.com/optimizely/agent/config"
"github.com/optimizely/agent/pkg/metrics"
"github.com/optimizely/agent/pkg/middleware"
"github.com/optimizely/agent/pkg/optimizely"
"github.com/optimizely/agent/pkg/routers"
"github.com/optimizely/agent/pkg/server"
Expand All @@ -52,6 +51,7 @@ import (
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
Expand Down Expand Up @@ -158,7 +158,6 @@ func getStdOutTraceProvider(conf config.OTELTracingConfig) (*sdktrace.TracerProv
return sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exp),
sdktrace.WithResource(res),
sdktrace.WithIDGenerator(middleware.NewTraceIDGenerator(conf.TraceIDHeaderKey)),
), nil
}

Expand Down Expand Up @@ -206,7 +205,6 @@ func getRemoteTraceProvider(conf config.OTELTracingConfig) (*sdktrace.TracerProv
sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(conf.Services.Remote.SampleRate))),
sdktrace.WithResource(res),
sdktrace.WithSpanProcessor(bsp),
sdktrace.WithIDGenerator(middleware.NewTraceIDGenerator(conf.TraceIDHeaderKey)),
), nil
}

Expand Down Expand Up @@ -253,6 +251,7 @@ func main() {
}
}()
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
log.Info().Msg(fmt.Sprintf("Tracing enabled with service %q", conf.Tracing.OpenTelemetry.Default))
} else {
log.Info().Msg("Tracing disabled")
Expand Down
7 changes: 3 additions & 4 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ log:
##
## tracing: tracing configuration
##
## For distributed tracing, trace context should be sent on "traceparent" header
## The value set in HTTP Header must be a hex compliant with the W3C trace-context specification.
## See more at https://www.w3.org/TR/trace-context/#trace-id
tracing:
## bydefault tracing is disabled
## to enable tracing set enabled to true
Expand All @@ -43,10 +46,6 @@ tracing:
## tracing environment name
## example: for production environment env can be set as "prod"
env: "dev"
## HTTP Header Key for TraceID in Distributed Tracing
## The value set in HTTP Header must be a hex compliant with the W3C trace-context specification.
## See more at https://www.w3.org/TR/trace-context/#trace-id
traceIDHeaderKey: "X-Optimizely-Trace-ID"
## tracing service configuration
services:
## stdout exporter configuration
Expand Down
9 changes: 4 additions & 5 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,10 @@ const (
)

type OTELTracingConfig struct {
Default TracingServiceType `json:"default"`
ServiceName string `json:"serviceName"`
Env string `json:"env"`
TraceIDHeaderKey string `json:"traceIDHeaderKey"`
Services TracingServiceConfig `json:"services"`
Default TracingServiceType `json:"default"`
ServiceName string `json:"serviceName"`
Env string `json:"env"`
Services TracingServiceConfig `json:"services"`
}

type TracingServiceConfig struct {
Expand Down
72 changes: 13 additions & 59 deletions pkg/middleware/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,74 +18,22 @@
package middleware

import (
"context"
crand "crypto/rand"
"encoding/binary"
"math/rand"
"net/http"
"sync"

"github.com/optimizely/agent/config"
"github.com/rs/zerolog/log"
"github.com/go-chi/chi/v5/middleware"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/propagation"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"go.opentelemetry.io/otel/trace"
)

type traceIDGenerator struct {
sync.Mutex
randSource *rand.Rand
traceIDHeaderKey string
}

func NewTraceIDGenerator(traceIDHeaderKey string) *traceIDGenerator {
var rngSeed int64
_ = binary.Read(crand.Reader, binary.LittleEndian, &rngSeed)
return &traceIDGenerator{
randSource: rand.New(rand.NewSource(rngSeed)),
traceIDHeaderKey: traceIDHeaderKey,
}
}

func (gen *traceIDGenerator) NewSpanID(ctx context.Context, traceID trace.TraceID) trace.SpanID {
gen.Lock()
defer gen.Unlock()
sid := trace.SpanID{}
_, _ = gen.randSource.Read(sid[:])
return sid
}

func (gen *traceIDGenerator) NewIDs(ctx context.Context) (trace.TraceID, trace.SpanID) {
gen.Lock()
defer gen.Unlock()
tid := trace.TraceID{}
_, _ = gen.randSource.Read(tid[:])
sid := trace.SpanID{}
_, _ = gen.randSource.Read(sid[:])

// read trace id from header if provided
traceIDHeader := ctx.Value(gen.traceIDHeaderKey)
if val, ok := traceIDHeader.(string); ok {
if val != "" {
headerTraceId, err := trace.TraceIDFromHex(val)
if err == nil {
tid = headerTraceId
} else {
log.Error().Err(err).Msg("failed to parse trace id from header, invalid trace id")
}
}
}

return tid, sid
}

func AddTracing(conf config.TracingConfig, tracerName, spanName string) func(http.Handler) http.Handler {
func AddTracing(tracerName, spanName string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
pctx := context.WithValue(r.Context(), conf.OpenTelemetry.TraceIDHeaderKey, r.Header.Get(conf.OpenTelemetry.TraceIDHeaderKey))
prop := otel.GetTextMapPropagator()
propCtx := prop.Extract(r.Context(), propagation.HeaderCarrier(r.Header))

ctx, span := otel.Tracer(tracerName).Start(pctx, spanName)
ctx, span := otel.Tracer(tracerName).Start(propCtx, spanName)
defer span.End()

span.SetAttributes(
Expand All @@ -97,7 +45,13 @@ func AddTracing(conf config.TracingConfig, tracerName, spanName string) func(htt
attribute.String(OptlySDKHeader, r.Header.Get(OptlySDKHeader)),
)

next.ServeHTTP(w, r.WithContext(ctx))
respWriter := middleware.NewWrapResponseWriter(w, r.ProtoMajor)

next.ServeHTTP(respWriter, r.WithContext(ctx))

span.SetAttributes(
semconv.HTTPStatusCodeKey.Int(respWriter.Status()),
)
}
return http.HandlerFunc(fn)
}
Expand Down
47 changes: 1 addition & 46 deletions pkg/middleware/trace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,9 @@
package middleware

import (
"context"
"net/http"
"net/http/httptest"
"testing"

"github.com/optimizely/agent/config"
"github.com/stretchr/testify/assert"
"go.opentelemetry.io/otel/trace"
)

func TestAddTracing(t *testing.T) {
Expand All @@ -37,7 +32,7 @@ func TestAddTracing(t *testing.T) {

req := httptest.NewRequest("GET", "/", nil)
rr := httptest.NewRecorder()
middleware := http.Handler(AddTracing(config.TracingConfig{}, "test-tracer", "test-span")(handler))
middleware := http.Handler(AddTracing("test-tracer", "test-span")(handler))

// Serve the request through the middleware
middleware.ServeHTTP(rr, req)
Expand All @@ -54,43 +49,3 @@ func TestAddTracing(t *testing.T) {
t.Errorf("Expected Content-Type header %v, but got %v", "application/text", typeHeader)
}
}

func TestNewIDs(t *testing.T) {
gen := NewTraceIDGenerator("")
n := 1000

for i := 0; i < n; i++ {
traceID, spanID := gen.NewIDs(context.Background())
assert.Truef(t, traceID.IsValid(), "trace id: %s", traceID.String())
assert.Truef(t, spanID.IsValid(), "span id: %s", spanID.String())
}
}

func TestNewSpanID(t *testing.T) {
gen := NewTraceIDGenerator("")
testTraceID := [16]byte{123, 123}
n := 1000

for i := 0; i < n; i++ {
spanID := gen.NewSpanID(context.Background(), testTraceID)
assert.Truef(t, spanID.IsValid(), "span id: %s", spanID.String())
}
}

func TestNewSpanIDWithInvalidTraceID(t *testing.T) {
gen := NewTraceIDGenerator("")
spanID := gen.NewSpanID(context.Background(), trace.TraceID{})
assert.Truef(t, spanID.IsValid(), "span id: %s", spanID.String())
}

func TestTraceIDWithGivenHeaderValue(t *testing.T) {
traceHeader := "X-Trace-ID"
traceID := "9b8eac67e332c6f8baf1e013de6891bb"

gen := NewTraceIDGenerator(traceHeader)

ctx := context.WithValue(context.Background(), traceHeader, traceID)
genTraceID, _ := gen.NewIDs(ctx)
assert.Truef(t, genTraceID.IsValid(), "trace id: %s", genTraceID.String())
assert.Equal(t, traceID, genTraceID.String())
}
30 changes: 15 additions & 15 deletions pkg/routers/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,19 @@ func NewDefaultAPIRouter(optlyCache optimizely.Cache, conf config.AgentConfig, m
corsHandler: corsHandler,
}

return NewAPIRouter(spec, conf.Tracing)
return NewAPIRouter(spec)
}

// NewAPIRouter returns HTTP API router backed by an optimizely.Cache implementation
func NewAPIRouter(opt *APIOptions, traceConf config.TracingConfig) *chi.Mux {
func NewAPIRouter(opt *APIOptions) *chi.Mux {
r := chi.NewRouter()
WithAPIRouter(opt, r, traceConf)
WithAPIRouter(opt, r)
return r
}

// WithAPIRouter appends routes and middleware to the given router.
// See https://godoc.org/github.com/go-chi/chi/v5#Mux.Group for usage
func WithAPIRouter(opt *APIOptions, r chi.Router, traceConf config.TracingConfig) {
func WithAPIRouter(opt *APIOptions, r chi.Router) {
getConfigTimer := middleware.Metricize("get-config", opt.metricsRegistry)
getDatafileTimer := middleware.Metricize("get-datafile", opt.metricsRegistry)
activateTimer := middleware.Metricize("activate", opt.metricsRegistry)
Expand All @@ -136,17 +136,17 @@ func WithAPIRouter(opt *APIOptions, r chi.Router, traceConf config.TracingConfig
createAccesstokenTimer := middleware.Metricize("create-api-access-token", opt.metricsRegistry)
contentTypeMiddleware := chimw.AllowContentType("application/json")

configTracer := middleware.AddTracing(traceConf, "configHandler", "OptimizelyConfig")
datafileTracer := middleware.AddTracing(traceConf, "datafileHandler", "OptimizelyDatafile")
activateTracer := middleware.AddTracing(traceConf, "activateHandler", "Activate")
decideTracer := middleware.AddTracing(traceConf, "decideHandler", "Decide")
trackTracer := middleware.AddTracing(traceConf, "trackHandler", "Track")
overrideTracer := middleware.AddTracing(traceConf, "overrideHandler", "Override")
lookupTracer := middleware.AddTracing(traceConf, "lookupHandler", "Lookup")
saveTracer := middleware.AddTracing(traceConf, "saveHandler", "Save")
sendOdpEventTracer := middleware.AddTracing(traceConf, "sendOdpEventHandler", "SendOdpEvent")
nStreamTracer := middleware.AddTracing(traceConf, "notificationHandler", "SendNotificationEvent")
authTracer := middleware.AddTracing(traceConf, "authHandler", "AuthToken")
configTracer := middleware.AddTracing("configHandler", "OptimizelyConfig")
datafileTracer := middleware.AddTracing("datafileHandler", "OptimizelyDatafile")
activateTracer := middleware.AddTracing("activateHandler", "Activate")
decideTracer := middleware.AddTracing("decideHandler", "Decide")
trackTracer := middleware.AddTracing("trackHandler", "Track")
overrideTracer := middleware.AddTracing("overrideHandler", "Override")
lookupTracer := middleware.AddTracing("lookupHandler", "Lookup")
saveTracer := middleware.AddTracing("saveHandler", "Save")
sendOdpEventTracer := middleware.AddTracing("sendOdpEventHandler", "SendOdpEvent")
nStreamTracer := middleware.AddTracing("notificationHandler", "SendNotificationEvent")
authTracer := middleware.AddTracing("authHandler", "AuthToken")

if opt.maxConns > 0 {
// Note this is NOT a rate limiter, but a concurrency threshold
Expand Down
4 changes: 2 additions & 2 deletions pkg/routers/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func (suite *APIV1TestSuite) SetupTest() {
corsHandler: testCorsHandler,
}

suite.mux = NewAPIRouter(opts, config.TracingConfig{})
suite.mux = NewAPIRouter(opts)
}

func (suite *APIV1TestSuite) TestValidRoutes() {
Expand All @@ -141,7 +141,7 @@ func (suite *APIV1TestSuite) TestValidRoutes() {
}
return http.HandlerFunc(fn)
}
suite.mux = NewAPIRouter(opts, config.TracingConfig{})
suite.mux = NewAPIRouter(opts)

routes := []struct {
method string
Expand Down

0 comments on commit d42c1b4

Please sign in to comment.