Skip to content

Commit 96672bf

Browse files
committed
o11y: initial tracing implementation
1 parent b6acaec commit 96672bf

File tree

3 files changed

+198
-47
lines changed

3 files changed

+198
-47
lines changed

go.mod

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,31 @@ go 1.24.0
55
require (
66
github.com/bwmarrin/discordgo v0.28.1
77
github.com/google/uuid v1.6.0
8+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0
9+
go.opentelemetry.io/otel v1.35.0
10+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0
11+
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0
12+
go.opentelemetry.io/otel/sdk v1.35.0
13+
go.opentelemetry.io/otel/trace v1.35.0
814
)
915

1016
require (
17+
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
18+
github.com/felixge/httpsnoop v1.0.4 // indirect
19+
github.com/go-logr/logr v1.4.2 // indirect
20+
github.com/go-logr/stdr v1.2.2 // indirect
1121
github.com/gorilla/websocket v1.4.2 // indirect
12-
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
13-
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
22+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
23+
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
24+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
25+
go.opentelemetry.io/otel/metric v1.35.0 // indirect
26+
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
27+
golang.org/x/crypto v0.33.0 // indirect
28+
golang.org/x/net v0.35.0 // indirect
29+
golang.org/x/sys v0.30.0 // indirect
30+
golang.org/x/text v0.22.0 // indirect
31+
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
32+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
33+
google.golang.org/grpc v1.71.0 // indirect
34+
google.golang.org/protobuf v1.36.5 // indirect
1435
)

go.sum

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,75 @@
11
github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=
22
github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
3+
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
4+
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
5+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
6+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
8+
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
9+
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
10+
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
11+
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
12+
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
13+
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
14+
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
15+
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
16+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
17+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
318
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
419
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
520
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
621
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
7-
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
22+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
23+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
24+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
25+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
26+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
27+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
28+
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
29+
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
30+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
31+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
32+
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
33+
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
34+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
35+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
36+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
37+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
38+
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0 h1:T0Ec2E+3YZf5bgTNQVet8iTDW7oIk03tXHq+wkwIDnE=
39+
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0/go.mod h1:30v2gqH+vYGJsesLWFov8u47EpYTcIQcBjKpI6pJThg=
40+
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
41+
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
42+
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
43+
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
44+
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
45+
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
46+
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
47+
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
48+
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
49+
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
50+
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
51+
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
852
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
53+
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
54+
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
955
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
10-
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
56+
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
57+
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
1158
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
59+
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
60+
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
1261
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
1362
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
63+
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
64+
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
1465
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
66+
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
67+
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
68+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
69+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
70+
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
71+
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
72+
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
73+
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
74+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
75+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

main.go

Lines changed: 112 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,21 @@ import (
1717
"github.com/bwmarrin/discordgo"
1818
"github.com/google/uuid"
1919
"github.com/sudomateo/yeetcode/internal/leetcode"
20+
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
21+
"go.opentelemetry.io/otel"
22+
"go.opentelemetry.io/otel/attribute"
23+
"go.opentelemetry.io/otel/codes"
24+
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
25+
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
26+
"go.opentelemetry.io/otel/sdk/resource"
27+
sdktrace "go.opentelemetry.io/otel/sdk/trace"
28+
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
29+
"go.opentelemetry.io/otel/trace"
30+
)
31+
32+
var tracer = otel.GetTracerProvider().Tracer(
33+
"github.com/sudomateo/yeetcode",
34+
trace.WithSchemaURL(semconv.SchemaURL),
2035
)
2136

2237
func main() {
@@ -29,6 +44,43 @@ func main() {
2944
}
3045

3146
func run(ctx context.Context, logger *slog.Logger) error {
47+
var exporter sdktrace.SpanExporter
48+
49+
axiomApiToken := os.Getenv("AXIOM_API_TOKEN")
50+
if axiomApiToken == "" {
51+
stdoutExp, err := stdouttrace.New()
52+
if err != nil {
53+
return fmt.Errorf("failed initializing stdout exporter: %w", err)
54+
}
55+
exporter = stdoutExp
56+
} else {
57+
httpExp, err := otlptracehttp.New(ctx,
58+
otlptracehttp.WithEndpoint("api.axiom.co"),
59+
otlptracehttp.WithHeaders(map[string]string{
60+
"Authorization": fmt.Sprintf("Bearer %s", axiomApiToken),
61+
"X-AXIOM-DATASET": "yeetcode",
62+
}),
63+
)
64+
if err != nil {
65+
return fmt.Errorf("failed initializing trace exporter: %w", err)
66+
}
67+
exporter = httpExp
68+
}
69+
70+
tracerProvider := sdktrace.NewTracerProvider(
71+
sdktrace.WithBatcher(exporter),
72+
sdktrace.WithResource(resource.NewWithAttributes(
73+
semconv.SchemaURL,
74+
semconv.ServiceName("yeetcode"),
75+
)),
76+
)
77+
78+
defer func() {
79+
_ = tracerProvider.Shutdown(ctx)
80+
}()
81+
82+
otel.SetTracerProvider(tracerProvider)
83+
3284
discordToken := os.Getenv("DISCORD_TOKEN")
3385
if discordToken == "" {
3486
return errors.New("DISCORD_TOKEN must be provided")
@@ -53,35 +105,37 @@ func run(ctx context.Context, logger *slog.Logger) error {
53105

54106
mux := http.NewServeMux()
55107

56-
mux.HandleFunc("POST /", func(w http.ResponseWriter, r *http.Request) {
108+
handleFunc := func(pattern string, handlerFunc func(http.ResponseWriter, *http.Request)) {
109+
handler := otelhttp.WithRouteTag(pattern, http.HandlerFunc(handlerFunc))
110+
mux.Handle(pattern, handler)
111+
}
112+
113+
handleFunc("POST /", func(w http.ResponseWriter, r *http.Request) {
114+
ctx, span := tracer.Start(r.Context(), "interaction")
115+
defer span.End()
116+
57117
requestID, err := uuid.NewRandom()
58118
if err != nil {
59-
logger.Error("failed generating request id")
119+
span.RecordError(err)
60120
requestID = uuid.UUID([16]byte{})
61121
}
62122

63-
logger := logger.With(
64-
"request.path", r.URL.Path,
65-
"request.id", requestID,
123+
span.SetAttributes(
124+
attribute.String("request.id", requestID.String()),
66125
)
67126

68-
logger.Info("received request")
69-
70-
requestReceivedAt := time.Now()
71-
defer func() {
72-
logger.Info("finished handling request", "duration_ms", time.Since(requestReceivedAt).Milliseconds())
73-
}()
74-
75127
if !discordgo.VerifyInteraction(r, publicKeyBytes) {
76-
logger.Error("failed verifying interaction")
128+
span.RecordError(err)
129+
span.SetStatus(codes.Error, "failed verifying interaction")
77130
w.WriteHeader(http.StatusBadRequest)
78131
return
79132
}
80133

81134
defer r.Body.Close()
82135
var interaction discordgo.Interaction
83136
if err := json.NewDecoder(r.Body).Decode(&interaction); err != nil {
84-
logger.Error("invalid interaction payload", "error", err)
137+
span.RecordError(err)
138+
span.SetStatus(codes.Error, "invalid interaction payload")
85139
w.WriteHeader(http.StatusBadRequest)
86140
return
87141
}
@@ -93,7 +147,8 @@ func run(ctx context.Context, logger *slog.Logger) error {
93147
}
94148

95149
if err := json.NewEncoder(w).Encode(resp); err != nil {
96-
logger.Error("failed sending ping response", "error", err)
150+
span.RecordError(err)
151+
span.SetStatus(codes.Error, "failed sending ping response")
97152
w.WriteHeader(http.StatusInternalServerError)
98153
return
99154
}
@@ -102,38 +157,17 @@ func run(ctx context.Context, logger *slog.Logger) error {
102157
}
103158

104159
if interaction.Type != discordgo.InteractionApplicationCommand {
105-
logger.Error("unsupported interaction type", "type", interaction.Type)
160+
span.RecordError(err)
161+
span.SetStatus(codes.Error, "unsupported interaction type")
106162
w.WriteHeader(http.StatusBadRequest)
107163
return
108164
}
109165

110166
applicationCommandData := interaction.ApplicationCommandData()
111-
112-
var difficultyOpt string
113-
114-
for _, v := range applicationCommandData.Options {
115-
if v.Name == "difficulty" {
116-
difficultyOpt = strings.ToUpper(v.StringValue())
117-
break
118-
}
119-
}
120-
121-
var difficulty leetcode.Difficulty
122-
123-
switch leetcode.Difficulty(difficultyOpt) {
124-
case leetcode.DifficultyEasy:
125-
difficulty = leetcode.DifficultyEasy
126-
case leetcode.DifficultyMedium:
127-
difficulty = leetcode.DifficultyMedium
128-
case leetcode.DifficultyHard:
129-
difficulty = leetcode.DifficultyHard
130-
default:
131-
difficulty = leetcode.RandomDifficulty()
132-
}
133-
134-
lcResp, err := leetcodeClient.RandomQuestion(difficulty)
167+
lcResp, err := fetchLeetCodeQuestion(ctx, &leetcodeClient, &applicationCommandData)
135168
if err != nil {
136-
logger.Error("failed to retrieve leetcode question", "error", err)
169+
span.RecordError(err)
170+
span.SetStatus(codes.Error, "failed to retreive leetcode question")
137171
w.WriteHeader(http.StatusInternalServerError)
138172
return
139173
}
@@ -146,12 +180,15 @@ func run(ctx context.Context, logger *slog.Logger) error {
146180
}
147181

148182
if err := discordClient.InteractionRespond(&interaction, &interactionResp); err != nil {
149-
logger.Error("failed responding to interaction")
183+
span.RecordError(err)
184+
span.SetStatus(codes.Error, "failed responding to interaction")
150185
w.WriteHeader(http.StatusInternalServerError)
151186
return
152187
}
153188

154-
logger.Info("responded to interaction", "leetcode", lcResp.Data.RandomQuestion.TitleSlug, "difficulty", difficulty)
189+
span.SetAttributes(
190+
attribute.String("leetcode.title_slug", lcResp.Data.RandomQuestion.TitleSlug),
191+
)
155192

156193
w.WriteHeader(http.StatusOK)
157194
return
@@ -188,3 +225,35 @@ func run(ctx context.Context, logger *slog.Logger) error {
188225

189226
return nil
190227
}
228+
229+
// This is just here to have a parent/child span relationship for Axiom.
230+
func fetchLeetCodeQuestion(ctx context.Context, leetcodeClient *leetcode.Client, applicationCommandData *discordgo.ApplicationCommandInteractionData) (leetcode.RandomQuestionResponse, error) {
231+
ctx, span := tracer.Start(ctx, "fetchLeetCodeQuestion")
232+
defer span.End()
233+
234+
var difficultyOpt string
235+
236+
for _, v := range applicationCommandData.Options {
237+
if v.Name == "difficulty" {
238+
difficultyOpt = strings.ToUpper(v.StringValue())
239+
break
240+
}
241+
}
242+
243+
var difficulty leetcode.Difficulty
244+
245+
switch leetcode.Difficulty(difficultyOpt) {
246+
case leetcode.DifficultyEasy:
247+
difficulty = leetcode.DifficultyEasy
248+
case leetcode.DifficultyMedium:
249+
difficulty = leetcode.DifficultyMedium
250+
case leetcode.DifficultyHard:
251+
difficulty = leetcode.DifficultyHard
252+
default:
253+
difficulty = leetcode.RandomDifficulty()
254+
}
255+
256+
span.SetAttributes(attribute.String("leetcode.difficulty", string(difficulty)))
257+
258+
return leetcodeClient.RandomQuestion(difficulty)
259+
}

0 commit comments

Comments
 (0)