Skip to content

Commit 2861ae9

Browse files
committed
feat: add json support
1 parent e4a44ba commit 2861ae9

3 files changed

Lines changed: 121 additions & 9 deletions

File tree

runtime.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ import (
66
"sync"
77
)
88

9+
type Format string
10+
11+
const (
12+
Plain Format = "plain"
13+
Json Format = "json"
14+
)
15+
916
type config struct {
1017
namespace string
1118
timestamp *Timestamp
@@ -14,6 +21,8 @@ type config struct {
1421

1522
useColors bool
1623

24+
format Format
25+
1726
mutex *sync.RWMutex
1827
}
1928

@@ -25,13 +34,31 @@ var runtime = &config{
2534

2635
useColors: true,
2736

37+
format: Plain,
38+
2839
mutex: &sync.RWMutex{},
2940
}
3041

3142
type Timestamp struct {
3243
Format string
3344
}
3445

46+
// SetFormat sets the global output format for debugging.
47+
func SetFormat(format Format) {
48+
runtime.mutex.Lock()
49+
defer runtime.mutex.Unlock()
50+
51+
runtime.format = format
52+
}
53+
54+
// GetFormat retrieves the current global output format for debugging.
55+
func GetFormat() Format {
56+
runtime.mutex.RLock()
57+
defer runtime.mutex.RUnlock()
58+
59+
return runtime.format
60+
}
61+
3562
// SetUseColors sets the global color usage for debugging.
3663
func SetUseColors(use bool) {
3764
runtime.mutex.Lock()

write.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
package debugo
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"strings"
67
"time"
78
)
89

10+
type asJson struct {
11+
Timestamp string `json:"timestamp,omitempty"`
12+
Namespace string `json:"namespace,omitempty"`
13+
Fields map[string]any `json:"fields,omitempty"`
14+
Message string `json:"message,omitempty"`
15+
ElapsedMs int64 `json:"elapsed_ms,omitempty"`
16+
}
17+
918
func (d *Debugger) Debug(message ...any) *Debugger {
1019
d.write(message...)
1120
return d
@@ -29,6 +38,11 @@ func (d *Debugger) write(message ...any) {
2938
return
3039
}
3140

41+
if GetFormat() == Json {
42+
d.writeJSON(message...)
43+
return
44+
}
45+
3246
// Optional timestamp
3347
var timestamp string
3448
if t := GetTimestamp(); t != nil {
@@ -64,6 +78,30 @@ func (d *Debugger) write(message ...any) {
6478
}
6579
}
6680

81+
func (d *Debugger) writeJSON(message ...any) {
82+
entry := asJson{
83+
Namespace: d.namespace,
84+
Message: fmt.Sprint(message...),
85+
Fields: d.fields,
86+
ElapsedMs: d.elapsed().Milliseconds(),
87+
}
88+
89+
if t := GetTimestamp(); t != nil {
90+
entry.Timestamp = time.Now().Format(t.Format)
91+
}
92+
93+
data, err := json.Marshal(entry)
94+
if err != nil {
95+
return // fail silently
96+
}
97+
98+
if d.output != nil {
99+
_, _ = d.output.Write(append(data, '\n'))
100+
} else if o := GetOutput(); o != nil {
101+
_, _ = o.Write(append(data, '\n'))
102+
}
103+
}
104+
67105
func (d *Debugger) formatFields() string {
68106
if len(d.fields) == 0 {
69107
return ""

write_test.go

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,58 @@ func TestDebug(t *testing.T) {
4242

4343
d.Debug(testMessage)
4444

45-
assert.True(t, hasANSI(buf.String()), "Must have no colors")
45+
assert.True(t, hasANSI(buf.String()))
4646

4747
output := strings.TrimSpace(stripANSI(buf.String())) // Strip colors and trim whitespace
4848
expected := strings.TrimSpace(testMessageExpected)
49-
assert.Equal(t, expected, output, "Must have no colors")
49+
assert.Equal(t, expected, output)
50+
}
51+
52+
func TestDebugJSON(t *testing.T) {
53+
var buf bytes.Buffer
54+
d := getDebugger()
55+
d.SetOutput(&buf)
56+
SetFormat(Json)
57+
d.Debug(testMessage)
58+
SetFormat(Plain)
59+
60+
assert.False(t, hasANSI(buf.String()))
61+
output := strings.TrimSpace(stripANSI(buf.String())) // Strip colors and trim whitespace
62+
expected := strings.TrimSpace("{\"namespace\":\"" + namespace + "\",\"message\":\"" + testMessage + "\"}")
63+
assert.Equal(t, expected, output)
64+
}
65+
66+
func TestDebugJSONWithField(t *testing.T) {
67+
var buf bytes.Buffer
68+
d := getDebugger()
69+
d.SetOutput(&buf)
70+
d = d.With("json", true).With("number", 123)
71+
SetFormat(Json)
72+
d.Debug(testMessage)
73+
SetFormat(Plain)
74+
75+
assert.False(t, hasANSI(buf.String()))
76+
output := strings.TrimSpace(stripANSI(buf.String())) // Strip colors and trim whitespace
77+
expected := strings.TrimSpace("{\"namespace\":\"" + namespace + "\",\"fields\":{\"json\":true,\"number\":123},\"message\":\"" + testMessage + "\"}")
78+
assert.Equal(t, expected, output)
79+
}
80+
81+
func TestDebugJSONWithFieldTimestamp(t *testing.T) {
82+
var buf bytes.Buffer
83+
d := getDebugger()
84+
d.SetOutput(&buf)
85+
86+
d = d.With("json", true).With("number", 123)
87+
SetTimestamp(&Timestamp{Format: "2006"})
88+
SetFormat(Json)
89+
d.Debug(testMessage)
90+
SetFormat(Plain)
91+
SetTimestamp(nil)
92+
93+
assert.False(t, hasANSI(buf.String()))
94+
output := strings.TrimSpace(stripANSI(buf.String())) // Strip colors and trim whitespace
95+
expected := strings.TrimSpace("{\"timestamp\":\"" + fmt.Sprint(time.Now().Year()) + "\",\"namespace\":\"" + namespace + "\",\"fields\":{\"json\":true,\"number\":123},\"message\":\"" + testMessage + "\"}")
96+
assert.Equal(t, expected, output)
5097
}
5198

5299
func TestDebugWithFields(t *testing.T) {
@@ -58,7 +105,7 @@ func TestDebugWithFields(t *testing.T) {
58105

59106
x.Debug(testMessage)
60107

61-
assert.True(t, hasANSI(buf.String()), "Must have no colors")
108+
assert.True(t, hasANSI(buf.String()))
62109

63110
output := strings.TrimSpace(stripANSI(buf.String())) // Strip colors and trim whitespace
64111
expected := strings.TrimSpace(fmt.Sprintf("%s key1=value1 key2=42 %s +0ms\n", namespace, testMessage))
@@ -74,11 +121,11 @@ func TestDebugGlobalOutput(t *testing.T) {
74121

75122
d.Debug(testMessage)
76123

77-
assert.True(t, hasANSI(buf.String()), "Must have no colors")
124+
assert.True(t, hasANSI(buf.String()))
78125

79126
output := strings.TrimSpace(stripANSI(buf.String())) // Strip colors and trim whitespace
80127
expected := strings.TrimSpace(testMessageExpected)
81-
assert.Equal(t, expected, output, "Must have no colors")
128+
assert.Equal(t, expected, output)
82129
}
83130

84131
func TestDebugNoColors(t *testing.T) {
@@ -89,7 +136,7 @@ func TestDebugNoColors(t *testing.T) {
89136

90137
d.Debug(testMessage)
91138

92-
assert.False(t, hasANSI(buf.String()), "Must have no colors")
139+
assert.False(t, hasANSI(buf.String()))
93140
}
94141

95142
func TestDebugNonMatchingNamespace(t *testing.T) {
@@ -100,7 +147,7 @@ func TestDebugNonMatchingNamespace(t *testing.T) {
100147

101148
d.Debug("")
102149

103-
assert.Empty(t, buf.String(), "Must have no message")
150+
assert.Empty(t, buf.String())
104151
}
105152

106153
func TestDebugEmptyMessage(t *testing.T) {
@@ -112,7 +159,7 @@ func TestDebugEmptyMessage(t *testing.T) {
112159
SetNamespace("does:not:exist")
113160
d.Debug("test")
114161

115-
assert.Empty(t, buf.String(), "Must have no message")
162+
assert.Empty(t, buf.String())
116163
}
117164

118165
func TestDebugWithColors(t *testing.T) {
@@ -123,7 +170,7 @@ func TestDebugWithColors(t *testing.T) {
123170

124171
d.Debug(testMessage)
125172

126-
assert.True(t, hasANSI(buf.String()), "Must have colors")
173+
assert.True(t, hasANSI(buf.String()))
127174
}
128175

129176
func TestDebugf(t *testing.T) {

0 commit comments

Comments
 (0)