-
Notifications
You must be signed in to change notification settings - Fork 241
Add slog logs integration #1033
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1033 +/- ##
==========================================
+ Coverage 86.27% 86.78% +0.50%
==========================================
Files 55 55
Lines 5670 5819 +149
==========================================
+ Hits 4892 5050 +158
+ Misses 637 627 -10
- Partials 141 142 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Adds support for capturing Sentry logs (in addition to events) by introducing a CaptureType
enum and a new log-based handler path.
- Introduce
CaptureType
enum (EventType
vs.LogType
) and makeNewSentryHandler
accept acontext.Context
- Implement
handleAsLog
inSentryHandler
to send structured logs viasentry-go/attribute
- Expand unit tests to cover both event- and log-based captures
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
File | Description |
---|---|
slog/sentryslog.go | Added CaptureType , context-aware handler, and handleAsLog |
slog/converter.go | New attrToSentryLog helper to map slog.Attr to Sentry attributes |
slog/sentryslog_test.go | Updated tests for new signature and added coverage for log capture |
log.go | Preserve existing sentry.origin attribute when logging |
Comments suppressed due to low confidence (1)
slog/sentryslog_test.go:203
newMockTransport
mutates the global Sentry hub viaCurrentHub()
, which can leak state across tests. Consider cloning the hub (e.g.sentry.CurrentHub().Clone()
) before binding your mock client.
func newMockTransport() (context.Context, *sentry.MockTransport) {
slog/sentryslog.go
Outdated
if o.LogLevel == nil { | ||
o.LogLevel = slog.LevelDebug |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on defaults, if a user upgrades, log messages will be sent. Should we handle the default level differently for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's still gated behind EnableLogs
right? I think thats fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeap, still gated behind it.
slog/converter.go
Outdated
func attrToSentryLog(group string, a slog.Attr) []attribute.Builder { | ||
switch a.Value.Kind() { | ||
case slog.KindAny: | ||
return []attribute.Builder{attribute.String(group+a.Key, fmt.Sprintf("%+v", a.Value.Any()))} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
l: Let's move group+a.Key
to the top key := group + a.Key
and pass in key
everywhere.
log.go
Outdated
@@ -144,7 +144,9 @@ func (l *sentryLogger) log(ctx context.Context, level LogLevel, severity int, me | |||
if sdkVersion := l.client.sdkVersion; sdkVersion != "" { | |||
attrs["sentry.sdk.version"] = Attribute{Value: sdkVersion, Type: "string"} | |||
} | |||
attrs["sentry.origin"] = Attribute{Value: "auto.logger.log", Type: "string"} | |||
if _, ok := attrs["sentry.origin"]; !ok { | |||
attrs["sentry.origin"] = Attribute{Value: "auto.logger.log", Type: "string"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
h: Origin should not be set by default, only for logs generated by logging integrations.
This is because explicit calls to the logging API are assumed to be manual
, so no applicable origin.
slog/sentryslog.go
Outdated
@@ -42,9 +43,17 @@ var ( | |||
) | |||
|
|||
type Option struct { | |||
// Level sets the minimum log level to capture and send to Sentry. | |||
// Logs at this level and above will be processed. The default level is debug. | |||
// Deprecated: Level is kept for backwards compatibility and defaults to EventLevel. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are going to deprecate, we need to provide the alternative (don't use it, use X instead, etc.).
It would also be nice to throw deprecations in https://github.com/getsentry/sentry-go/blob/master/MIGRATION.md so that they can be easily searched for and linked in the changelog, but I'll leave the exact process up to you.
slog/sentryslog.go
Outdated
if o.LogLevel == nil { | ||
o.LogLevel = slog.LevelDebug |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's still gated behind EnableLogs
right? I think thats fine.
slog/sentryslog.go
Outdated
|
||
if h.eventHandler.Enabled(ctx, record.Level) { | ||
if handleErr := h.eventHandler.Handle(ctx, record); handleErr != nil { | ||
err = handleErr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m: shouldn't we return the first error that we get? Otherwise I'd prefer if we construct an error that is the combo of of both if we hit both eventHandler
and logHandler
errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to handle both events and logs so can't return on first error. But it would be probably nicer to wrap them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, let's wrap them.
@@ -82,24 +99,80 @@ func (o Option) NewSentryHandler() slog.Handler { | |||
o.AttrFromContext = []func(ctx context.Context) []slog.Attr{} | |||
} | |||
|
|||
return &SentryHandler{ | |||
logger := sentry.NewLogger(ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this will lead to stale contexts.
Should we just create a new logger everytime on Handle
? Overhead might be more, so perhaps we can just set new contexts if it changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are already using the ctx
passed on sentryLogger.log to update the hub if possible, so we don't need to create a new logger.
slog/converter.go
Outdated
case slog.KindLogValuer: | ||
return []attribute.Builder{attribute.String(group+a.Key, a.Value.LogValuer().LogValue().String())} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This risks infinite recursion right? I wonder if we need to just special case this somehow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was the comment meant for slog.KindGroup
? Valuer should be fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes sorry - slog.KindGroup
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably can, but not while using WithGroup
. You can possibly create :
attr1 := slog.Attr{
Key: "group1",
Value: slog.GroupValue(),
}
attr2 := slog.Attr{
Key: "group2",
Value: slog.GroupValue(attr1),
}
and then create another group reference to the second one attr1.Value = slog.GroupValue(attr2)
. Should we guard against that? Seems like a pretty wacky case to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delved a bit deeper into it, slog.Attr and slog.Groups are values, so we can't really have a cycle. If a user manually creates a slog.Attr
with its' Value
being a pointer to a GroupValue it should be handled by slog.KindAny
not slog.KindGroup
.
slog/sentryslog.go
Outdated
// LogLevel sets the minimum log level to capture and send to Sentry as a Log entry. | ||
// Logs at this level and above will be processed as log entries. | ||
// Defaults to slog.LevelDebug. | ||
LogLevel slog.Leveler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know I said previously that EnableLogs
gating this is fine, but I tried running this on a local go app and now I'm now debating if there should be a way to turn this off.
I can see users wanting to disable EventLevel
completely and only rely on LogLevel
for example.
Maybe we expose separate boolean options? What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't know if i like adding also boolean options.
It might be nicer to specify an array of log levels so the user directly specifies all the levels that wants to be captured. This also provides more fine grained control, since maybe you don't want to catch Error
level for logs and want only events.
But then I don't know how we can handle and compare custom slog levels.
EDIT: we actually don't support them with the current implementation, so let me change that as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the array suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One note on the README, otherwise we are good
slog/README.MD
Outdated
|
||
- `LogLevel`: Minimum level to send logs to Sentry. Defaults to `slog.LevelDebug`. | ||
- `LogLevel`: Slice of specific levels to send `Log` entries to Sentry. Defaults to `[]slog.Level{slog.LevelError, LevelFatal}`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this right? From below I can see o.LogLevel = []slog.Level{slog.LevelDebug, slog.LevelInfo, slog.LevelWarn, slog.LevelError, LevelFatal}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that was missed. Nice catch Abhi!
Description
Closes #1027.
Changing the current
sentryslog
implementation to add support for creating sentry Logs, using different option Levels:Somes notes:
context.Context
while creating theNewSentryHandler
, but that would be a breaking change.