Skip to content

Commit 94138ce

Browse files
committed
fix: slog handler compatibility
1 parent 5a8d352 commit 94138ce

File tree

3 files changed

+101
-64
lines changed

3 files changed

+101
-64
lines changed

gcp_handler.go

Lines changed: 92 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,55 +27,59 @@ import (
2727

2828
// GoogleCloudSlogHandler wraps Google Cloud Logging's Logger for use with slog.
2929
type GoogleCloudSlogHandler struct {
30-
client *logging.Client
31-
logger *logging.Logger
32-
level slog.Level
30+
logger *logging.Logger
31+
client *logging.Client
32+
level slog.Leveler
33+
groupPrefix string
34+
attrs []slog.Attr
3335
}
3436

37+
var _ slog.Handler = &GoogleCloudSlogHandler{}
38+
3539
// NewGoogleCloudSlogHandler initializes a new GoogleCloudSlogHandler.
36-
func NewGoogleCloudSlogHandler(ctx context.Context, projectID, logName string, level slog.Level) (*GoogleCloudSlogHandler, error) {
40+
func NewGoogleCloudSlogHandler(ctx context.Context, projectID, logName string, level slog.Level) *GoogleCloudSlogHandler {
3741
client, err := logging.NewClient(ctx, projectID)
3842
if err != nil {
39-
return nil, fmt.Errorf("failed to create logging client: %w", err)
43+
return nil
4044
}
4145
return &GoogleCloudSlogHandler{
4246
client: client,
4347
logger: client.Logger(logName),
4448
level: level,
45-
}, nil
49+
}
50+
}
51+
52+
func (h *GoogleCloudSlogHandler) Enabled(ctx context.Context, level slog.Level) bool {
53+
minLevel := slog.LevelInfo
54+
if h.level != nil {
55+
minLevel = h.level.Level()
56+
}
57+
return level >= minLevel
4658
}
4759

4860
// Handle adapts slog.Record entries to Google Cloud Logging entries.
4961
func (h *GoogleCloudSlogHandler) Handle(ctx context.Context, r slog.Record) error {
50-
// Convert slog.Level to Google Cloud Logging severity
51-
var severity logging.Severity
52-
switch r.Level {
53-
case slog.LevelDebug:
54-
severity = logging.Debug
55-
case slog.LevelInfo:
56-
severity = logging.Info
57-
case slog.LevelWarn:
58-
severity = logging.Warning
59-
case slog.LevelError:
60-
severity = logging.Error
61-
default:
62-
severity = logging.Default
62+
// Check if the log level is enabled
63+
if !h.Enabled(ctx, r.Level) {
64+
return nil
6365
}
6466

65-
// Construct the payload with message and additional attributes
67+
// Convert slog.Level to Google Cloud Logging severity
68+
severity := h.mapSeverity(r.Level)
69+
70+
// Construct the payload with message, time, and additional attributes
6671
payload := map[string]interface{}{
6772
"message": r.Message,
68-
"level": r.Level.String(),
6973
"time": r.Time,
7074
}
7175

7276
// Add attributes from slog fields
7377
r.Attrs(func(a slog.Attr) bool {
74-
payload[a.Key] = a.Value
78+
payload[a.Key] = h.formatAttrValue(a.Value)
7579
return true
7680
})
7781

78-
// Send log entry to Google Cloud Logging
82+
// Send log entry to Google Cloud Logging and return any errors
7983
h.logger.Log(logging.Entry{
8084
Payload: payload,
8185
Severity: severity,
@@ -84,6 +88,71 @@ func (h *GoogleCloudSlogHandler) Handle(ctx context.Context, r slog.Record) erro
8488
return nil
8589
}
8690

91+
// mapSeverity converts slog.Level to Google Cloud Logging's Severity.
92+
func (h *GoogleCloudSlogHandler) mapSeverity(level slog.Level) logging.Severity {
93+
switch level {
94+
case slog.LevelDebug:
95+
return logging.Debug
96+
case slog.LevelInfo:
97+
return logging.Info
98+
case slog.LevelWarn:
99+
return logging.Warning
100+
case slog.LevelError:
101+
return logging.Error
102+
default:
103+
return logging.Default
104+
}
105+
}
106+
107+
// formatAttrValue formats attribute values for Google Cloud Logging.
108+
func (h *GoogleCloudSlogHandler) formatAttrValue(value interface{}) interface{} {
109+
switch v := value.(type) {
110+
case string, int, int64, float64, bool:
111+
return v
112+
case error:
113+
return v.Error()
114+
default:
115+
return fmt.Sprintf("%v", v) // Fallback for unsupported types
116+
}
117+
}
118+
119+
func (h *GoogleCloudSlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
120+
for i, attr := range attrs {
121+
attrs[i] = withGroupPrefix(h.groupPrefix, attr)
122+
}
123+
124+
return &GoogleCloudSlogHandler{
125+
logger: h.logger,
126+
level: h.level,
127+
groupPrefix: h.groupPrefix,
128+
attrs: append(h.attrs, attrs...),
129+
}
130+
}
131+
132+
func (h *GoogleCloudSlogHandler) WithGroup(name string) slog.Handler {
133+
if name == "" {
134+
return h
135+
}
136+
prefix := name + "."
137+
if h.groupPrefix != "" {
138+
prefix = h.groupPrefix + prefix
139+
}
140+
141+
return &GoogleCloudSlogHandler{
142+
logger: h.logger,
143+
level: h.level,
144+
attrs: h.attrs,
145+
groupPrefix: prefix,
146+
}
147+
}
148+
149+
func withGroupPrefix(groupPrefix string, attr slog.Attr) slog.Attr {
150+
if groupPrefix != "" {
151+
attr.Key = groupPrefix + attr.Key
152+
}
153+
return attr
154+
}
155+
87156
// Close closes the Google Cloud Logging client.
88157
func (h *GoogleCloudSlogHandler) Close() error {
89158
return h.client.Close()

gcp_handler_test.go

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"fmt"
66
"os"
77
"testing"
8-
"time"
98

109
"log/slog"
1110

@@ -26,47 +25,16 @@ func TestGoogleCloudSlogHandler(t *testing.T) {
2625

2726
// Initialize GoogleCloudSlogHandler
2827
logName := "test-log"
29-
handler, err := NewGoogleCloudSlogHandler(ctx, projectID, logName, slog.LevelInfo)
30-
if err != nil {
31-
t.Fatalf("failed to initialize GoogleCloudSlogHandler: %v", err)
32-
}
33-
defer func() {
34-
if err := handler.Close(); err != nil {
35-
t.Fatalf("failed to close handler: %v", err)
36-
}
37-
}()
38-
39-
// Define test cases
40-
testCases := []struct {
41-
level slog.Level
42-
message string
43-
attrs []slog.Attr
44-
}{
45-
{slog.LevelDebug, "Debug message", []slog.Attr{slog.String("key1", "value1")}},
46-
{slog.LevelInfo, "Info message", []slog.Attr{slog.String("key2", "value2")}},
47-
{slog.LevelWarn, "Warning message", []slog.Attr{slog.Int("key3", 123)}},
48-
{slog.LevelError, "Error message", []slog.Attr{slog.Float64("key4", 3.14)}},
49-
}
28+
handler := NewGoogleCloudSlogHandler(ctx, projectID, logName, slog.LevelInfo)
29+
defer handler.Close()
5030

51-
// Execute each test case
52-
for _, tc := range testCases {
53-
// Create a record for each test case
54-
record := slog.Record{
55-
Time: time.Now(),
56-
Level: tc.level,
57-
Message: tc.message,
58-
}
31+
// Set the handler for slog
32+
slog.SetDefault(slog.New(handler))
5933

60-
for _, attr := range tc.attrs {
61-
record.AddAttrs(attr)
62-
}
63-
64-
// Handle the record
65-
err := handler.Handle(ctx, record)
66-
if err != nil {
67-
t.Fatalf("unexpected error in Handle: %v", err)
68-
}
69-
}
34+
// Example log entries
35+
slog.Info("Starting application", "version", "1.0")
36+
slog.Warn("This is a warning message", "component", "main")
37+
slog.Error("An error occurred", "error", "sample error")
7038

7139
fmt.Printf("All logs written successfully to log: %s\n", logName)
7240
}

version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ package sloggcp
1818

1919
// Version is the current release version of GCP Cloud Logging Slog in use.
2020
func Version() string {
21-
return "0.0.1"
21+
return "0.0.2"
2222
}

0 commit comments

Comments
 (0)