Skip to content
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

Add zapgrpc package #402

Merged
merged 8 commits into from
Apr 14, 2017
Merged

Add zapgrpc package #402

merged 8 commits into from
Apr 14, 2017

Conversation

bufdev
Copy link
Contributor

@bufdev bufdev commented Apr 6, 2017

I'm not sure if we want this, so feel free to close this otherwise, but I was adding it in yarpc/yarpc-go#863 and thought it might make more sense here.

https://github.com/grpc/grpc-go has a global internal logger it uses, and https://github.com/grpc/grpc-go/tree/master/grpclog defines what logger is used. If a logger is not set, the default golang logger is used, which could mean lost logs for applications that use zap and grpc. This package defines a simple wrapper for a *zap.Logger that is compatible with grpclog.Logger. To use it, one would do:

grpclog.SetLogger(zapgrpc.NewLogger(zap.L())

@bufdev bufdev requested a review from akshayjshah April 6, 2017 12:06
@akshayjshah
Copy link
Contributor

Generally love this idea. I'm out for a week and won't be able to look until I'm back; try pinging @prashantv or we can pick this up on Wednesday.

In the meantime, no harm in integrating it into YARPC as an internal package.

@bufdev
Copy link
Contributor Author

bufdev commented Apr 6, 2017

I updated this to have a WithDebug function, and to use a struct instead of an interface. @prashantv if you want to review that's great, but no rush on this.

@akshayjshah akshayjshah reopened this Apr 12, 2017
Copy link
Contributor

@akshayjshah akshayjshah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the idea of bundling this package with zap, since it doesn't introduce any new dependencies.

Needs tests, but the general approach lgtm.

fatalfFunc func(*zap.SugaredLogger, string, ...interface{})
printFunc func(*zap.SugaredLogger, ...interface{})
printfFunc func(*zap.SugaredLogger, string, ...interface{})
}
Copy link
Contributor

@akshayjshah akshayjshah Apr 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the benefit of keeping these four function pointers instead of

type Logger struct {
  sugar *zap.SugaredLogger
  development bool
}

and checking development in Print and friends? I don't think that the performance impact of a single boolean check will even be measurable relative to the cost of string formatting.

This also lets us get rid of the the loggerOptions type in favor of type Option func(*Logger) (or even type Option interface { apply(*Logger) }).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Talked a little offline, I still prefer this, but I can change to something else

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could also do debug bool for now, if that's better for you - it accomplishes the same thing. My biggest issue would be doing WithDevelopment - I want this to be more specific.

Copy link
Contributor

@akshayjshah akshayjshah Apr 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind explaining what's attractive about this approach? I'm not necessarily opposed, I'm just not seeing the upside.

There's no reason I can see that changing the internal structure of Logger requires any changes to the public API - WithDevelopment can still do exactly what it does now.

Copy link
Contributor Author

@bufdev bufdev Apr 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe there's a misunderstanding - note this is controlling what happens when you call a grpclog function such as grpclog.Printf, not controlling the actual *zap.Logger given to zapgrpc.SetLogger, so for example:

grpclog.Print("hello") // if WithDebug was not set, this will call zap.Info
grpclog.Print("hello") // if WithDebug was set, this will call zap.Debug

By setting WithDebug, if the level of the *zap.Logger is not debug, this will suppress grpc logging, which might be what someone wants.

Doing this by setting printFunc, printfFunc versus having a debug bool to me is cleaner, and the difference is minor, something around a few lines. I think the function style is nicer, and also note how it lets me test grpclog.Fatal* functions easily - without being able to also set fatalFunc, fatalfFunc, I wouldn't be really able to test grpclog.Fatal* functions because the program would exit. Without adding any special boolean to the actual implementation, I am able to add a withWarn LoggerOption in the tests that sets the fatal functions to redirect to the warn level, in the same style as the WithDebug option. This alone might be a good tie breaker.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough.

import "go.uber.org/zap"

// LoggerOption is an option for a new Logger.
type LoggerOption func(*loggerOptions)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment on Logger - if possible, it'd be nice to avoid having exported types reference unexported types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to take a *Logger instead

@bufdev
Copy link
Contributor Author

bufdev commented Apr 13, 2017

I've added tests that give 100% coverage on zapgrpc

@akshayjshah akshayjshah merged commit 4a7873d into master Apr 14, 2017
@akshayjshah akshayjshah deleted the zapgrpc branch April 14, 2017 05:00
@flisky
Copy link
Contributor

flisky commented Apr 17, 2017

Sorry for a late chiming in, but Iet me introduce another way.

grpclog.Logger interface says:
Logger mimics golang's standard Logger as an interface.

So you can do this -

stdLogger := zap.NewStdLog(logger)
grpclog.SetLogger(stdLogger)

@akshayjshah
Copy link
Contributor

@flisky you can definitely do that, but it sacrifices quite a bit of performance in some common situations. Since NewStdLog needs to return a *log.Logger, we have to implement io.Writer - and Write is only called after all string formatting is done. When info-level logging is disabled, this incurs a bunch of string-formatting expense only to drop the final output.

By wrapping the SugaredLogger and directly implementing the gRPC interface, we can avoid all that overhead.

@flisky
Copy link
Contributor

flisky commented Apr 18, 2017

Thank you for your elaboration, and I see the point.

I'm just not sure whether it should be maintained under zap.

  • The grpclog is overhauling. How would we keep sync with it?
  • How about other projects if we maintain zapgrpc? Are we encourage the community to contribute more bindings into zap?

Just my 2 cents.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants