Skip to content

Commit d3a4296

Browse files
author
F.
committed
{feat) docs
1 parent e59f4f3 commit d3a4296

File tree

15 files changed

+3614
-0
lines changed

15 files changed

+3614
-0
lines changed

docs/docs/advanced/context.md

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
# Context Integration
2+
3+
Understanding how to effectively integrate error handling with Go's context package is crucial for building robust, context-aware applications. Context integration allows us to carry request-scoped data, handle timeouts gracefully, and maintain traceability throughout our application's error handling flow.
4+
5+
## Understanding Context in Error Handling
6+
7+
Go's context package serves multiple purposes in error handling:
8+
9+
- Carrying request-scoped values (like request IDs or user information)
10+
- Managing timeouts and cancellation
11+
- Ensuring proper resource cleanup
12+
- Maintaining traceability across service boundaries
13+
14+
Let's explore how ewrap integrates with context to enhance error handling capabilities.
15+
16+
## Basic Context Integration
17+
18+
The most straightforward way to integrate context with error handling is through the WithContext option:
19+
20+
```go
21+
func processUserRequest(ctx context.Context, userID string) error {
22+
// Create an error with context
23+
if err := validateUser(userID); err != nil {
24+
return ewrap.Wrap(err, "user validation failed",
25+
ewrap.WithContext(ctx, ErrorTypeValidation, SeverityError))
26+
}
27+
28+
return nil
29+
}
30+
```
31+
32+
When you add context to an error, ewrap automatically extracts and preserves important context values such as:
33+
34+
- Request IDs for tracing
35+
- User information for auditing
36+
- Operation metadata for monitoring
37+
- Timing information for performance tracking
38+
39+
## Advanced Context Usage
40+
41+
Let's look at more sophisticated ways to use context in error handling:
42+
43+
```go
44+
// Define context keys for common values
45+
type contextKey string
46+
47+
const (
48+
requestIDKey contextKey = "request_id"
49+
userIDKey contextKey = "user_id"
50+
traceIDKey contextKey = "trace_id"
51+
)
52+
53+
// RequestContext enriches context with standard fields
54+
func RequestContext(ctx context.Context, requestID, userID string) context.Context {
55+
ctx = context.WithValue(ctx, requestIDKey, requestID)
56+
ctx = context.WithValue(ctx, userIDKey, userID)
57+
ctx = context.WithValue(ctx, traceIDKey, generateTraceID())
58+
return ctx
59+
}
60+
61+
// ContextualOperation shows how to use context throughout an operation
62+
func ContextualOperation(ctx context.Context) error {
63+
// Extract context values
64+
requestID := ctx.Value(requestIDKey).(string)
65+
userID := ctx.Value(userIDKey).(string)
66+
67+
// Create an operation-specific context
68+
opCtx := ewrap.WithOperationContext(ctx, "user_update")
69+
70+
// Start a timed operation
71+
timer := time.Now()
72+
73+
// Perform the operation with timeout
74+
if err := performTimedOperation(opCtx); err != nil {
75+
return ewrap.Wrap(err, "operation failed",
76+
ewrap.WithContext(ctx, ErrorTypeInternal, SeverityError)).
77+
WithMetadata("request_id", requestID).
78+
WithMetadata("user_id", userID).
79+
WithMetadata("duration_ms", time.Since(timer).Milliseconds())
80+
}
81+
82+
return nil
83+
}
84+
```
85+
86+
## Context-Aware Error Groups
87+
88+
Error groups can be made context-aware to handle cancellation and timeouts:
89+
90+
```go
91+
// ContextualErrorGroup manages errors with context awareness
92+
type ContextualErrorGroup struct {
93+
*ewrap.ErrorGroup
94+
ctx context.Context
95+
}
96+
97+
// NewContextualErrorGroup creates a context-aware error group
98+
func NewContextualErrorGroup(ctx context.Context, pool *ewrap.ErrorGroupPool) *ContextualErrorGroup {
99+
return &ContextualErrorGroup{
100+
ErrorGroup: pool.Get(),
101+
ctx: ctx,
102+
}
103+
}
104+
105+
// ProcessWithContext demonstrates context-aware parallel processing
106+
func ProcessWithContext(ctx context.Context, items []Item) error {
107+
pool := ewrap.NewErrorGroupPool(len(items))
108+
group := NewContextualErrorGroup(ctx, pool)
109+
defer group.Release()
110+
111+
var wg sync.WaitGroup
112+
for _, item := range items {
113+
wg.Add(1)
114+
go func(item Item) {
115+
defer wg.Done()
116+
117+
// Check context cancellation
118+
select {
119+
case <-ctx.Done():
120+
group.Add(ewrap.New("operation cancelled",
121+
ewrap.WithContext(ctx, ErrorTypeInternal, SeverityWarning)))
122+
return
123+
default:
124+
if err := processItem(ctx, item); err != nil {
125+
group.Add(err)
126+
}
127+
}
128+
}(item)
129+
}
130+
131+
wg.Wait()
132+
return group.Error()
133+
}
134+
```
135+
136+
## Timeout and Cancellation Handling
137+
138+
Proper context integration includes handling timeouts and cancellation gracefully:
139+
140+
```go
141+
// TimeoutAwareOperation shows how to handle context timeouts
142+
func TimeoutAwareOperation(ctx context.Context) error {
143+
// Create a timeout context
144+
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
145+
defer cancel()
146+
147+
// Channel for operation result
148+
resultCh := make(chan error, 1)
149+
150+
// Start the operation
151+
go func() {
152+
resultCh <- performLongOperation(ctx)
153+
}()
154+
155+
// Wait for result or timeout
156+
select {
157+
case err := <-resultCh:
158+
if err != nil {
159+
return ewrap.Wrap(err, "operation failed",
160+
ewrap.WithContext(ctx, ErrorTypeInternal, SeverityError))
161+
}
162+
return nil
163+
case <-ctx.Done():
164+
return ewrap.New("operation timed out",
165+
ewrap.WithContext(ctx, ErrorTypeTimeout, SeverityCritical)).
166+
WithMetadata("timeout", 5*time.Second)
167+
}
168+
}
169+
```
170+
171+
## Context Propagation in Middleware
172+
173+
Context integration is particularly useful in middleware chains:
174+
175+
```go
176+
// ErrorHandlingMiddleware demonstrates context propagation
177+
func ErrorHandlingMiddleware(next http.Handler) http.Handler {
178+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
179+
// Create request context with tracing
180+
ctx := r.Context()
181+
requestID := generateRequestID()
182+
ctx = context.WithValue(ctx, requestIDKey, requestID)
183+
184+
// Create error group for request
185+
pool := ewrap.NewErrorGroupPool(4)
186+
eg := pool.Get()
187+
defer eg.Release()
188+
189+
// Wrap handler execution
190+
err := func() error {
191+
// Add request timing
192+
timer := time.Now()
193+
defer func() {
194+
if err := recover(); err != nil {
195+
eg.Add(ewrap.New("panic in handler",
196+
ewrap.WithContext(ctx, ErrorTypeInternal, SeverityCritical)).
197+
WithMetadata("panic_value", err).
198+
WithMetadata("stack", debug.Stack()))
199+
}
200+
}()
201+
202+
// Execute handler
203+
next.ServeHTTP(w, r.WithContext(ctx))
204+
205+
// Record timing
206+
duration := time.Since(timer)
207+
if duration > time.Second {
208+
eg.Add(ewrap.New("slow request",
209+
ewrap.WithContext(ctx, ErrorTypePerformance, SeverityWarning)).
210+
WithMetadata("duration_ms", duration.Milliseconds()))
211+
}
212+
213+
return nil
214+
}()
215+
216+
if err != nil {
217+
eg.Add(err)
218+
}
219+
220+
// Handle any collected errors
221+
if eg.HasErrors() {
222+
handleRequestErrors(w, eg.Error())
223+
}
224+
})
225+
}
226+
```
227+
228+
## Best Practices
229+
230+
### 1. Consistent Context Propagation
231+
232+
Maintain consistent context handling throughout your application:
233+
234+
```go
235+
// ContextualService demonstrates consistent context handling
236+
type ContextualService struct {
237+
db *Database
238+
log Logger
239+
}
240+
241+
func (s *ContextualService) ProcessRequest(ctx context.Context, req Request) error {
242+
// Enrich context with request information
243+
ctx = enrichContext(ctx, req)
244+
245+
// Use context in all operations
246+
if err := s.validateRequest(ctx, req); err != nil {
247+
return ewrap.Wrap(err, "validation failed",
248+
ewrap.WithContext(ctx, ErrorTypeValidation, SeverityError))
249+
}
250+
251+
if err := s.processData(ctx, req.Data); err != nil {
252+
return ewrap.Wrap(err, "processing failed",
253+
ewrap.WithContext(ctx, ErrorTypeInternal, SeverityError))
254+
}
255+
256+
return nil
257+
}
258+
```
259+
260+
### 2. Context Value Management
261+
262+
Be careful with context values and provide type-safe accessors:
263+
264+
```go
265+
// RequestInfo holds request-specific context values
266+
type RequestInfo struct {
267+
RequestID string
268+
UserID string
269+
TraceID string
270+
StartTime time.Time
271+
}
272+
273+
// GetRequestInfo safely extracts request information from context
274+
func GetRequestInfo(ctx context.Context) (RequestInfo, bool) {
275+
info, ok := ctx.Value(requestInfoKey).(RequestInfo)
276+
return info, ok
277+
}
278+
279+
// WithRequestInfo adds request information to context
280+
func WithRequestInfo(ctx context.Context, info RequestInfo) context.Context {
281+
return context.WithValue(ctx, requestInfoKey, info)
282+
}
283+
```
284+
285+
### 3. Error Context Enrichment
286+
287+
Systematically enrich errors with context information:
288+
289+
```go
290+
// EnrichError adds standard context information to errors
291+
func EnrichError(ctx context.Context, err error) error {
292+
if err == nil {
293+
return nil
294+
}
295+
296+
info, ok := GetRequestInfo(ctx)
297+
if !ok {
298+
return err
299+
}
300+
301+
return ewrap.Wrap(err, "operation failed",
302+
ewrap.WithContext(ctx, getErrorType(err), getSeverity(err))).
303+
WithMetadata("request_id", info.RequestID).
304+
WithMetadata("user_id", info.UserID).
305+
WithMetadata("trace_id", info.TraceID).
306+
WithMetadata("duration_ms", time.Since(info.StartTime).Milliseconds())
307+
}
308+
```
309+
310+
The context integration in ewrap provides a robust foundation for error tracking and debugging. By consistently using these features, you can build applications that are easier to monitor, debug, and maintain.

0 commit comments

Comments
 (0)