Skip to content

Commit 1ff4b01

Browse files
authored
Merge pull request #52 from database-playground/metrics
2 parents 3cf406e + a74fe87 commit 1ff4b01

7 files changed

Lines changed: 106 additions & 133 deletions

File tree

cmd/backend/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"go.uber.org/fx"
55

66
"github.com/database-playground/backend-v2/internal/deps"
7+
_ "github.com/database-playground/backend-v2/internal/metrics" // Initialize metrics
78

89
_ "github.com/database-playground/backend-v2/ent/runtime"
910
_ "github.com/jackc/pgx/v5/stdlib"

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ require (
1717
github.com/joho/godotenv v1.5.1
1818
github.com/mattn/go-sqlite3 v1.14.32
1919
github.com/posthog/posthog-go v1.8.2
20+
github.com/prometheus/client_golang v1.23.2
2021
github.com/ravilushqa/otelgqlgen v0.19.0
2122
github.com/redis/rueidis v1.0.70
2223
github.com/redis/rueidis/rueidisotel v1.0.70
@@ -129,7 +130,6 @@ require (
129130
github.com/pkg/errors v0.9.1 // indirect
130131
github.com/pmezard/go-difflib v1.0.0 // indirect
131132
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
132-
github.com/prometheus/client_golang v1.23.2 // indirect
133133
github.com/prometheus/client_model v0.6.2 // indirect
134134
github.com/prometheus/common v0.67.4 // indirect
135135
github.com/prometheus/procfs v0.19.2 // indirect

graph/user.resolvers.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/events/events.go

Lines changed: 0 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +0,0 @@
1-
package events
2-
3-
import (
4-
"context"
5-
"log/slog"
6-
"strconv"
7-
"time"
8-
9-
"github.com/database-playground/backend-v2/ent"
10-
"github.com/posthog/posthog-go"
11-
"go.opentelemetry.io/otel"
12-
"go.opentelemetry.io/otel/attribute"
13-
otelcodes "go.opentelemetry.io/otel/codes"
14-
"go.opentelemetry.io/otel/trace"
15-
)
16-
17-
var tracer = otel.Tracer("dbplay.events")
18-
19-
// EventService is the service for triggering events.
20-
type EventService struct {
21-
entClient *ent.Client
22-
posthogClient posthog.Client
23-
24-
handlers []EventHandler
25-
}
26-
27-
// NewEventService creates a new EventService.
28-
func NewEventService(entClient *ent.Client, posthogClient posthog.Client) *EventService {
29-
return &EventService{
30-
entClient: entClient,
31-
posthogClient: posthogClient,
32-
handlers: []EventHandler{NewPointsGranter(entClient, posthogClient)},
33-
}
34-
}
35-
36-
// Event is the event to be triggered.
37-
type Event struct {
38-
Type EventType
39-
Payload map[string]any
40-
UserID int
41-
}
42-
43-
// EventHandler is the handler for the event.
44-
//
45-
// You can think it as the callback of the event.
46-
type EventHandler interface {
47-
HandleEvent(ctx context.Context, event *ent.Event) error
48-
}
49-
50-
// TriggerEvent triggers an event.
51-
func (s *EventService) TriggerEvent(ctx context.Context, event Event) {
52-
ctx, span := tracer.Start(ctx, "TriggerEvent",
53-
trace.WithAttributes(
54-
attribute.String("event.type", string(event.Type)),
55-
attribute.Int("user.id", event.UserID),
56-
))
57-
defer span.End()
58-
59-
err := s.triggerEvent(ctx, event)
60-
if err != nil {
61-
span.SetStatus(otelcodes.Error, "Failed to trigger event")
62-
span.RecordError(err)
63-
slog.Error("failed to trigger event", "error", err)
64-
} else {
65-
span.SetStatus(otelcodes.Ok, "Event triggered successfully")
66-
}
67-
68-
if s.posthogClient != nil {
69-
span.AddEvent("posthog.send", trace.WithAttributes(
70-
attribute.String("event.type", string(event.Type)),
71-
attribute.Int("user.id", event.UserID),
72-
))
73-
slog.Debug("sending event to PostHog", "event_type", event.Type, "user_id", event.UserID)
74-
err = s.posthogClient.Enqueue(posthog.Capture{
75-
DistinctId: strconv.Itoa(event.UserID),
76-
Event: string(event.Type),
77-
Timestamp: time.Now(),
78-
Properties: event.Payload,
79-
})
80-
if err != nil {
81-
span.RecordError(err)
82-
slog.Error("failed to send event to PostHog", "error", err)
83-
}
84-
}
85-
}
86-
87-
// triggerEvent triggers an event synchronously.
88-
func (s *EventService) triggerEvent(ctx context.Context, event Event) error {
89-
ctx, span := tracer.Start(ctx, "triggerEvent",
90-
trace.WithAttributes(
91-
attribute.String("event.type", string(event.Type)),
92-
attribute.Int("user.id", event.UserID),
93-
))
94-
defer span.End()
95-
96-
span.AddEvent("database.event.create")
97-
eventEntity, err := s.entClient.Event.Create().
98-
SetType(string(event.Type)).
99-
SetPayload(event.Payload).
100-
SetUserID(event.UserID).
101-
SetTriggeredAt(time.Now()).
102-
Save(ctx)
103-
if err != nil {
104-
span.SetStatus(otelcodes.Error, "Failed to create event")
105-
span.RecordError(err)
106-
return err
107-
}
108-
109-
span.SetAttributes(attribute.Int("event.id", eventEntity.ID))
110-
span.AddEvent("handlers.processing", trace.WithAttributes(
111-
attribute.Int("handlers.count", len(s.handlers)),
112-
))
113-
114-
for i, handler := range s.handlers {
115-
span.AddEvent("handler.executing", trace.WithAttributes(
116-
attribute.Int("handler.index", i),
117-
))
118-
err := handler.HandleEvent(ctx, eventEntity)
119-
if err != nil {
120-
span.SetStatus(otelcodes.Error, "Failed to handle event")
121-
span.RecordError(err)
122-
return err
123-
}
124-
}
125-
126-
span.SetStatus(otelcodes.Ok, "Event triggered successfully")
127-
return nil
128-
}

internal/metrics/metrics.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package metrics
2+
3+
import (
4+
"github.com/prometheus/client_golang/prometheus"
5+
"github.com/prometheus/client_golang/prometheus/promauto"
6+
)
7+
8+
var (
9+
// SubmissionTotal tracks the total number of submissions with status label (success, failed, or pending)
10+
SubmissionTotal = promauto.NewCounterVec(
11+
prometheus.CounterOpts{
12+
Name: "dbplay_submission_total",
13+
Help: "Total number of submissions by status (success, failed, or pending)",
14+
},
15+
[]string{"status"},
16+
)
17+
18+
// QuestionAttemptedTotal tracks the total number of questions attempted
19+
QuestionAttemptedTotal = promauto.NewCounter(
20+
prometheus.CounterOpts{
21+
Name: "dbplay_question_attempted_total",
22+
Help: "Total number of questions attempted",
23+
},
24+
)
25+
26+
// ReferenceAnswerExecutionErrorTotal tracks errors when executing reference answers
27+
ReferenceAnswerExecutionErrorTotal = promauto.NewCounter(
28+
prometheus.CounterOpts{
29+
Name: "dbplay_reference_answer_execution_error_total",
30+
Help: "Total number of errors when executing reference answers",
31+
},
32+
)
33+
34+
// CheatTotal tracks the total number of cheat records created
35+
CheatTotal = promauto.NewCounter(
36+
prometheus.CounterOpts{
37+
Name: "dbplay_cheat_total",
38+
Help: "Total number of cheat records created",
39+
},
40+
)
41+
42+
// LoginTotal tracks the total number of user logins
43+
LoginTotal = promauto.NewCounter(
44+
prometheus.CounterOpts{
45+
Name: "dbplay_login_total",
46+
Help: "Total number of user logins",
47+
},
48+
)
49+
50+
// EventTotal tracks the total number of events by event type
51+
EventTotal = promauto.NewCounterVec(
52+
prometheus.CounterOpts{
53+
Name: "dbplay_event_total",
54+
Help: "Total number of events by event type",
55+
},
56+
[]string{"event_type"},
57+
)
58+
)
59+
60+
// RecordSubmission records a submission with the given status
61+
func RecordSubmission(status string) {
62+
SubmissionTotal.WithLabelValues(status).Inc()
63+
}
64+
65+
// RecordQuestionAttempted records a question attempt
66+
func RecordQuestionAttempted() {
67+
QuestionAttemptedTotal.Inc()
68+
}
69+
70+
// RecordReferenceAnswerExecutionError records an error when executing a reference answer
71+
func RecordReferenceAnswerExecutionError() {
72+
ReferenceAnswerExecutionErrorTotal.Inc()
73+
}
74+
75+
// RecordCheat records a cheat record creation
76+
func RecordCheat() {
77+
CheatTotal.Inc()
78+
}
79+
80+
// RecordLogin records a user login
81+
func RecordLogin() {
82+
LoginTotal.Inc()
83+
}
84+
85+
// RecordEvent records an event with the given event type
86+
func RecordEvent(eventType string) {
87+
EventTotal.WithLabelValues(eventType).Inc()
88+
}

internal/submission/submission.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import (
77
"reflect"
88

99
"github.com/database-playground/backend-v2/ent"
10-
"github.com/database-playground/backend-v2/ent/submission"
10+
entsubmission "github.com/database-playground/backend-v2/ent/submission"
1111
"github.com/database-playground/backend-v2/internal/events"
12+
"github.com/database-playground/backend-v2/internal/metrics"
1213
"github.com/database-playground/backend-v2/internal/sqlrunner"
1314
"github.com/database-playground/backend-v2/models"
1415
"go.opentelemetry.io/otel"
@@ -80,16 +81,16 @@ func (ss *SubmissionService) SubmitAnswer(ctx context.Context, input SubmitAnswe
8081
if err != nil {
8182
span.AddEvent("answer.execution.failed")
8283
submissionModel.SetError(err.Error())
83-
submissionModel.SetStatus(submission.StatusFailed)
84+
submissionModel.SetStatus(entsubmission.StatusFailed)
8485
} else {
8586
submissionModel.SetQueryResult(result)
8687

8788
if result.MatchAnswer {
8889
span.AddEvent("answer.match.success")
89-
submissionModel.SetStatus(submission.StatusSuccess)
90+
submissionModel.SetStatus(entsubmission.StatusSuccess)
9091
} else {
9192
span.AddEvent("answer.match.failed")
92-
submissionModel.SetStatus(submission.StatusFailed)
93+
submissionModel.SetStatus(entsubmission.StatusFailed)
9394
}
9495
}
9596

@@ -106,6 +107,10 @@ func (ss *SubmissionService) SubmitAnswer(ctx context.Context, input SubmitAnswe
106107
attribute.String("submission.status", string(submission.Status)),
107108
)
108109

110+
// Record metrics
111+
metrics.RecordSubmission(string(submission.Status))
112+
metrics.RecordQuestionAttempted()
113+
109114
// Write event to database
110115
span.AddEvent("event.triggering")
111116
ss.eventService.TriggerEvent(ctx, events.Event{
@@ -137,6 +142,7 @@ func (ss *SubmissionService) runAnswer(ctx context.Context, schema, answer, refe
137142
if err != nil {
138143
span.SetStatus(otelcodes.Error, "Failed to run reference answer")
139144
span.RecordError(err)
145+
metrics.RecordReferenceAnswerExecutionError()
140146
return nil, fmt.Errorf("run reference answer: %w", err)
141147
}
142148

internal/useraccount/token.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/database-playground/backend-v2/ent"
88
"github.com/database-playground/backend-v2/internal/auth"
99
"github.com/database-playground/backend-v2/internal/events"
10+
"github.com/database-playground/backend-v2/internal/metrics"
1011
"go.opentelemetry.io/otel/attribute"
1112
otelcodes "go.opentelemetry.io/otel/codes"
1213
"go.opentelemetry.io/otel/trace"
@@ -94,6 +95,7 @@ func (c *Context) GrantToken(ctx context.Context, user *ent.User, machine string
9495
"machine": machine,
9596
},
9697
})
98+
metrics.RecordLogin()
9799
}
98100

99101
span.AddEvent("token.create.started")

0 commit comments

Comments
 (0)