-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
WIP: Sentry logger #184
WIP: Sentry logger #184
Conversation
f3df89f
to
f27a212
Compare
} | ||
|
||
// Default to live sentry packet capturer | ||
if l.Capturer == nil { |
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.
How about a Client
option instead of the dsn string?
- then you wouldn't need an
error
return here, since it's the caller's responsibility to create and pass a raven client - other capturer forms could be handled by other options (e.g. a
BatchedClient
option)
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 it
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.
There is one small problem with this @jcorbin : it means that people using this logger would have to import raven-go as well as zap/sentry (which also imports raven-go) into their projects. If the two api versions don't match then there be trouble.
I wonder if this is a safer approach, even though it requires some more error validation.
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 not possible for a single Go binary to have two versions of the same package running around - this isn't node! :)
Agreed with @jcorbin - we should take a Client
here instead.
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.
@akshayjshah I'm not for a moment suggesting that there will be two versions of a binary running around, I was simply talking about glide and dependency management.
This change would mean that people using zap/sentry
would also have to import raven-go
directly in order to use the sentry logger. That means they could lock in the version of raven-go in their glide.yaml, which then can lead to trouble. We run into this situation pretty frequently with uberfx, since we're acting as a middle man for a lot of other libraries.
If both of you don't think that it is going to be a usability issue, I have no problem with making the change otherwise.
} | ||
|
||
// WithCapturer allows to specify which packet capturer should be used | ||
func WithCapturer(c Capturer) func(l *Logger) { |
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 With
prefix isn't a pattern that the core zap options follow, maybe we should drop it here too?
} | ||
} | ||
|
||
// WithMinLevel provides a slice of levels for which sentry packaet should be sent |
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 not actually a slice anymore, artifact of prior form?
Ideally, the sentry logger should use the new unified level options introduced by #180; however the sentry logger here doesn't currently take any of the core zap options for its embedded Meta
.
|
||
// WithTraceCfg allows to change the number of skipped frames, number of context lines and | ||
// list of go prefixes that are considered "in-app", i.e. "github.com/uber-go/zap" | ||
func WithTraceCfg(skip, context int, prefixes []string) func(l *Logger) { |
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.
These could (probably should) just be three separate options.
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 started that way, then consolidated :(
|
||
// Panic sends Sentry information provided minimum threshold is met | ||
func (l *Logger) Panic(msg string, fields ...zap.Field) { | ||
l.log(zap.PanicLevel, msg, fields) |
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.
should panec()
after log
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.
have never heard of panEc
;)
will make the change
|
||
// Fatal sends Sentry information provided minimum threshold is met | ||
func (l *Logger) Fatal(msg string, fields ...zap.Field) { | ||
l.log(zap.FatalLevel, msg, fields) |
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.
Should os.Exit(1)
after log.
@akshayjshah maybe we should move the _exit
symbol into a sub-package like github.com/uber-go/zap/internal
so that it can be shared by other zap-included concrete implementations for testing? (as bonus, this would be one less reason why tests couldn't be in their *_test
package form)
|
||
// Notify sentry if the log level meets minimum threshold | ||
func (l *Logger) log(lvl zap.Level, msg string, fields []zap.Field) { | ||
if lvl >= l.minLevel { |
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.
Should be using Meta.Enabled
, if we could do away with the minLevel
field here and just use Meta
's leveling support
365c306
to
f27a212
Compare
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.
Left some comments throughout; the most important are about the way we're handling the extra
fields, which looks incorrect.
Please also add some benchmarks here; I suspect that using a zwrap.KeyValueMap
and copying repeatedly will be very, very slow.
// | ||
// Allows for a variety of implementations of how to send Sentry packets. | ||
// For more performance sensisitve systems, it might make sense to batch | ||
// rather than opening op a connection on each send. |
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.
Spelling: "sensitive" and "up". There are a number of other typos in the comments here - can you run a spell checker?
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 I don't know what's vscode's problem. I've fixed it once before to run spell check in comments, seems like I need to look into it again.
// THE SOFTWARE. | ||
|
||
// Package sentry provides a logger which will automatically send events to sentry | ||
// over a certain set threshold |
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.
Please make doc comments full sentences, with periods or other punctuation at the ends of sentences.
raven "github.com/getsentry/raven-go" | ||
"github.com/pkg/errors" | ||
"github.com/uber-go/zap" | ||
"github.com/uber-go/zap/zwrap" |
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.
Let's also follow our in-house style guide and group imports. The zap imports should be in a group above the third-party imports.
zap.InfoLevel: raven.INFO, | ||
zap.WarnLevel: raven.WARNING, | ||
zap.ErrorLevel: raven.ERROR, | ||
zap.PanicLevel: raven.ERROR, |
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 raven.FATAL
more appropriate here? I'd definitely want to distinguish panic-level logs from garden-variety errors.
Let's also include zap.DebugLevel
, just for completeness.
var ( | ||
_platform = "go" | ||
_traceContextLines = 3 | ||
_traceSkipFrames = 3 |
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.
Can these three be constants instead?
|
||
// Notify sentry if the log level meets minimum threshold. | ||
func (l *Logger) log(lvl zap.Level, msg string, fields []zap.Field) { | ||
if lvl >= l.minLevel { |
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.
Prefer to structure your code to minimize indentation (similar to https://github.com/golang/go/wiki/CodeReviewComments#indent-error-flow) - in this case, return early if the level doesn't meet the min, which allows the meat of the method to be de-dented.
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.
Good tip, thank you.
extra := l.extra | ||
for _, f := range fields { | ||
f.AddTo(extra) | ||
} |
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.
Again, this is mutating the receiver, which isn't what we want.
} | ||
} | ||
|
||
l.Capture(packet) |
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.
Fine for now. You may want to talk to the upstream raven-go
maintainers to see if they're willing to expose a more performant API. Perhaps CaptureBytes([]byte)
, so we can use zap's JSONEncoder and incrementally serialize this payload, passing the JSON-encoded bytes here? If that doesn't work, I can't imagine it's that difficult to fire off HTTP requests to the Sentry API without using raven-go.
/cc @sectioneight
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 is definitely on the list.
There are a bunch of PRs/issues into raven-go along the same lines: not very flexible external API. We plan to help with the situation if we can.
Even though, I still think using their lower-level API to capture a packet is better than doing it by hand ourselves, but could be convinced either way.
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.
Let's see what the benchmarks look like.
|
||
type memCapturer struct { | ||
packets []*raven.Packet | ||
} |
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 anyone going to use the memCapturer
outside the tests in this package? If not, let's un-export all these types and remove the WithCapturer
option - your tests can just mutate logger.Capturer
.
) | ||
|
||
// Logger automatically sends logs above a certain threshold to Sentry. | ||
type Logger struct { |
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.
Do we need to export this type, or can New
just return a zap.Logger
? If possible, I'd prefer that - you can cast back to a *logger
in your tests.
a88148a
to
2eab299
Compare
Accessing that map with a debug level should rightly so panic, as that's not a supported use case.
Cover Panic functions on the logger
Hey there, |
This Sentry Logger can be used in combination with the forthcoming TeeLogger to easily report errors, or other types of logs as well, to Sentry.
It is written in a separate package
zap/sentry
, so the users of zap do not inherit a transitive dependence, but I think that quite a few if the current users might be interested in this Sentry integration.It allows for somewhat flexible setup about how and when to send sentry packets through
Capturer
interface. Provided with it are two examples: memory one used for testing and a non-blocking one (without checking the outcome of the packet send). If speed is no main concern, anotherBlockingCapturer
could be added in the future that will wait on the outcome of the packet send.TODO: Benchmark?