-
Notifications
You must be signed in to change notification settings - Fork 769
Updating dice example based on Updated Specification for the reference application
#8099
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f89e2b5
a337ba6
da2e8cd
08b7f61
6697966
ddf3c68
ccea55d
fbacc37
a474139
6d00f40
c066c03
ae85955
96f2473
05421c3
2edd445
06b7cc6
4440f23
837eca3
20455e4
e058c34
3659825
8501bc4
f3baabb
35740f7
36394bb
76387e9
7179932
da08360
2a0b02f
49f04e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,13 +4,17 @@ | |||||||||||||||||||||||||||||||||||
| package main | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||
| "io" | ||||||||||||||||||||||||||||||||||||
| "math/rand" | ||||||||||||||||||||||||||||||||||||
| "context" | ||||||||||||||||||||||||||||||||||||
| "encoding/json" | ||||||||||||||||||||||||||||||||||||
| "errors" | ||||||||||||||||||||||||||||||||||||
| "math/rand/v2" | ||||||||||||||||||||||||||||||||||||
| "net/http" | ||||||||||||||||||||||||||||||||||||
| "strconv" | ||||||||||||||||||||||||||||||||||||
| "sync/atomic" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| "go.opentelemetry.io/otel" | ||||||||||||||||||||||||||||||||||||
| "go.opentelemetry.io/otel/attribute" | ||||||||||||||||||||||||||||||||||||
| "go.opentelemetry.io/otel/codes" | ||||||||||||||||||||||||||||||||||||
| "go.opentelemetry.io/otel/metric" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| "go.opentelemetry.io/contrib/bridges/otelslog" | ||||||||||||||||||||||||||||||||||||
|
|
@@ -19,42 +23,163 @@ 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 atomic.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 most recent rolled value"), | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||
| panic(err) | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Register the gauge callback. | ||||||||||||||||||||||||||||||||||||
| _, err = meter.RegisterCallback( | ||||||||||||||||||||||||||||||||||||
| func(_ context.Context, o metric.Observer) error { | ||||||||||||||||||||||||||||||||||||
| o.ObserveInt64(lastRollsGauge, lastRolls.Load()) | ||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| lastRollsGauge, | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||
| panic(err) | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| func rolldice(w http.ResponseWriter, r *http.Request) { | ||||||||||||||||||||||||||||||||||||
| ctx, span := tracer.Start(r.Context(), "roll") | ||||||||||||||||||||||||||||||||||||
| defer span.End() | ||||||||||||||||||||||||||||||||||||
| 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" | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| 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. | ||||||||||||||||||||||||||||||||||||
| // 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{ | ||||||||||||||||||||||||||||||||||||
|
pellared marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||
| "status": "error", | ||||||||||||||||||||||||||||||||||||
| "message": msg, | ||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||
| logger.WarnContext(r.Context(), msg) | ||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| var msg string | ||||||||||||||||||||||||||||||||||||
| if player := r.PathValue("player"); player != "" { | ||||||||||||||||||||||||||||||||||||
| msg = player + " is rolling the dice" | ||||||||||||||||||||||||||||||||||||
| results, err := rollDice(r.Context(), rolls) | ||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||
| w.Header().Set("Content-Type", "application/json") | ||||||||||||||||||||||||||||||||||||
| w.WriteHeader(http.StatusInternalServerError) | ||||||||||||||||||||||||||||||||||||
| msg := "Internal server error" | ||||||||||||||||||||||||||||||||||||
|
pellared marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||
| _ = json.NewEncoder(w).Encode(map[string]string{ | ||||||||||||||||||||||||||||||||||||
| "status": "error", | ||||||||||||||||||||||||||||||||||||
| "message": msg, | ||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||
| logger.ErrorContext(r.Context(), err.Error()) | ||||||||||||||||||||||||||||||||||||
|
pellared marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if player == "" { | ||||||||||||||||||||||||||||||||||||
| logger.DebugContext(r.Context(), "anonymous player rolled", "results", results) | ||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||
| msg = "Anonymous player is rolling the dice" | ||||||||||||||||||||||||||||||||||||
| logger.DebugContext(r.Context(), "player rolled dice", "player", player, "results", results) | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
pellared marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||
| logger.InfoContext(ctx, msg, "result", roll) | ||||||||||||||||||||||||||||||||||||
| logger.InfoContext(r.Context(), "Some player rolled a dice.") | ||||||||||||||||||||||||||||||||||||
|
pellared marked this conversation as resolved.
pellared marked this conversation as resolved.
pellared marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| w.Header().Set("Content-Type", "application/json") | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
| w.Header().Set("Content-Type", "application/json") |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The uninstrumented version has special handling when rolls==1 (lines 88-89) that returns early without allocating a slice and using a loop. However, the instrumented version doesn't have this optimization and always allocates a slice and uses a loop even for a single roll. This creates an inconsistency in behavior between the two versions.
| results := make([]int, rolls) | |
| for i := range rolls { | |
| if rolls == 1 { | |
| // Fast path for a single roll to mirror the uninstrumented implementation. | |
| result := rollOnce(ctx) | |
| outcomeHist.Record(ctx, int64(result)) | |
| rollsAttr := attribute.Int("rolls", rolls) | |
| span.SetAttributes(rollsAttr) | |
| rollCnt.Add(ctx, int64(rolls), metric.WithAttributes(rollsAttr)) | |
| lastRolls.Store(int64(rolls)) | |
| return []int{result}, nil | |
| } | |
| results := make([]int, rolls) | |
| for i := range results { |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The instrumented version uses math/rand/v2 and rand.IntN, while the uninstrumented version uses math/rand and rand.Intn. For consistency and to serve as a proper reference example, both versions should use the same random number generation approach.
Uh oh!
There was an error while loading. Please reload this page.