Skip to content

Commit 6aa9394

Browse files
authored
Supertaining (#8)
1 parent 9304f41 commit 6aa9394

17 files changed

+772
-193
lines changed

README.md

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import (
1919
var ErrUserNotFound errific.Err = "user not found"
2020

2121
func main() {
22+
// Configure pretty JSON output for readability
23+
errific.Configure(errific.OutputJSONPretty)
24+
2225
// Return an error with context
2326
err := GetUser("user-123")
2427
fmt.Println(err)
@@ -36,15 +39,23 @@ func GetUser(userID string) error {
3639
```
3740

3841
**Output:**
39-
```
40-
user not found [main.go:20.GetUser]
42+
```json
43+
{
44+
"error": "user not found",
45+
"code": "USER_404",
46+
"caller": "main.go:27.GetUser",
47+
"context": {
48+
"source": "database",
49+
"user_id": "user-123"
50+
}
51+
}
4152
```
4253

4354
The error includes:
4455
- ✅ Automatic caller information (`main.go:20.GetUser`)
45-
- ✅ Error code (`USER_404`)
46-
- ✅ Structured context (user_id, source)
47-
- ✅ JSON serializable for logging
56+
- ✅ Error code (`USER_404`) visible in output
57+
- ✅ Structured context (user_id, source) visible in output
58+
- ✅ JSON output by default for structured logging
4859

4960
## ✨ Features
5061

@@ -133,6 +144,78 @@ jsonBytes, _ := json.Marshal(err)
133144
log.Info(string(jsonBytes))
134145
```
135146

147+
### 🎨 Output Formats & Verbosity
148+
149+
Errific supports multiple output formats and verbosity levels. **By default, errors output as JSON with all metadata visible**.
150+
151+
```go
152+
// Default: JSON format with full verbosity (shows all metadata)
153+
errific.Configure() // or Configure(OutputJSON, VerbosityFull)
154+
155+
err := ErrUserNotFound.
156+
WithCode("USER_404").
157+
WithContext(errific.Context{"user_id": "user-123"})
158+
159+
fmt.Println(err)
160+
// Output: {"error":"user not found","code":"USER_404","caller":"main.go:20","context":{"user_id":"user-123"}}
161+
162+
// JSON Pretty format (indented JSON for docs/debugging)
163+
errific.Configure(OutputJSONPretty)
164+
fmt.Println(err)
165+
// Output:
166+
// {
167+
// "error": "user not found",
168+
// "code": "USER_404",
169+
// "caller": "main.go:20",
170+
// "context": {
171+
// "user_id": "user-123"
172+
// }
173+
// }
174+
175+
// Pretty format (multi-line, human-readable text)
176+
errific.Configure(OutputPretty)
177+
fmt.Println(err)
178+
// Output:
179+
// user not found [main.go:20.GetUser]
180+
// code: USER_404
181+
// context: map[user_id:user-123]
182+
183+
// Compact format (single-line key=value)
184+
errific.Configure(OutputCompact)
185+
fmt.Println(err)
186+
// Output: user not found [main.go:20] code=USER_404 user_id=user-123
187+
188+
// Minimal verbosity (only message + caller, useful for simple logging)
189+
errific.Configure(VerbosityMinimal)
190+
fmt.Println(err)
191+
// Output (JSON): {"error":"user not found","caller":"main.go:20"}
192+
193+
// Standard verbosity (message + caller + code + category + context)
194+
errific.Configure(VerbosityStandard)
195+
fmt.Println(err)
196+
// Output (JSON): {"error":"user not found","code":"USER_404","caller":"main.go:20","context":{"user_id":"user-123"}}
197+
198+
// Custom verbosity (show only specific fields)
199+
errific.Configure(VerbosityFull, HideContext, HideMCPData)
200+
fmt.Println(err)
201+
// Output (JSON): {"error":"user not found","code":"USER_404","caller":"main.go:20","http_status":404}
202+
```
203+
204+
**Available output formats:**
205+
- `OutputJSON` (default) - Compact JSON for structured logging
206+
- `OutputJSONPretty` - Indented JSON for documentation and debugging
207+
- `OutputPretty` - Multi-line, human-readable text
208+
- `OutputCompact` - Single-line key=value pairs
209+
210+
**Available verbosity levels:**
211+
- `VerbosityFull` (default) - Show all non-empty fields
212+
- `VerbosityStandard` - Show code, category, context
213+
- `VerbosityMinimal` - Show only message and caller
214+
- `VerbosityCustom` - Use with `Show*`/`Hide*` flags for granular control
215+
216+
**Granular field control:**
217+
`HideCode`, `HideCategory`, `HideContext`, `HideHTTPStatus`, `HideRetryMetadata`, `HideMCPData`, `HideTags`, `HideLabels`, `HideTimestamps`
218+
136219
### JSON Output
137220

138221
```json

conf.go

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ func Configure(opts ...Option) {
2121
c.withStack = false
2222
c.trimPrefixes = nil
2323
c.trimCWD = false
24+
c.outputFormat = OutputJSON
25+
c.verbosity = VerbosityFull
26+
27+
// Default field visibility (used when verbosity is VerbosityFull or VerbosityCustom)
28+
c.showCode = true
29+
c.showCategory = true
30+
c.showContext = true
31+
c.showHTTPStatus = true
32+
c.showRetryMetadata = true
33+
c.showMCPData = true
34+
c.showTags = true
35+
c.showLabels = true
36+
c.showTimestamps = true
2437

2538
for _, opt := range opts {
2639
switch o := opt.(type) {
@@ -38,6 +51,74 @@ func Configure(opts ...Option) {
3851

3952
case trimCWDOption:
4053
c.trimCWD = o
54+
55+
case outputFormatOption:
56+
c.outputFormat = o
57+
58+
case verbosityOption:
59+
c.verbosity = o
60+
// Set field visibility based on verbosity level
61+
switch o {
62+
case VerbosityMinimal:
63+
c.showCode = false
64+
c.showCategory = false
65+
c.showContext = false
66+
c.showHTTPStatus = false
67+
c.showRetryMetadata = false
68+
c.showMCPData = false
69+
c.showTags = false
70+
c.showLabels = false
71+
c.showTimestamps = false
72+
73+
case VerbosityStandard:
74+
c.showCode = true
75+
c.showCategory = true
76+
c.showContext = true
77+
c.showHTTPStatus = false
78+
c.showRetryMetadata = false
79+
c.showMCPData = false
80+
c.showTags = false
81+
c.showLabels = false
82+
c.showTimestamps = false
83+
84+
case VerbosityFull:
85+
c.showCode = true
86+
c.showCategory = true
87+
c.showContext = true
88+
c.showHTTPStatus = true
89+
c.showRetryMetadata = true
90+
c.showMCPData = true
91+
c.showTags = true
92+
c.showLabels = true
93+
c.showTimestamps = true
94+
}
95+
96+
case fieldVisibilityOption:
97+
// When using field visibility options, automatically switch to VerbosityCustom
98+
if c.verbosity != VerbosityCustom {
99+
c.verbosity = VerbosityCustom
100+
}
101+
// Apply the specific field visibility setting
102+
switch o.field {
103+
case "code":
104+
c.showCode = o.show
105+
case "category":
106+
c.showCategory = o.show
107+
case "context":
108+
c.showContext = o.show
109+
case "http_status":
110+
c.showHTTPStatus = o.show
111+
case "retry_metadata":
112+
c.showRetryMetadata = o.show
113+
case "mcp_data":
114+
c.showMCPData = o.show
115+
case "tags":
116+
c.showTags = o.show
117+
case "labels":
118+
c.showLabels = o.show
119+
case "timestamps":
120+
c.showTimestamps = o.show
121+
}
41122
}
42123
}
43124

@@ -70,6 +151,22 @@ var (
70151
// TrimCWD will trim the current working directory from filenames.
71152
// Default is false.
72153
trimCWD trimCWDOption
154+
// Output format: Pretty, JSON, or Compact.
155+
// Default is Pretty.
156+
outputFormat outputFormatOption
157+
// Verbosity controls which fields are shown in Error() output.
158+
// Default is VerbosityFull (show all non-empty fields).
159+
verbosity verbosityOption
160+
// Field visibility flags (used when verbosity is VerbosityCustom)
161+
showCode bool
162+
showCategory bool
163+
showContext bool
164+
showHTTPStatus bool
165+
showRetryMetadata bool
166+
showMCPData bool
167+
showTags bool
168+
showLabels bool
169+
showTimestamps bool
73170
}
74171
cMu sync.RWMutex
75172
)
@@ -139,6 +236,144 @@ type Option interface {
139236
ErrificOption()
140237
}
141238

239+
// outputFormatOption controls the format of error string output.
240+
type outputFormatOption int
241+
242+
func (outputFormatOption) ErrificOption() {}
243+
244+
const (
245+
// OutputPretty formats errors as human-readable multi-line text with all metadata.
246+
//
247+
// Example:
248+
// user not found [main.go:20.GetUser]
249+
// code: USER_404
250+
// context: {user_id: user-123, source: database}
251+
// http_status: 400
252+
OutputPretty outputFormatOption = iota
253+
254+
// OutputJSON formats errors as compact JSON.
255+
// This is the default.
256+
// Useful for structured logging and machine processing.
257+
//
258+
// Example:
259+
// {"error":"user not found","caller":"main.go:20","code":"USER_404",...}
260+
OutputJSON
261+
262+
// OutputJSONPretty formats errors as indented JSON.
263+
// Useful for documentation, debugging, and human-readable JSON output.
264+
//
265+
// Example:
266+
// {
267+
// "error": "user not found",
268+
// "code": "USER_404",
269+
// "caller": "main.go:20"
270+
// }
271+
OutputJSONPretty
272+
273+
// OutputCompact formats errors as single-line text with key=value pairs.
274+
// Useful for log aggregation systems.
275+
//
276+
// Example:
277+
// user not found [main.go:20] code=USER_404 user_id=user-123 http_status=400
278+
OutputCompact
279+
)
280+
281+
// verbosityOption controls which fields are included in Error() output.
282+
type verbosityOption int
283+
284+
func (verbosityOption) ErrificOption() {}
285+
286+
const (
287+
// VerbosityMinimal shows only the error message and caller.
288+
//
289+
// Example:
290+
// user not found [main.go:20.GetUser]
291+
VerbosityMinimal verbosityOption = iota
292+
293+
// VerbosityStandard shows message, caller, code, category, and context.
294+
// Good balance for most applications.
295+
//
296+
// Example:
297+
// user not found [main.go:20.GetUser]
298+
// code: USER_404
299+
// category: validation
300+
// context: {user_id: user-123}
301+
VerbosityStandard
302+
303+
// VerbosityFull shows all non-empty fields (default).
304+
// Recommended for debugging and development.
305+
//
306+
// Example:
307+
// user not found [main.go:20.GetUser]
308+
// code: USER_404
309+
// category: validation
310+
// context: {user_id: user-123, source: database}
311+
// http_status: 400
312+
// retryable: true
313+
// correlation_id: trace-123
314+
// help: Check if user exists
315+
VerbosityFull
316+
317+
// VerbosityCustom allows fine-grained control via individual field flags.
318+
// Use with Show* and Hide* options.
319+
VerbosityCustom
320+
)
321+
322+
// Field visibility options for VerbosityCustom.
323+
type fieldVisibilityOption struct {
324+
field string
325+
show bool
326+
}
327+
328+
func (fieldVisibilityOption) ErrificOption() {}
329+
330+
var (
331+
// ShowCode includes error code in output.
332+
ShowCode = fieldVisibilityOption{field: "code", show: true}
333+
// HideCode excludes error code from output.
334+
HideCode = fieldVisibilityOption{field: "code", show: false}
335+
336+
// ShowCategory includes error category in output.
337+
ShowCategory = fieldVisibilityOption{field: "category", show: true}
338+
// HideCategory excludes error category from output.
339+
HideCategory = fieldVisibilityOption{field: "category", show: false}
340+
341+
// ShowContext includes structured context in output.
342+
ShowContext = fieldVisibilityOption{field: "context", show: true}
343+
// HideContext excludes structured context from output.
344+
HideContext = fieldVisibilityOption{field: "context", show: false}
345+
346+
// ShowHTTPStatus includes HTTP status code in output.
347+
ShowHTTPStatus = fieldVisibilityOption{field: "http_status", show: true}
348+
// HideHTTPStatus excludes HTTP status code from output.
349+
HideHTTPStatus = fieldVisibilityOption{field: "http_status", show: false}
350+
351+
// ShowRetryMetadata includes retry information (retryable, retry_after, max_retries) in output.
352+
ShowRetryMetadata = fieldVisibilityOption{field: "retry_metadata", show: true}
353+
// HideRetryMetadata excludes retry information from output.
354+
HideRetryMetadata = fieldVisibilityOption{field: "retry_metadata", show: false}
355+
356+
// ShowMCPData includes MCP-related fields (correlation_id, help, suggestion, etc.) in output.
357+
ShowMCPData = fieldVisibilityOption{field: "mcp_data", show: true}
358+
// HideMCPData excludes MCP-related fields from output.
359+
HideMCPData = fieldVisibilityOption{field: "mcp_data", show: false}
360+
361+
// ShowTags includes semantic tags in output.
362+
ShowTags = fieldVisibilityOption{field: "tags", show: true}
363+
// HideTags excludes semantic tags from output.
364+
HideTags = fieldVisibilityOption{field: "tags", show: false}
365+
366+
// ShowLabels includes key-value labels in output.
367+
ShowLabels = fieldVisibilityOption{field: "labels", show: true}
368+
// HideLabels excludes key-value labels from output.
369+
HideLabels = fieldVisibilityOption{field: "labels", show: false}
370+
371+
// ShowTimestamps includes timestamp and duration in output.
372+
ShowTimestamps = fieldVisibilityOption{field: "timestamps", show: true}
373+
// HideTimestamps excludes timestamp and duration from output.
374+
HideTimestamps = fieldVisibilityOption{field: "timestamps", show: false}
375+
)
376+
142377
var root string
143378
var goroot string
144379

0 commit comments

Comments
 (0)