From f89e2b5e0d9d24adb176daf76bde882a7bce64dd Mon Sep 17 00:00:00 2001 From: lightsta1ker Date: Thu, 30 Oct 2025 18:23:44 +0530 Subject: [PATCH 01/12] Updating uninstrumented example --- examples/dice/uninstrumented/main.go | 59 ++++++++++++++++++++++-- examples/dice/uninstrumented/rolldice.go | 32 +++++++------ 2 files changed, 72 insertions(+), 19 deletions(-) diff --git a/examples/dice/uninstrumented/main.go b/examples/dice/uninstrumented/main.go index 87bf30c9cae..436dc61a9c4 100644 --- a/examples/dice/uninstrumented/main.go +++ b/examples/dice/uninstrumented/main.go @@ -7,11 +7,13 @@ package main import ( "context" + "encoding/json" "log" "net" "net/http" "os" "os/signal" + "strconv" "time" ) @@ -27,8 +29,13 @@ func run() (err error) { defer stop() // Start HTTP server. + port := os.Getenv("APPLICATION_PORT") + if port == "" { + port = "8080" + } + srv := &http.Server{ - Addr: ":8080", + Addr: ":" + port, BaseContext: func(net.Listener) context.Context { return ctx }, ReadTimeout: time.Second, WriteTimeout: 10 * time.Second, @@ -60,8 +67,52 @@ func newHTTPHandler() http.Handler { mux := http.NewServeMux() // Register handlers. - mux.HandleFunc("/rolldice/", rolldice) - mux.HandleFunc("/rolldice/{player}", rolldice) - + mux.HandleFunc("/rolldice", handleRollDice) return mux } + +func handleRollDice(w http.ResponseWriter, r *http.Request) { + // Parse query parameters + rollsParam := r.URL.Query().Get("rolls") + player := r.URL.Query().Get("player") + + // Default rolls = 1 if not defined + if rollsParam == "" { + rollsParam = "1" + } + + // Check if rolls is a number + rolls, err := strconv.Atoi(rollsParam) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + msg := "Parameter rolls must be a positive integer" + _ = json.NewEncoder(w).Encode(map[string]string{ + "status": "error", + "message": msg, + }) + log.Printf("WARN: %s", msg) + return + } + + results, err := rolldice(rolls) + if err != nil { + // Library signals invalid input (<=0) + w.WriteHeader(http.StatusInternalServerError) + log.Printf("ERROR: %v", err) + return + } + + if player == "" { + log.Printf("DEBUG: anonymous player rolled %v", results) + } else { + log.Printf("DEBUG: player=%s rolled %v", player, results) + } + log.Printf("INFO: %s %s -> 200 OK", r.Method, r.URL.String()) + + w.Header().Set("Content-Type", "application/json") + if len(results) == 1 { + json.NewEncoder(w).Encode(results[0]) + } else { + json.NewEncoder(w).Encode(results) + } +} diff --git a/examples/dice/uninstrumented/rolldice.go b/examples/dice/uninstrumented/rolldice.go index 95ac6b4a516..c3820517148 100644 --- a/examples/dice/uninstrumented/rolldice.go +++ b/examples/dice/uninstrumented/rolldice.go @@ -4,26 +4,28 @@ package main import ( - "io" - "log" + "errors" "math/rand" - "net/http" - "strconv" ) -func rolldice(w http.ResponseWriter, r *http.Request) { - roll := 1 + rand.Intn(6) //nolint:gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand) is ignored as this is not security-sensitive. +func rolldice(rolls int) ([]int, error) { + if rolls <= 0 { + return nil, errors.New("rolls must be positive") + } - var msg string - if player := r.PathValue("player"); player != "" { - msg = player + " is rolling the dice" - } else { - msg = "Anonymous player is rolling the dice" + if rolls == 1 { + return []int{rollOnce()}, nil } - log.Printf("%s, result: %d", msg, roll) - resp := strconv.Itoa(roll) + "\n" - if _, err := io.WriteString(w, resp); err != nil { - log.Printf("Write failed: %v", err) + results := make([]int, rolls) + for i := 0; i < rolls; i++ { + results[i] = rollOnce() } + return results, nil +} + +// rollOnce is the inner function — returns a random number 1–6. +func rollOnce() int { + roll := 1 + rand.Intn(6) //nolint:gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand) is ignored as this is not security-sensitive. + return roll } From a337ba6d97e3c260789ef7a7eb83d6c06ae66899 Mon Sep 17 00:00:00 2001 From: lightsta1ker Date: Thu, 30 Oct 2025 23:11:55 +0530 Subject: [PATCH 02/12] Updating instrumented example --- examples/dice/instrumented/main.go | 57 +++++++++++++- examples/dice/instrumented/otel.go | 34 +++++---- examples/dice/instrumented/rolldice.go | 96 ++++++++++++++++++------ examples/dice/uninstrumented/main.go | 2 +- examples/dice/uninstrumented/rolldice.go | 1 + 5 files changed, 150 insertions(+), 40 deletions(-) diff --git a/examples/dice/instrumented/main.go b/examples/dice/instrumented/main.go index 4a3ade11d0d..be77145f46b 100644 --- a/examples/dice/instrumented/main.go +++ b/examples/dice/instrumented/main.go @@ -7,12 +7,14 @@ package main import ( "context" + "encoding/json" "errors" "log" "net" "net/http" "os" "os/signal" + "strconv" "time" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" @@ -40,8 +42,12 @@ func run() error { }() // Start HTTP server. + port := os.Getenv("APPLICATION_PORT") + if port == "" { + port = "8080" + } srv := &http.Server{ - Addr: ":8080", + Addr: ":" + port, BaseContext: func(net.Listener) context.Context { return ctx }, ReadTimeout: time.Second, WriteTimeout: 10 * time.Second, @@ -80,10 +86,55 @@ func newHTTPHandler() http.Handler { } // Register handlers. - handleFunc("/rolldice/", rolldice) - handleFunc("/rolldice/{player}", rolldice) + handleFunc("/rolldice", handleRollDice) // Add HTTP instrumentation for the whole server. handler := otelhttp.NewHandler(mux, "/") return handler } + +func handleRollDice(w http.ResponseWriter, r *http.Request) { + // Parse query parameters + rollsParam := r.URL.Query().Get("rolls") + player := r.URL.Query().Get("player") + + // Default rolls = 1 if not defined + if rollsParam == "" { + rollsParam = "1" + } + + // Check if rolls is a number + rolls, err := strconv.Atoi(rollsParam) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + msg := "Parameter rolls must be a positive integer" + _ = json.NewEncoder(w).Encode(map[string]string{ + "status": "error", + "message": msg, + }) + log.Printf("WARN: %s", msg) + return + } + + results, err := rollDice(r.Context(), rolls) + if err != nil { + // Signals invalid input (<=0) + w.WriteHeader(http.StatusInternalServerError) + log.Printf("ERROR: %v", err) + return + } + + if player == "" { + log.Printf("DEBUG: anonymous player rolled %v", results) + } else { + log.Printf("DEBUG: player=%s rolled %v", player, results) + } + log.Printf("INFO: %s %s -> 200 OK", r.Method, r.URL.String()) + + w.Header().Set("Content-Type", "application/json") + if len(results) == 1 { + json.NewEncoder(w).Encode(results[0]) + } else { + json.NewEncoder(w).Encode(results) + } +} diff --git a/examples/dice/instrumented/otel.go b/examples/dice/instrumented/otel.go index b0e0e58f29d..16770e2245c 100644 --- a/examples/dice/instrumented/otel.go +++ b/examples/dice/instrumented/otel.go @@ -16,6 +16,7 @@ import ( "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/log" "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/trace" ) @@ -42,12 +43,19 @@ func setupOTelSDK(ctx context.Context) (func(context.Context) error, error) { err = errors.Join(inErr, shutdown(ctx)) } + res, _ := resource.New(ctx, + resource.WithFromEnv(), // reads OTEL_SERVICE_NAME & OTEL_RESOURCE_ATTRIBUTES + resource.WithProcess(), + resource.WithHost(), + resource.WithTelemetrySDK(), + ) + // Set up propagator. prop := newPropagator() otel.SetTextMapPropagator(prop) // Set up trace provider. - tracerProvider, err := newtracerProvider() + tracerProvider, err := newtracerProvider(res) if err != nil { handleErr(err) return shutdown, err @@ -56,7 +64,7 @@ func setupOTelSDK(ctx context.Context) (func(context.Context) error, error) { otel.SetTracerProvider(tracerProvider) // Set up meter provider. - meterProvider, err := newMeterProvider() + meterProvider, err := newMeterProvider(res) if err != nil { handleErr(err) return shutdown, err @@ -65,7 +73,7 @@ func setupOTelSDK(ctx context.Context) (func(context.Context) error, error) { otel.SetMeterProvider(meterProvider) // Set up logger provider. - loggerProvider, err := newLoggerProvider() + loggerProvider, err := newLoggerProvider(res) if err != nil { handleErr(err) return shutdown, err @@ -83,36 +91,33 @@ func newPropagator() propagation.TextMapPropagator { ) } -func newtracerProvider() (*trace.TracerProvider, error) { - traceExporter, err := stdouttrace.New( - stdouttrace.WithPrettyPrint()) +func newtracerProvider(res *resource.Resource) (*trace.TracerProvider, error) { + traceExporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) if err != nil { return nil, err } tracerProvider := trace.NewTracerProvider( - trace.WithBatcher(traceExporter, - // Default is 5s. Set to 1s for demonstrative purposes. - trace.WithBatchTimeout(time.Second)), + trace.WithBatcher(traceExporter, trace.WithBatchTimeout(time.Second)), // Default is 5s. Set to 1s for demonstrative purposes. + trace.WithResource(res), ) return tracerProvider, nil } -func newMeterProvider() (*metric.MeterProvider, error) { +func newMeterProvider(res *resource.Resource) (*metric.MeterProvider, error) { metricExporter, err := stdoutmetric.New() if err != nil { return nil, err } meterProvider := metric.NewMeterProvider( - metric.WithReader(metric.NewPeriodicReader(metricExporter, - // Default is 1m. Set to 3s for demonstrative purposes. - metric.WithInterval(3*time.Second))), + metric.WithReader(metric.NewPeriodicReader(metricExporter, metric.WithInterval(3*time.Second))), // Default is 1m. Set to 3s for demonstrative purposes. + metric.WithResource(res), ) return meterProvider, nil } -func newLoggerProvider() (*log.LoggerProvider, error) { +func newLoggerProvider(res *resource.Resource) (*log.LoggerProvider, error) { logExporter, err := stdoutlog.New() if err != nil { return nil, err @@ -120,6 +125,7 @@ func newLoggerProvider() (*log.LoggerProvider, error) { loggerProvider := log.NewLoggerProvider( log.WithProcessor(log.NewBatchProcessor(logExporter)), + log.WithResource(res), ) return loggerProvider, nil } diff --git a/examples/dice/instrumented/rolldice.go b/examples/dice/instrumented/rolldice.go index 10edcfd4a91..9426840b88d 100644 --- a/examples/dice/instrumented/rolldice.go +++ b/examples/dice/instrumented/rolldice.go @@ -4,10 +4,9 @@ package main import ( - "io" + "context" + "errors" "math/rand" - "net/http" - "strconv" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -19,42 +18,95 @@ import ( const name = "go.opentelemetry.io/contrib/examples/dice" var ( - tracer = otel.Tracer(name) - meter = otel.Meter(name) - logger = otelslog.NewLogger(name) - rollCnt metric.Int64Counter + tracer = otel.Tracer(name) + meter = otel.Meter(name) + logger = otelslog.NewLogger(name) + rollCnt metric.Int64Counter + outcomeHist metric.Int64Histogram + lastRollsGauge metric.Int64ObservableGauge + lastRolls int64 ) func init() { var err error rollCnt, err = meter.Int64Counter("dice.rolls", - metric.WithDescription("The number of rolls by roll value"), + metric.WithDescription("The number of rolls"), metric.WithUnit("{roll}")) if err != nil { panic(err) } + + outcomeHist, err = meter.Int64Histogram( + "dice.outcome", + metric.WithDescription("Distribution of dice outcomes (1-6)"), + metric.WithUnit("{count}"), + ) + if err != nil { + panic(err) + } + + lastRollsGauge, err = meter.Int64ObservableGauge( + "dice.last.rolls", + metric.WithDescription("The last rolls value observed"), + ) + if err != nil { + panic(err) + } + + // Register the gauge callback + _, err = meter.RegisterCallback( + func(ctx context.Context, o metric.Observer) error { + o.ObserveInt64(lastRollsGauge, lastRolls) + return nil + }, + lastRollsGauge, + ) + if err != nil { + panic(err) + } } -func rolldice(w http.ResponseWriter, r *http.Request) { - ctx, span := tracer.Start(r.Context(), "roll") +// rollDice is the outer function which Does the error handling. +func rollDice(ctx context.Context, rolls int) ([]int, error) { + ctx, span := tracer.Start(ctx, "rollDice") defer span.End() - roll := 1 + rand.Intn(6) //nolint:gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand) is ignored as this is not security-sensitive. + if rolls <= 0 { + err := errors.New("rolls must be positive") + span.RecordError(err) + logger.ErrorContext(ctx, "error", "error", err) + return nil, err + } + + if rolls == 1 { + val := rollOnce(ctx) + outcomeHist.Record(ctx, int64(val)) + lastRolls = int64(rolls) + return []int{val}, nil + } - var msg string - if player := r.PathValue("player"); player != "" { - msg = player + " is rolling the dice" - } else { - msg = "Anonymous player is rolling the dice" + results := make([]int, rolls) + for i := 0; i < rolls; i++ { + results[i] = rollOnce(ctx) + outcomeHist.Record(ctx, int64(results[i])) } - logger.InfoContext(ctx, msg, "result", roll) + + rollsAttr := attribute.Int("rolls", rolls) + span.SetAttributes(rollsAttr) + rollCnt.Add(ctx, 1, metric.WithAttributes(rollsAttr)) + lastRolls = int64(rolls) + return results, nil +} + +// rollOnce is the inner function — returns a random number 1–6. +func rollOnce(ctx context.Context) int { + ctx, span := tracer.Start(ctx, "rollOnce") + defer span.End() + + roll := 1 + rand.Intn(6) //nolint:gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand) is ignored as this is not security-sensitive. rollValueAttr := attribute.Int("roll.value", roll) span.SetAttributes(rollValueAttr) - rollCnt.Add(ctx, 1, metric.WithAttributes(rollValueAttr)) - resp := strconv.Itoa(roll) + "\n" - if _, err := io.WriteString(w, resp); err != nil { - logger.ErrorContext(ctx, "Write failed", "error", err) - } + return roll } diff --git a/examples/dice/uninstrumented/main.go b/examples/dice/uninstrumented/main.go index 436dc61a9c4..c004b634e3d 100644 --- a/examples/dice/uninstrumented/main.go +++ b/examples/dice/uninstrumented/main.go @@ -96,7 +96,7 @@ func handleRollDice(w http.ResponseWriter, r *http.Request) { results, err := rolldice(rolls) if err != nil { - // Library signals invalid input (<=0) + // Signals invalid input (<=0) w.WriteHeader(http.StatusInternalServerError) log.Printf("ERROR: %v", err) return diff --git a/examples/dice/uninstrumented/rolldice.go b/examples/dice/uninstrumented/rolldice.go index c3820517148..788b408abe7 100644 --- a/examples/dice/uninstrumented/rolldice.go +++ b/examples/dice/uninstrumented/rolldice.go @@ -8,6 +8,7 @@ import ( "math/rand" ) +// rolldice is the outer function which does the error handling. func rolldice(rolls int) ([]int, error) { if rolls <= 0 { return nil, errors.New("rolls must be positive") From da2e8cd4bb3704a0b288d426e7f02a24486d69fd Mon Sep 17 00:00:00 2001 From: lightsta1ker Date: Tue, 4 Nov 2025 15:27:56 +0530 Subject: [PATCH 03/12] chore: lint and suggestions --- examples/dice/instrumented/main.go | 48 ---------------------- examples/dice/instrumented/otel.go | 2 +- examples/dice/instrumented/rolldice.go | 52 +++++++++++++++++++++++- examples/dice/uninstrumented/main.go | 48 ---------------------- examples/dice/uninstrumented/rolldice.go | 50 +++++++++++++++++++++++ 5 files changed, 102 insertions(+), 98 deletions(-) diff --git a/examples/dice/instrumented/main.go b/examples/dice/instrumented/main.go index be77145f46b..75573f97f5d 100644 --- a/examples/dice/instrumented/main.go +++ b/examples/dice/instrumented/main.go @@ -7,14 +7,12 @@ package main import ( "context" - "encoding/json" "errors" "log" "net" "net/http" "os" "os/signal" - "strconv" "time" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" @@ -92,49 +90,3 @@ func newHTTPHandler() http.Handler { handler := otelhttp.NewHandler(mux, "/") return handler } - -func handleRollDice(w http.ResponseWriter, r *http.Request) { - // Parse query parameters - rollsParam := r.URL.Query().Get("rolls") - player := r.URL.Query().Get("player") - - // Default rolls = 1 if not defined - if rollsParam == "" { - rollsParam = "1" - } - - // Check if rolls is a number - rolls, err := strconv.Atoi(rollsParam) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - msg := "Parameter rolls must be a positive integer" - _ = json.NewEncoder(w).Encode(map[string]string{ - "status": "error", - "message": msg, - }) - log.Printf("WARN: %s", msg) - return - } - - results, err := rollDice(r.Context(), rolls) - if err != nil { - // Signals invalid input (<=0) - w.WriteHeader(http.StatusInternalServerError) - log.Printf("ERROR: %v", err) - return - } - - if player == "" { - log.Printf("DEBUG: anonymous player rolled %v", results) - } else { - log.Printf("DEBUG: player=%s rolled %v", player, results) - } - log.Printf("INFO: %s %s -> 200 OK", r.Method, r.URL.String()) - - w.Header().Set("Content-Type", "application/json") - if len(results) == 1 { - json.NewEncoder(w).Encode(results[0]) - } else { - json.NewEncoder(w).Encode(results) - } -} diff --git a/examples/dice/instrumented/otel.go b/examples/dice/instrumented/otel.go index 16770e2245c..9d79fc2279e 100644 --- a/examples/dice/instrumented/otel.go +++ b/examples/dice/instrumented/otel.go @@ -44,7 +44,7 @@ func setupOTelSDK(ctx context.Context) (func(context.Context) error, error) { } res, _ := resource.New(ctx, - resource.WithFromEnv(), // reads OTEL_SERVICE_NAME & OTEL_RESOURCE_ATTRIBUTES + resource.WithFromEnv(), // reads OTEL_SERVICE_NAME & OTEL_RESOURCE_ATTRIBUTES. resource.WithProcess(), resource.WithHost(), resource.WithTelemetrySDK(), diff --git a/examples/dice/instrumented/rolldice.go b/examples/dice/instrumented/rolldice.go index 9426840b88d..3f4e3a03df8 100644 --- a/examples/dice/instrumented/rolldice.go +++ b/examples/dice/instrumented/rolldice.go @@ -5,8 +5,12 @@ package main import ( "context" + "encoding/json" "errors" + "log" "math/rand" + "net/http" + "strconv" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -53,7 +57,7 @@ func init() { panic(err) } - // Register the gauge callback + // Register the gauge callback. _, err = meter.RegisterCallback( func(ctx context.Context, o metric.Observer) error { o.ObserveInt64(lastRollsGauge, lastRolls) @@ -66,6 +70,52 @@ func init() { } } +func handleRollDice(w http.ResponseWriter, r *http.Request) { + // Parse query parameters. + rollsParam := r.URL.Query().Get("rolls") + player := r.URL.Query().Get("player") + + // Default rolls = 1 if not defined. + if rollsParam == "" { + rollsParam = "1" + } + + // Check if rolls is a number. + rolls, err := strconv.Atoi(rollsParam) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + msg := "Parameter rolls must be a positive integer" + _ = json.NewEncoder(w).Encode(map[string]string{ + "status": "error", + "message": msg, + }) + logger.WarnContext(r.Context(), msg) + return + } + + results, err := rollDice(r.Context(), rolls) + if err != nil { + // Signals invalid input (<=0). + w.WriteHeader(http.StatusInternalServerError) + log.Printf("ERROR: %v", err) + return + } + + if player == "" { + log.Printf("DEBUG: anonymous player rolled %v", results) + } else { + log.Printf("DEBUG: player=%s rolled %v", player, results) + } + log.Printf("INFO: %s %s -> 200 OK", r.Method, r.URL.String()) + + w.Header().Set("Content-Type", "application/json") + if len(results) == 1 { + json.NewEncoder(w).Encode(results[0]) + } else { + json.NewEncoder(w).Encode(results) + } +} + // rollDice is the outer function which Does the error handling. func rollDice(ctx context.Context, rolls int) ([]int, error) { ctx, span := tracer.Start(ctx, "rollDice") diff --git a/examples/dice/uninstrumented/main.go b/examples/dice/uninstrumented/main.go index c004b634e3d..9675e1fd4cd 100644 --- a/examples/dice/uninstrumented/main.go +++ b/examples/dice/uninstrumented/main.go @@ -7,13 +7,11 @@ package main import ( "context" - "encoding/json" "log" "net" "net/http" "os" "os/signal" - "strconv" "time" ) @@ -70,49 +68,3 @@ func newHTTPHandler() http.Handler { mux.HandleFunc("/rolldice", handleRollDice) return mux } - -func handleRollDice(w http.ResponseWriter, r *http.Request) { - // Parse query parameters - rollsParam := r.URL.Query().Get("rolls") - player := r.URL.Query().Get("player") - - // Default rolls = 1 if not defined - if rollsParam == "" { - rollsParam = "1" - } - - // Check if rolls is a number - rolls, err := strconv.Atoi(rollsParam) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - msg := "Parameter rolls must be a positive integer" - _ = json.NewEncoder(w).Encode(map[string]string{ - "status": "error", - "message": msg, - }) - log.Printf("WARN: %s", msg) - return - } - - results, err := rolldice(rolls) - if err != nil { - // Signals invalid input (<=0) - w.WriteHeader(http.StatusInternalServerError) - log.Printf("ERROR: %v", err) - return - } - - if player == "" { - log.Printf("DEBUG: anonymous player rolled %v", results) - } else { - log.Printf("DEBUG: player=%s rolled %v", player, results) - } - log.Printf("INFO: %s %s -> 200 OK", r.Method, r.URL.String()) - - w.Header().Set("Content-Type", "application/json") - if len(results) == 1 { - json.NewEncoder(w).Encode(results[0]) - } else { - json.NewEncoder(w).Encode(results) - } -} diff --git a/examples/dice/uninstrumented/rolldice.go b/examples/dice/uninstrumented/rolldice.go index 788b408abe7..6497641eb1e 100644 --- a/examples/dice/uninstrumented/rolldice.go +++ b/examples/dice/uninstrumented/rolldice.go @@ -4,10 +4,60 @@ package main import ( + "encoding/json" "errors" + "log" "math/rand" + "net/http" + "strconv" ) +func handleRollDice(w http.ResponseWriter, r *http.Request) { + // Parse query parameters. + rollsParam := r.URL.Query().Get("rolls") + player := r.URL.Query().Get("player") + + // Default rolls = 1 if not defined. + if rollsParam == "" { + rollsParam = "1" + } + + // Check if rolls is a number. + rolls, err := strconv.Atoi(rollsParam) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + msg := "Parameter rolls must be a positive integer" + _ = json.NewEncoder(w).Encode(map[string]string{ + "status": "error", + "message": msg, + }) + log.Printf("WARN: %s", msg) + return + } + + results, err := rolldice(rolls) + if err != nil { + // Signals invalid input (<=0). + w.WriteHeader(http.StatusInternalServerError) + log.Printf("ERROR: %v", err) + return + } + + if player == "" { + log.Printf("DEBUG: anonymous player rolled %v", results) + } else { + log.Printf("DEBUG: player=%s rolled %v", player, results) + } + log.Printf("INFO: %s %s -> 200 OK", r.Method, r.URL.String()) + + w.Header().Set("Content-Type", "application/json") + if len(results) == 1 { + json.NewEncoder(w).Encode(results[0]) + } else { + json.NewEncoder(w).Encode(results) + } +} + // rolldice is the outer function which does the error handling. func rolldice(rolls int) ([]int, error) { if rolls <= 0 { From 6697966aede57029e2913e407f9b7cec34acc063 Mon Sep 17 00:00:00 2001 From: lightsta1ker Date: Tue, 4 Nov 2025 16:22:53 +0530 Subject: [PATCH 04/12] chore: rebase & refactor suggestions --- examples/dice/instrumented/main.go | 2 +- examples/dice/instrumented/rolldice.go | 11 +++++------ examples/dice/uninstrumented/main.go | 2 +- examples/dice/uninstrumented/rolldice.go | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/dice/instrumented/main.go b/examples/dice/instrumented/main.go index 9e9a160eb81..82c0b97779b 100644 --- a/examples/dice/instrumented/main.go +++ b/examples/dice/instrumented/main.go @@ -76,7 +76,7 @@ func newHTTPHandler() http.Handler { mux := http.NewServeMux() // Register handlers. - handleFunc("/rolldice", handleRollDice) + mux.Handle("/rolldice", http.HandlerFunc(handleRolldice)) // Add HTTP instrumentation for the whole server. handler := otelhttp.NewHandler(mux, "/") diff --git a/examples/dice/instrumented/rolldice.go b/examples/dice/instrumented/rolldice.go index 3f4e3a03df8..38c22619de6 100644 --- a/examples/dice/instrumented/rolldice.go +++ b/examples/dice/instrumented/rolldice.go @@ -7,7 +7,6 @@ import ( "context" "encoding/json" "errors" - "log" "math/rand" "net/http" "strconv" @@ -70,7 +69,7 @@ func init() { } } -func handleRollDice(w http.ResponseWriter, r *http.Request) { +func handleRolldice(w http.ResponseWriter, r *http.Request) { // Parse query parameters. rollsParam := r.URL.Query().Get("rolls") player := r.URL.Query().Get("player") @@ -97,16 +96,16 @@ func handleRollDice(w http.ResponseWriter, r *http.Request) { if err != nil { // Signals invalid input (<=0). w.WriteHeader(http.StatusInternalServerError) - log.Printf("ERROR: %v", err) + logger.ErrorContext(r.Context(), err.Error()) return } if player == "" { - log.Printf("DEBUG: anonymous player rolled %v", results) + logger.DebugContext(r.Context(), "anonymous player rolled", results) } else { - log.Printf("DEBUG: player=%s rolled %v", player, results) + logger.DebugContext(r.Context(), "player=%s rolled %v", player, results) } - log.Printf("INFO: %s %s -> 200 OK", r.Method, r.URL.String()) + logger.InfoContext(r.Context(), " %s %s -> 200 OK", r.Method, r.URL.String()) w.Header().Set("Content-Type", "application/json") if len(results) == 1 { diff --git a/examples/dice/uninstrumented/main.go b/examples/dice/uninstrumented/main.go index 9675e1fd4cd..0616c0921b3 100644 --- a/examples/dice/uninstrumented/main.go +++ b/examples/dice/uninstrumented/main.go @@ -65,6 +65,6 @@ func newHTTPHandler() http.Handler { mux := http.NewServeMux() // Register handlers. - mux.HandleFunc("/rolldice", handleRollDice) + mux.HandleFunc("/rolldice", handleRolldice) return mux } diff --git a/examples/dice/uninstrumented/rolldice.go b/examples/dice/uninstrumented/rolldice.go index 6497641eb1e..a66174bdb57 100644 --- a/examples/dice/uninstrumented/rolldice.go +++ b/examples/dice/uninstrumented/rolldice.go @@ -12,7 +12,7 @@ import ( "strconv" ) -func handleRollDice(w http.ResponseWriter, r *http.Request) { +func handleRolldice(w http.ResponseWriter, r *http.Request) { // Parse query parameters. rollsParam := r.URL.Query().Get("rolls") player := r.URL.Query().Get("player") From ddf3c686feb7310de0436991a8eb1ea9acff5308 Mon Sep 17 00:00:00 2001 From: lightsta1ker Date: Tue, 4 Nov 2025 22:16:27 +0530 Subject: [PATCH 05/12] chore: refactor suggestions --- examples/dice/instrumented/otel.go | 4 ++-- examples/dice/instrumented/rolldice.go | 27 +++++++++++++++----------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/examples/dice/instrumented/otel.go b/examples/dice/instrumented/otel.go index 9d79fc2279e..6e32b12aeea 100644 --- a/examples/dice/instrumented/otel.go +++ b/examples/dice/instrumented/otel.go @@ -55,7 +55,7 @@ func setupOTelSDK(ctx context.Context) (func(context.Context) error, error) { otel.SetTextMapPropagator(prop) // Set up trace provider. - tracerProvider, err := newtracerProvider(res) + tracerProvider, err := newTracerProvider(res) if err != nil { handleErr(err) return shutdown, err @@ -91,7 +91,7 @@ func newPropagator() propagation.TextMapPropagator { ) } -func newtracerProvider(res *resource.Resource) (*trace.TracerProvider, error) { +func newTracerProvider(res *resource.Resource) (*trace.TracerProvider, error) { traceExporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) if err != nil { return nil, err diff --git a/examples/dice/instrumented/rolldice.go b/examples/dice/instrumented/rolldice.go index 38c22619de6..7a00200346f 100644 --- a/examples/dice/instrumented/rolldice.go +++ b/examples/dice/instrumented/rolldice.go @@ -7,9 +7,10 @@ import ( "context" "encoding/json" "errors" - "math/rand" + "math/rand/v2" "net/http" "strconv" + "sync/atomic" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -27,7 +28,7 @@ var ( rollCnt metric.Int64Counter outcomeHist metric.Int64Histogram lastRollsGauge metric.Int64ObservableGauge - lastRolls int64 + lastRolls atomic.Int64 ) func init() { @@ -59,7 +60,7 @@ func init() { // Register the gauge callback. _, err = meter.RegisterCallback( func(ctx context.Context, o metric.Observer) error { - o.ObserveInt64(lastRollsGauge, lastRolls) + o.ObserveInt64(lastRollsGauge, lastRolls.Load()) return nil }, lastRollsGauge, @@ -73,6 +74,7 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { // Parse query parameters. rollsParam := r.URL.Query().Get("rolls") player := r.URL.Query().Get("player") + maxRolls := 1000 // Arbitrary limit to prevent Slice memory allocation with excessive size value. // Default rolls = 1 if not defined. if rollsParam == "" { @@ -82,6 +84,7 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { // Check if rolls is a number. rolls, err := strconv.Atoi(rollsParam) if err != nil { + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) msg := "Parameter rolls must be a positive integer" _ = json.NewEncoder(w).Encode(map[string]string{ @@ -93,7 +96,7 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { } results, err := rollDice(r.Context(), rolls) - if err != nil { + if err != nil || rolls > maxRolls { // Signals invalid input (<=0). w.WriteHeader(http.StatusInternalServerError) logger.ErrorContext(r.Context(), err.Error()) @@ -101,11 +104,11 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { } if player == "" { - logger.DebugContext(r.Context(), "anonymous player rolled", results) + logger.DebugContext(r.Context(), "anonymous player rolled", "results", results) } else { - logger.DebugContext(r.Context(), "player=%s rolled %v", player, results) + logger.DebugContext(r.Context(), "player", player, "results", results) } - logger.InfoContext(r.Context(), " %s %s -> 200 OK", r.Method, r.URL.String()) + logger.InfoContext(r.Context(), "Some player rolled a dice.") w.Header().Set("Content-Type", "application/json") if len(results) == 1 { @@ -123,14 +126,16 @@ func rollDice(ctx context.Context, rolls int) ([]int, error) { if rolls <= 0 { err := errors.New("rolls must be positive") span.RecordError(err) - logger.ErrorContext(ctx, "error", "error", err) return nil, err } if rolls == 1 { val := rollOnce(ctx) + rollsAttr := attribute.Int("rolls", rolls) + span.SetAttributes(rollsAttr) + rollCnt.Add(ctx, 1, metric.WithAttributes(rollsAttr)) outcomeHist.Record(ctx, int64(val)) - lastRolls = int64(rolls) + lastRolls.Store(int64(rolls)) return []int{val}, nil } @@ -143,7 +148,7 @@ func rollDice(ctx context.Context, rolls int) ([]int, error) { rollsAttr := attribute.Int("rolls", rolls) span.SetAttributes(rollsAttr) rollCnt.Add(ctx, 1, metric.WithAttributes(rollsAttr)) - lastRolls = int64(rolls) + lastRolls.Store(int64(rolls)) return results, nil } @@ -152,7 +157,7 @@ func rollOnce(ctx context.Context) int { ctx, span := tracer.Start(ctx, "rollOnce") defer span.End() - roll := 1 + rand.Intn(6) //nolint:gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand) is ignored as this is not security-sensitive. + roll := 1 + rand.IntN(6) //nolint:gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand) is ignored as this is not security-sensitive. rollValueAttr := attribute.Int("roll.value", roll) span.SetAttributes(rollValueAttr) From fbacc37ef1c771c4de44e1ba18d7a9768fba901d Mon Sep 17 00:00:00 2001 From: lightsta1ker Date: Tue, 4 Nov 2025 22:30:55 +0530 Subject: [PATCH 06/12] fix: Limiting memory allocation for rolls --- examples/dice/uninstrumented/rolldice.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/dice/uninstrumented/rolldice.go b/examples/dice/uninstrumented/rolldice.go index a66174bdb57..42e06133d79 100644 --- a/examples/dice/uninstrumented/rolldice.go +++ b/examples/dice/uninstrumented/rolldice.go @@ -16,6 +16,7 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { // Parse query parameters. rollsParam := r.URL.Query().Get("rolls") player := r.URL.Query().Get("player") + maxRolls := 1000 // Arbitrary limit to prevent Slice memory allocation with excessive size value. // Default rolls = 1 if not defined. if rollsParam == "" { @@ -36,7 +37,7 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { } results, err := rolldice(rolls) - if err != nil { + if err != nil || rolls > maxRolls { // Signals invalid input (<=0). w.WriteHeader(http.StatusInternalServerError) log.Printf("ERROR: %v", err) From 6d00f408e6e5e798b1ca0175808162d2f5a2b0de Mon Sep 17 00:00:00 2001 From: lightsta1ker Date: Wed, 5 Nov 2025 15:40:47 +0530 Subject: [PATCH 07/12] chore: consolidating rollDice observability logic --- examples/dice/instrumented/rolldice.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/examples/dice/instrumented/rolldice.go b/examples/dice/instrumented/rolldice.go index 7a00200346f..4115ed95f91 100644 --- a/examples/dice/instrumented/rolldice.go +++ b/examples/dice/instrumented/rolldice.go @@ -129,16 +129,6 @@ func rollDice(ctx context.Context, rolls int) ([]int, error) { return nil, err } - if rolls == 1 { - val := rollOnce(ctx) - rollsAttr := attribute.Int("rolls", rolls) - span.SetAttributes(rollsAttr) - rollCnt.Add(ctx, 1, metric.WithAttributes(rollsAttr)) - outcomeHist.Record(ctx, int64(val)) - lastRolls.Store(int64(rolls)) - return []int{val}, nil - } - results := make([]int, rolls) for i := 0; i < rolls; i++ { results[i] = rollOnce(ctx) From c066c03c975530aa9792f548c968f8b5969aacdb Mon Sep 17 00:00:00 2001 From: lightsta1ker Date: Wed, 5 Nov 2025 19:59:52 +0530 Subject: [PATCH 08/12] chore: refactoring suggestion --- examples/dice/instrumented/rolldice.go | 3 ++- examples/dice/uninstrumented/rolldice.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/dice/instrumented/rolldice.go b/examples/dice/instrumented/rolldice.go index 4115ed95f91..172c3b3e978 100644 --- a/examples/dice/instrumented/rolldice.go +++ b/examples/dice/instrumented/rolldice.go @@ -71,10 +71,11 @@ func init() { } func handleRolldice(w http.ResponseWriter, r *http.Request) { + const maxRolls = 1000 // Arbitrary limit to prevent Slice memory allocation with excessive size value. + // Parse query parameters. rollsParam := r.URL.Query().Get("rolls") player := r.URL.Query().Get("player") - maxRolls := 1000 // Arbitrary limit to prevent Slice memory allocation with excessive size value. // Default rolls = 1 if not defined. if rollsParam == "" { diff --git a/examples/dice/uninstrumented/rolldice.go b/examples/dice/uninstrumented/rolldice.go index 42e06133d79..f669b2f6c83 100644 --- a/examples/dice/uninstrumented/rolldice.go +++ b/examples/dice/uninstrumented/rolldice.go @@ -13,10 +13,11 @@ import ( ) func handleRolldice(w http.ResponseWriter, r *http.Request) { + const maxRolls = 1000 // Arbitrary limit to prevent Slice memory allocation with excessive size value. + // Parse query parameters. rollsParam := r.URL.Query().Get("rolls") player := r.URL.Query().Get("player") - maxRolls := 1000 // Arbitrary limit to prevent Slice memory allocation with excessive size value. // Default rolls = 1 if not defined. if rollsParam == "" { From 96f24739bb7cb7bdd396f44e64af17697df33f47 Mon Sep 17 00:00:00 2001 From: lightsta1ker Date: Wed, 19 Nov 2025 11:56:58 +0530 Subject: [PATCH 09/12] fix: lint fixes & error handling --- examples/dice/instrumented/rolldice.go | 27 ++++++++++++++++++------ examples/dice/uninstrumented/rolldice.go | 19 ++++++++++++++--- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/examples/dice/instrumented/rolldice.go b/examples/dice/instrumented/rolldice.go index 172c3b3e978..17012ed3b93 100644 --- a/examples/dice/instrumented/rolldice.go +++ b/examples/dice/instrumented/rolldice.go @@ -59,7 +59,7 @@ func init() { // Register the gauge callback. _, err = meter.RegisterCallback( - func(ctx context.Context, o metric.Observer) error { + func(_ context.Context, o metric.Observer) error { o.ObserveInt64(lastRollsGauge, lastRolls.Load()) return nil }, @@ -96,8 +96,14 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { return } + if rolls > maxRolls { + w.WriteHeader(http.StatusInternalServerError) + logger.ErrorContext(r.Context(), "rolls parameter exceeds maximum allowed value") + return + } + results, err := rollDice(r.Context(), rolls) - if err != nil || rolls > maxRolls { + if err != nil { // Signals invalid input (<=0). w.WriteHeader(http.StatusInternalServerError) logger.ErrorContext(r.Context(), err.Error()) @@ -107,15 +113,22 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { if player == "" { logger.DebugContext(r.Context(), "anonymous player rolled", "results", results) } else { - logger.DebugContext(r.Context(), "player", player, "results", results) + logger.DebugContext(r.Context(), "player rolled dice", "player", player, "results", results) } logger.InfoContext(r.Context(), "Some player rolled a dice.") w.Header().Set("Content-Type", "application/json") if len(results) == 1 { - json.NewEncoder(w).Encode(results[0]) + writeJSON(r.Context(), w, results[0]) } else { - json.NewEncoder(w).Encode(results) + writeJSON(r.Context(), w, results) + } +} + +func writeJSON(ctx context.Context, w http.ResponseWriter, v any) { + if err := json.NewEncoder(w).Encode(v); err != nil { + w.WriteHeader(http.StatusInternalServerError) + logger.ErrorContext(ctx, "json encode failed", "error", err) } } @@ -131,7 +144,7 @@ func rollDice(ctx context.Context, rolls int) ([]int, error) { } results := make([]int, rolls) - for i := 0; i < rolls; i++ { + for i := range rolls { results[i] = rollOnce(ctx) outcomeHist.Record(ctx, int64(results[i])) } @@ -145,7 +158,7 @@ func rollDice(ctx context.Context, rolls int) ([]int, error) { // rollOnce is the inner function — returns a random number 1–6. func rollOnce(ctx context.Context) int { - ctx, span := tracer.Start(ctx, "rollOnce") + _, span := tracer.Start(ctx, "rollOnce") defer span.End() roll := 1 + rand.IntN(6) //nolint:gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand) is ignored as this is not security-sensitive. diff --git a/examples/dice/uninstrumented/rolldice.go b/examples/dice/uninstrumented/rolldice.go index f669b2f6c83..22a965bbbdd 100644 --- a/examples/dice/uninstrumented/rolldice.go +++ b/examples/dice/uninstrumented/rolldice.go @@ -37,8 +37,14 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { return } + if rolls > maxRolls { + w.WriteHeader(http.StatusInternalServerError) + log.Printf("ERROR: rolls parameter exceeds maximum allowed value") + return + } + results, err := rolldice(rolls) - if err != nil || rolls > maxRolls { + if err != nil { // Signals invalid input (<=0). w.WriteHeader(http.StatusInternalServerError) log.Printf("ERROR: %v", err) @@ -54,9 +60,16 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if len(results) == 1 { - json.NewEncoder(w).Encode(results[0]) + writeJSON(w, results[0]) } else { - json.NewEncoder(w).Encode(results) + writeJSON(w, results) + } +} + +func writeJSON(w http.ResponseWriter, v any) { + if err := json.NewEncoder(w).Encode(v); err != nil { + w.WriteHeader(http.StatusInternalServerError) + log.Printf("ERROR: %v", err) } } From 05421c36d1a26411fbc26870280c79187b6f1cb5 Mon Sep 17 00:00:00 2001 From: lightsta1ker Date: Wed, 19 Nov 2025 13:25:09 +0530 Subject: [PATCH 10/12] fix: refactoring suggestions --- examples/dice/instrumented/rolldice.go | 19 +++++++++---------- examples/dice/uninstrumented/rolldice.go | 20 ++++++++------------ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/examples/dice/instrumented/rolldice.go b/examples/dice/instrumented/rolldice.go index 17012ed3b93..9438aea3c54 100644 --- a/examples/dice/instrumented/rolldice.go +++ b/examples/dice/instrumented/rolldice.go @@ -71,7 +71,6 @@ func init() { } func handleRolldice(w http.ResponseWriter, r *http.Request) { - const maxRolls = 1000 // Arbitrary limit to prevent Slice memory allocation with excessive size value. // Parse query parameters. rollsParam := r.URL.Query().Get("rolls") @@ -96,15 +95,8 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { return } - if rolls > maxRolls { - w.WriteHeader(http.StatusInternalServerError) - logger.ErrorContext(r.Context(), "rolls parameter exceeds maximum allowed value") - return - } - results, err := rollDice(r.Context(), rolls) if err != nil { - // Signals invalid input (<=0). w.WriteHeader(http.StatusInternalServerError) logger.ErrorContext(r.Context(), err.Error()) return @@ -132,11 +124,18 @@ func writeJSON(ctx context.Context, w http.ResponseWriter, v any) { } } -// rollDice is the outer function which Does the error handling. func rollDice(ctx context.Context, rolls int) ([]int, error) { + const maxRolls = 1000 // Arbitrary limit to prevent Slice memory allocation with excessive size value. + ctx, span := tracer.Start(ctx, "rollDice") defer span.End() + if rolls > maxRolls { + err := errors.New("rolls parameter exceeds maximum allowed value") + span.RecordError(err) + return nil, err + } + if rolls <= 0 { err := errors.New("rolls must be positive") span.RecordError(err) @@ -156,7 +155,7 @@ func rollDice(ctx context.Context, rolls int) ([]int, error) { return results, nil } -// rollOnce is the inner function — returns a random number 1–6. +// rollOnce returns a random number between 1 and 6. func rollOnce(ctx context.Context) int { _, span := tracer.Start(ctx, "rollOnce") defer span.End() diff --git a/examples/dice/uninstrumented/rolldice.go b/examples/dice/uninstrumented/rolldice.go index 22a965bbbdd..2e33f61608f 100644 --- a/examples/dice/uninstrumented/rolldice.go +++ b/examples/dice/uninstrumented/rolldice.go @@ -13,8 +13,6 @@ import ( ) func handleRolldice(w http.ResponseWriter, r *http.Request) { - const maxRolls = 1000 // Arbitrary limit to prevent Slice memory allocation with excessive size value. - // Parse query parameters. rollsParam := r.URL.Query().Get("rolls") player := r.URL.Query().Get("player") @@ -37,15 +35,8 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { return } - if rolls > maxRolls { - w.WriteHeader(http.StatusInternalServerError) - log.Printf("ERROR: rolls parameter exceeds maximum allowed value") - return - } - results, err := rolldice(rolls) if err != nil { - // Signals invalid input (<=0). w.WriteHeader(http.StatusInternalServerError) log.Printf("ERROR: %v", err) return @@ -73,8 +64,13 @@ func writeJSON(w http.ResponseWriter, v any) { } } -// rolldice is the outer function which does the error handling. func rolldice(rolls int) ([]int, error) { + const maxRolls = 1000 // Arbitrary limit to prevent Slice memory allocation with excessive size value. + + if rolls > maxRolls { + return nil, errors.New("rolls parameter exceeds maximum allowed value") + } + if rolls <= 0 { return nil, errors.New("rolls must be positive") } @@ -84,13 +80,13 @@ func rolldice(rolls int) ([]int, error) { } results := make([]int, rolls) - for i := 0; i < rolls; i++ { + for i := range rolls { results[i] = rollOnce() } return results, nil } -// rollOnce is the inner function — returns a random number 1–6. +// rollOnce returns a random number between 1 and 6. func rollOnce() int { roll := 1 + rand.Intn(6) //nolint:gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand) is ignored as this is not security-sensitive. return roll From 2edd44564f5966be9c439de54eef421ec465316b Mon Sep 17 00:00:00 2001 From: lightsta1ker Date: Wed, 19 Nov 2025 15:58:11 +0530 Subject: [PATCH 11/12] chore: format with gofumpt --- examples/dice/instrumented/rolldice.go | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/dice/instrumented/rolldice.go b/examples/dice/instrumented/rolldice.go index 9438aea3c54..4c44e57537e 100644 --- a/examples/dice/instrumented/rolldice.go +++ b/examples/dice/instrumented/rolldice.go @@ -71,7 +71,6 @@ func init() { } func handleRolldice(w http.ResponseWriter, r *http.Request) { - // Parse query parameters. rollsParam := r.URL.Query().Get("rolls") player := r.URL.Query().Get("player") From 837eca303ee969114b9157e47f5589de56a7eb23 Mon Sep 17 00:00:00 2001 From: lightsta1ker Date: Mon, 24 Nov 2025 15:21:35 +0530 Subject: [PATCH 12/12] chore: rebasing & error handling changes --- examples/dice/instrumented/otel.go | 6 +++++- examples/dice/instrumented/rolldice.go | 20 ++++++++++++++++++-- examples/dice/uninstrumented/rolldice.go | 14 ++++++++++++-- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/examples/dice/instrumented/otel.go b/examples/dice/instrumented/otel.go index 6e32b12aeea..af55dcaac33 100644 --- a/examples/dice/instrumented/otel.go +++ b/examples/dice/instrumented/otel.go @@ -43,12 +43,16 @@ func setupOTelSDK(ctx context.Context) (func(context.Context) error, error) { err = errors.Join(inErr, shutdown(ctx)) } - res, _ := resource.New(ctx, + res, resErr := resource.New(ctx, resource.WithFromEnv(), // reads OTEL_SERVICE_NAME & OTEL_RESOURCE_ATTRIBUTES. resource.WithProcess(), resource.WithHost(), resource.WithTelemetrySDK(), ) + if resErr != nil { + handleErr(resErr) + return shutdown, resErr + } // Set up propagator. prop := newPropagator() diff --git a/examples/dice/instrumented/rolldice.go b/examples/dice/instrumented/rolldice.go index 4c44e57537e..32ecbb756aa 100644 --- a/examples/dice/instrumented/rolldice.go +++ b/examples/dice/instrumented/rolldice.go @@ -96,7 +96,13 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { results, err := rollDice(r.Context(), rolls) if err != nil { + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) + msg := "Internal server error" + _ = json.NewEncoder(w).Encode(map[string]string{ + "status": "error", + "message": msg, + }) logger.ErrorContext(r.Context(), err.Error()) return } @@ -117,10 +123,20 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { } func writeJSON(ctx context.Context, w http.ResponseWriter, v any) { - if err := json.NewEncoder(w).Encode(v); err != nil { + data, err := json.Marshal(v) + if err != nil { + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) + _ = json.NewEncoder(w).Encode(map[string]string{ + "status": "error", + "message": "Internal Server Error", + }) logger.ErrorContext(ctx, "json encode failed", "error", err) + return } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write(data) } func rollDice(ctx context.Context, rolls int) ([]int, error) { @@ -149,7 +165,7 @@ func rollDice(ctx context.Context, rolls int) ([]int, error) { rollsAttr := attribute.Int("rolls", rolls) span.SetAttributes(rollsAttr) - rollCnt.Add(ctx, 1, metric.WithAttributes(rollsAttr)) + rollCnt.Add(ctx, int64(rolls), metric.WithAttributes(rollsAttr)) lastRolls.Store(int64(rolls)) return results, nil } diff --git a/examples/dice/uninstrumented/rolldice.go b/examples/dice/uninstrumented/rolldice.go index 2e33f61608f..30e5e786158 100644 --- a/examples/dice/uninstrumented/rolldice.go +++ b/examples/dice/uninstrumented/rolldice.go @@ -37,6 +37,7 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { results, err := rolldice(rolls) if err != nil { + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) log.Printf("ERROR: %v", err) return @@ -49,7 +50,6 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { } log.Printf("INFO: %s %s -> 200 OK", r.Method, r.URL.String()) - w.Header().Set("Content-Type", "application/json") if len(results) == 1 { writeJSON(w, results[0]) } else { @@ -58,10 +58,20 @@ func handleRolldice(w http.ResponseWriter, r *http.Request) { } func writeJSON(w http.ResponseWriter, v any) { - if err := json.NewEncoder(w).Encode(v); err != nil { + data, err := json.Marshal(v) + if err != nil { + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) + _ = json.NewEncoder(w).Encode(map[string]string{ + "status": "error", + "message": "Internal Server Error", + }) log.Printf("ERROR: %v", err) + return } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write(data) } func rolldice(rolls int) ([]int, error) {