-
Notifications
You must be signed in to change notification settings - Fork 328
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
Making logging to stdout optional #4
Comments
This is clearly a good idea. The
Using a simpler Hm. I at least want to sleep on the two approaches and/or hear thoughts from others. |
Being able to pull out the method name or destination could be useful. I think you would need the ctx to do that. It could just be ...
:) Or a Fprint style args after the ctx(?) |
@cep21, yes, providing the context seems important here. I don't think there's going to be a nice way to work in key-values for structured logging, though. How about |
That works. Wonder if it's worth sneaking the |
If we included the We could ask for something like
The default would be to I'm not sure what the advantage is with that though, to be honest. What would someone do with the The simpler |
To enable compos-ability with other 3rd party libraries, why not |
@dividinglimits Fair point, but these errors are not ones that the user controls - they aren't likely to have stack traces or causes like from github.com/pkg/errors. I don't think that's likely to come up, in practice. I suppose someone could implement their own HTTP stack and use it for Twirp, and then they might benefit from being able to unpack the error, though. But then, there doesn't seem to be any real downside to This has been a good discussion, so I'll leave it open for a few more days in case more people have good ideas. |
Maybe it's not a logger at all. If you reframe it to something that handles errors the API doesn't want to return or throw away, then you don't have to constrain yourself with worrying about a generic logging API.
The downside here is there's no context about where the error happened, which may motivate a third string parameter. Another advantage of reframing it, is you can think on and solve the logger problem later. |
In the spirt of @spenczar comment:
I think I am in favor of making this a logger and not an error receiver. However, I think structured logging is important, both for reporting and for expansibility. I like the
Adding
@spenczar what is your concern with the keyvals? What about a standard |
Oh, believe me, @huttotw, I think structured logging is important too. I'm just uncomfortable with If someone is not using structured logging - like, they are using Your point about a 'standard I feel that we've gotten to a good place here. I like Proposal 1: Named interface: We go with this basic structure: The generated structs will include a new method, func (s *haberdasherServer) SetErrorLogger(l ErrorLogger) {
s.errorLogger = l
}
func (c *haberdasherJSONClient) SetErrorLogger(l ErrorLogger) {
c.errorLogger = l
}
func (c *haberdasherProtobufClient) SetErrorLogger(l ErrorLogger) {
c.errorLogger = l
} And in the generated code, we'll include this interface definition: type ErrorLogger interface {
LogError(context.Context, error)
} If users want to configure the logger, they'll need to type-assert into being able to call the method, and then they can set it: c := haberdasher.NewHaberdasherProtobufClient("http://localhost:8080", http.DefaultClient)
if cc, ok := c.(interface {
SetErrorLogger(haberdasher.ErrorLogger)
}); ok {
cc.SetErrorLogger(logAdapter)
} Proposal 2: Anonymous interface In Proposal 1, because The simplest way around that is to drop the func (s *haberdasherServer) SetErrorLogger(l interface {
LogError(context.Context, error)
}) {
s.errorLog = l
}
func (c *haberdasherJSONClient) SetErrorLogger(l interface {
LogError(context.Context, error)
}) {
c.errorLog = l
}
func (c *haberdasherProtobufClient) SetErrorLogger(l interface {
LogError(context.Context, error)
}) {
c.errorLog = l
} Anonymous interfaces like this are horrible for readability. But this has a significant advantage - you could write a library like this, which would be the way users would set the logger: package twirplogrus
import "github.com/sirupsen/logrus"
type logSetter interface {
SetErrorLogger(l interface {
LogError(context.Context, error)
})
}
func SetTwirpLogger(twirpObj logSetter) {
twirpObj.SetErrorLogger(logrusTwirpAdapter{})
}
type logrusTwirpAdapter struct {}
func (a logrusTwirpAdapter) LogError(_ context.Context, err error) {
// Don't use this! Just an example:
logrus.Error(err.Error())
} That library would be impossible to write in Proposal 1. So, we're weighing library-ability versus readability of the generated code. I like Proposal 2 more. I think that the users of this functionality will be opinionated people who want to enforce a standardized logger across all projects they work on. They'll probably be more happy with making a library once than with going into all their projects and making sure they do the bizarre type assertion. I'm interested in feedback on these two proposals, and to any other ideas on how to implement Let's consider the discussion of the method signature closed. We got lots of good ideas, and I like |
Twirp has a mechanism for calling user code in response to particular events: The idea of For server errors not associated with a request, the generated code would call the function if set. If the field is not set, the generated code would log as it does today. Same with server errors encountered while writing a response. For the client, all errors would be reported synchronously. Currently the client will log any error encountered while calling This does mean the generated code would refer to a new field in the |
You can get the advantage of both named and anonymous interfaces with a type alias. |
@rhysh, your point about clients is really good: we can drop the I hadn't given hooks much thought because I thought we would need to support clients. I would actually prefer to see us do this through the existing Using the existing The downside is that we change the expand the meaning of the I actually prefer that new meaning. I think it's clearer - it separates the sending-of-the-response (which is, after all, This does mean that the It seems we can get away with this without any new APIs, which is just terrific. Good catch that the client error could be returned! |
I like this last approach:
Right now in some Twirp services I'm using, this rare exceptions are just being lost, because we track errors on What I don't like about the other approaches:
|
The generated twirp servers include
import log "log"
They're used in error cases like this:
It would be great if I could provide my own logger of some kind.
One proposed API would be to expose an optional function I could type assert for.
The client code could then call
If you're ambitious, you could have a specific twirp type logger for more strongly typed information
My implementation could then extract the method name from the ctx.
The text was updated successfully, but these errors were encountered: