Skip to content

Commit 256851f

Browse files
committed
Add telemetry package
1 parent 554716f commit 256851f

13 files changed

Lines changed: 1318 additions & 2 deletions

docs/features.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
|---------|-------------|
1515
| `web` | Chi router helpers, response utilities, flash messages |
1616
| `htmx` | HTMX primitives (triggers, actions, targets, swaps), response headers, template helpers |
17-
| `middleware` | Request ID, role-based access, rate limiting, locale detection, static cache |
17+
| `middleware` | Request ID, role-based access, rate limiting, locale detection, static cache, telemetry |
1818
| `render` | Template FuncMap utilities, i18n integration |
1919
| `ui` | UI kit with HTMX-first components (Chip, Label, Button, Alert, Flash, Toast, Link, Form, Table, Nav) |
2020
| `format` | Formatting utilities (Price, Number) for templates |
@@ -55,6 +55,7 @@
5555
|---------|-------------|
5656
| `mailer` | Email delivery (SMTP, SendGrid, AWS SES) |
5757
| `pubsub` | Publish/subscribe messaging |
58+
| `telemetry` | Request counting, crash collection, settings schemas |
5859
| `testhelper` | Testing utilities |
59-
| `fake` | Test doubles (mailer) |
60+
| `fake` | Test doubles (mailer, telemetry) |
6061

fake/telemetry.go

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package fake
2+
3+
import "sync"
4+
5+
// CounterCall records an IncrementRequests call.
6+
type CounterCall struct{}
7+
8+
// Counter is a test double for telemetry.RequestCounter.
9+
type Counter struct {
10+
mu sync.Mutex
11+
12+
IncrementFunc func()
13+
incrementCalls []CounterCall
14+
GetAndResetFunc func() int64
15+
getAndResetCalls int
16+
simulatedCount int64
17+
}
18+
19+
// NewCounter creates a new fake Counter.
20+
func NewCounter() *Counter {
21+
return &Counter{}
22+
}
23+
24+
// IncrementRequests records the call and invokes IncrementFunc if set.
25+
func (c *Counter) IncrementRequests() {
26+
c.mu.Lock()
27+
defer c.mu.Unlock()
28+
29+
c.incrementCalls = append(c.incrementCalls, CounterCall{})
30+
c.simulatedCount++
31+
32+
if c.IncrementFunc != nil {
33+
c.IncrementFunc()
34+
}
35+
}
36+
37+
// GetAndResetRequests returns simulatedCount and resets it, or calls GetAndResetFunc if set.
38+
func (c *Counter) GetAndResetRequests() int64 {
39+
c.mu.Lock()
40+
defer c.mu.Unlock()
41+
42+
c.getAndResetCalls++
43+
44+
if c.GetAndResetFunc != nil {
45+
return c.GetAndResetFunc()
46+
}
47+
48+
count := c.simulatedCount
49+
c.simulatedCount = 0
50+
return count
51+
}
52+
53+
// Reset clears all recorded calls and the simulated count.
54+
func (c *Counter) Reset() {
55+
c.mu.Lock()
56+
defer c.mu.Unlock()
57+
58+
c.incrementCalls = nil
59+
c.getAndResetCalls = 0
60+
c.simulatedCount = 0
61+
}
62+
63+
// IncrementCount returns the number of IncrementRequests calls.
64+
func (c *Counter) IncrementCount() int {
65+
c.mu.Lock()
66+
defer c.mu.Unlock()
67+
return len(c.incrementCalls)
68+
}
69+
70+
// GetAndResetCount returns the number of GetAndResetRequests calls.
71+
func (c *Counter) GetAndResetCount() int {
72+
c.mu.Lock()
73+
defer c.mu.Unlock()
74+
return c.getAndResetCalls
75+
}
76+
77+
// WithCount sets the simulated count and returns the Counter for chaining.
78+
func (c *Counter) WithCount(n int64) *Counter {
79+
c.mu.Lock()
80+
defer c.mu.Unlock()
81+
c.simulatedCount = n
82+
return c
83+
}
84+
85+
// CrashCall records a RecordPanic call.
86+
type CrashCall struct {
87+
Message string
88+
Endpoint string
89+
Method string
90+
}
91+
92+
// CrashCollector is a test double for telemetry.CrashRecorder.
93+
type CrashCollector struct {
94+
mu sync.Mutex
95+
96+
RecordPanicFunc func(message, endpoint, method string)
97+
calls []CrashCall
98+
}
99+
100+
// NewCrashCollector creates a new fake CrashCollector.
101+
func NewCrashCollector() *CrashCollector {
102+
return &CrashCollector{}
103+
}
104+
105+
// RecordPanic records the call and invokes RecordPanicFunc if set.
106+
func (c *CrashCollector) RecordPanic(message, endpoint, method string) {
107+
c.mu.Lock()
108+
defer c.mu.Unlock()
109+
110+
c.calls = append(c.calls, CrashCall{
111+
Message: message,
112+
Endpoint: endpoint,
113+
Method: method,
114+
})
115+
116+
if c.RecordPanicFunc != nil {
117+
c.RecordPanicFunc(message, endpoint, method)
118+
}
119+
}
120+
121+
// Reset clears all recorded calls.
122+
func (c *CrashCollector) Reset() {
123+
c.mu.Lock()
124+
defer c.mu.Unlock()
125+
c.calls = nil
126+
}
127+
128+
// RecordCount returns the number of RecordPanic calls.
129+
func (c *CrashCollector) RecordCount() int {
130+
c.mu.Lock()
131+
defer c.mu.Unlock()
132+
return len(c.calls)
133+
}
134+
135+
// LastRecord returns the most recent CrashCall or nil if none.
136+
func (c *CrashCollector) LastRecord() *CrashCall {
137+
c.mu.Lock()
138+
defer c.mu.Unlock()
139+
140+
if len(c.calls) == 0 {
141+
return nil
142+
}
143+
call := c.calls[len(c.calls)-1]
144+
return &call
145+
}
146+
147+
// HasRecordFor returns true if any call was recorded for the given endpoint.
148+
func (c *CrashCollector) HasRecordFor(endpoint string) bool {
149+
c.mu.Lock()
150+
defer c.mu.Unlock()
151+
152+
for _, call := range c.calls {
153+
if call.Endpoint == endpoint {
154+
return true
155+
}
156+
}
157+
return false
158+
}
159+
160+
// GetCalls returns a copy of all recorded calls.
161+
func (c *CrashCollector) GetCalls() []CrashCall {
162+
c.mu.Lock()
163+
defer c.mu.Unlock()
164+
165+
result := make([]CrashCall, len(c.calls))
166+
copy(result, c.calls)
167+
return result
168+
}

0 commit comments

Comments
 (0)