--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
type Field struct {
key string
fieldType fieldType
ival int64
str string
obj interface{}
}
name old time/op new time/op delta
GokitLogfmt-4 3.35µs ± 1% 3.42µs ± 2% +2.11% (p=0.000 n=10+10)
Log15Logfmt-4 17.9µs ± 1% 17.7µs ± 1% -1.19% (p=0.000 n=9+9)
LogrusLogfmt-4 12.8µs ± 1% 13.0µs ± 1% +1.09% (p=0.000 n=10+9)
GokitJSON-4 10.0µs ± 0% 3.5µs ± 0% -64.80% (p=0.000 n=9+10)
Log15JSON-4 18.9µs ± 1% 18.8µs ± 0% -0.39% (p=0.027 n=10+9)
LogrusJSON-4 18.3µs ± 1% 18.4µs ± 0% ~ (p=0.074 n=9+10)
ZapJSON-4 1.57µs ± 1% 1.57µs ± 0% ~ (p=0.741 n=9+9)
name old alloc/op new alloc/op delta
GokitLogfmt-4 320B ± 0% 320B ± 0% ~ (all samples are equal)
Log15Logfmt-4 1.96kB ± 0% 1.96kB ± 0% ~ (all samples are equal)
LogrusLogfmt-4 1.62kB ± 0% 1.62kB ± 0% ~ (all samples are equal)
GokitJSON-4 1.12kB ± 0% 0.32kB ± 0% -71.43% (p=0.000 n=10+10)
Log15JSON-4 1.81kB ± 0% 1.81kB ± 0% ~ (all samples are equal)
LogrusJSON-4 2.30kB ± 0% 2.30kB ± 0% ~ (all samples are equal)
ZapJSON-4 192B ± 0% 192B ± 0% ~ (all samples are equal)
name old allocs/op new allocs/op delta
GokitLogfmt-4 11.0 ± 0% 11.0 ± 0% ~ (all samples are equal)
Log15Logfmt-4 45.0 ± 0% 45.0 ± 0% ~ (all samples are equal)
LogrusLogfmt-4 27.0 ± 0% 27.0 ± 0% ~ (all samples are equal)
GokitJSON-4 32.0 ± 0% 11.0 ± 0% -65.62% (p=0.000 n=10+10)
Log15JSON-4 40.0 ± 0% 40.0 ± 0% ~ (all samples are equal)
LogrusJSON-4 43.0 ± 0% 43.0 ± 0% ~ (all samples are equal)
ZapJSON-4 1.00 ± 0% 1.00 ± 0% ~ (all samples are equal)
For what it's worth, many developers at Uber agree with Peter on the ergonomics/verbosity of specifying types at call sites. A "sugared" logger is in the works (and already being used in our service framework) that provides variadic key/value pairs of eface: https://github.com/uber-go/zap/pull/185/files#diff-1b63c741609271aaf2cacbe869f304c8R68
>>> >>> -- >>> You received this message because you are subscribed to the Google Groups >>> "golang-dev" group. >>> To unsubscribe from this group and stop receiving emails from it, send an >>> email to golang-dev+unsubscribe@googlegroups.com. >>> For more options, visit https://groups.google.com/d/optout. > > -- > You received this message because you are subscribed to the Google Groups "golang-dev" group. > To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+unsubscribe@googlegroups.com. > For more options, visit https://groups.google.com/d/optout.
Rather than argue about whether libraries should log (it’s a losing proposition to argue this),
>>> >>> -- >>> You received this message because you are subscribed to the Google Groups >>> "golang-dev" group. >>> To unsubscribe from this group and stop receiving emails from it, send an >>> email to golang-dev+...@googlegroups.com. >>> For more options, visit https://groups.google.com/d/optout. > > -- > You received this message because you are subscribed to the Google Groups "golang-dev" group. > To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout.
var log MyFavoriteLogger
log.Error(“whoops!”)
log.Fatal(“aw, man :/”)
type Logger interface {
Info(...interface{}) // or whatever args
Error(...interface{})
Fatal(...interface{})
// … etc
}
type Logger interface{
Log(log.Level, ...interface{}) // or whatever as the args after level
Logf(log.Level, string, ...interface{}) // ? maybe?
// …. WithFields, etc whatever else
}
func (l MyLogger) Log(level log.Level, args ...interface{}) {
switch level {
case log.Warn:
l.Warn(args)
case log.Error:
l.Error(args)
case log.Panic:
l.Error(args) // maybe this library doesn’t believe in log.Panic, so map it to error
// etc ….
default:
l.Print(args) // or whatever
}
}
type Logger interface{
// ...
WithOut(io.Writer) Logger
// ...
}
type F func() func (l LoggerF) Info(f F) { if l.Enabled { f() } }
```
BenchmarkLog/LoggerI:_Disabled-4 2000000 569 ns/op 1008 B/op 9 allocs/op
BenchmarkLog/LoggerF:_Disabled-4 2000000000 0.00 ns/op 0 B/op 0 allocs/op
BenchmarkLog/LoggerI:_Enabled-4 2000000 752 ns/op 1008 B/op 9 allocs/op
BenchmarkLog/LoggerF:_Enabled-4 2000000 748 ns/op 1008 B/op 9 allocs/op
Hello,
By my informal survey, logging in Go applications follows three routes:
- Using some form of fmt.Printf
- Using functions from the log package
- Declaring a log variable at a package level
> [...]
I've tried to codify my solution to this problem with these recommendations.
- libraries should not log error messages, they should return error values
- if libraries need to log _informational_ messages, they should do so
using a value passed in during construction, usually stored in their
structure. I believe this is what Brian refers to when he says "A
common log interface", the type of the logger stored in your type and
availble to methods to use to log informational mesages.
type Log interface {
Print(v ...interface{})
}// common formatted string output
Print(fmt.Sprintf(“some custom print string: %s”, value))
// no format string, just values
Print(someText, struct1, struct2, err)
// Custom key value style structs that can be interrogated with the logger that satisfied the interface to create structured loggin
Print(keyValueStruct, keyValueStruct, keyValueStruct)
// log levels (with a clog style implementation)
Print(“info: starting up system”)
Print(fmt.Sprintf(“err: kaboom %s”, err))I couldn't agree more.
Brian Ketelsen made a Tweet recently
https://twitter.com/bketelsen/status/820768241849077760
which spawned an interesting proto-discussion about logging, tracing,
error reporting, and metrics, with Sameer, Rakyll, Dave, and other
titans of industry. We quickly outgrew the limits of Twitter, so I've
started this thread to continue the conversation.
Here are the questions as I see them:
1) What would a "standardized" logging interface look like?
2) How would that interface interact with related concerns e.g.
context, tracing, instrumentation?
--
> The benefit of a standard interface is that a program wide selection can be configured once like for example db/sql does.
I think when you read "interface" as in Application Programming Interface.
What I'm talking about is "interfaces" as in type logger interface.
No, that would be the worst; a silent global dependency which if missing from the final program means it magically stops working. Just search any of the usual go support forums to find people who use database/SQL and are bemused why it says that no driver is registered.
But that's the point. Pushing that concern into a package level variable creates a direct dependency on the logging implementation and we're back to exactly the situation that big go projects find themselves.
a. Projects have to manage multiple logging libraries
b. Projects that are limited to only use packages that favor their chosen logging library
c. Libraries that cannot use *any* logging libraries, including the standard library, because they don't want to get the decision wrong and cut themselves out of some or all of the marketplace.
But these would all go away with a db/sql solution.
It would require a common interface in the same fashion as db/sql and it would allow for the end user to easily select the implementation to use.
Missing the registration can happen of course but rarely more than once.
But that's done once in the main application and the choice has to be made at some point anyway.
I don't understand the tangent this conversation just took. Who mentioned testing?
>>>>>>>>>> >>>> an email to golang-dev+unsubscribe@googlegroups.com.
>>>>>>>>>> >>>> For more options, visit https://groups.google.com/d/optout.
>>>>>>>>>> >>>
>>>>>>>>>> >>> --
>>>>>>>>>> >>> You received this message because you are subscribed to the
>>>>>>>>>> >>> Google Groups
>>>>>>>>>> >>> "golang-dev" group.
>>>>>>>>>> >>> To unsubscribe from this group and stop receiving emails from
>>>>>>>>>> >>> it, send an
>>>>>>>>>> >>> email to golang-dev+unsubscribe@googlegroups.com.
>>>>>>>>>> >>> For more options, visit https://groups.google.com/d/optout.
>>>>>
>>>>> --
>>>>> You received this message because you are subscribed to the Google
>>>>> Groups "golang-dev" group.
>>>>> To unsubscribe from this group and stop receiving emails from it, send
>>>>> an email to golang-dev+unsubscribe@googlegroups.com.
>>>>> For more options, visit https://groups.google.com/d/optout.
>
> --
> You received this message because you are subscribed to the Google Groups
> "golang-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-dev+unsubscribe@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+unsubscribe@googlegroups.com.
We just have to disagree but if you can make end usage as easy then I am all for it.
Propagating loggers manually everywhere seems horrible but perhaps I am exaggerating the issue.
>>>>>>>>>> >>>>> [2] https://godoc.org/github.com/go-kit/kit/log#Context<br class="m_13609667854213
--
I think your proposal still requires a compile time dependency between the code logging and your logging implementation.
Can you show a sample package main that demonstrates how someone could use this proposal?
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+unsubscribe@googlegroups.com.
>>>> an email to golang-dev+unsubscribe@googlegroups.com.
>>>> an email to golang-dev+...@googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.
type Log interface {
Print(v ...interface{})
}type Logger interface { Log(keyvals ...interface{}) error }
--
I think we need to reduce the scope of and simplify the problem statement.
IMO there are two major problems which should be solved by this logger interface.
1. eliminate the need for multiple logging-backend dependencies compiled in an application binary -- we need backend-agnostic logging libraries.
2. leveling. We need the ability to pass a level to a backend, as different backends will handle these levels differently. Eg. Google Cloud logging supports filtering on the front-end based on the level passed when the entry was written.
I leave out structured logging, as the k/v pairs end up as part of the entry payload / text. The formatted string can be constructed however the logging library chooses.
Now, I think we should defer solving the second problem (leveling), as the first is more pressing and will require less work on the part of authors of existing 3rd party logging packages. Let’s start with something small, let that gain traction, then move on to harder problems. (leveling will require us to come up with a standard set of levels which logging packages must use, as well as a standard way to pass these levels to a backend).
The first problem can be solved simply, though it will require rewrite work for 3rd party logging package authors. Not a terribly complicated rewrite, though.
My example impl:
// package log/logger (TBD, but for example’s sake)
// standard logging interface
type Log interface {
Print(msg string) // note: concrete type
}
// GCP logger backend implementation (for example)
// package cloud.google.com/go/logging
type Logger struct {
// … conn, etc.
}
func (l *Logger) Print(msg string) { // std interface impl
l.Log(logging.Entry{Payload: msg}) // GCP-specific write, writes to buffer, periodically flushed
}
// package github.com/me/mylogger (my favorite logger, eg. logrus, log15)
type Logger struct {
l logger.Log // std interface above
}
// SetLog sets a backend implementation of logger.Log
func (l *Logger) SetLog(ll logger.Log /* std interface */) {
l.l = ll
}
// add all my favorite logging methods
func (l *Logger) Error(msg string) {
// note: for this idea to work, all 3rd party logging packages like this one will need
// to be reimplemented to write using the interface, not any concrete type
l.l.Print("ERROR: " + msg)
}
// ... Warn, Debug, WithFields, etc
// my program
func main() {
log := mylogger.New(gcplog.New())
log.Error("whoops!") // goes to GCP, using mylogger’s entry payload formatting
}
Currently logging in Go applications is handled in four ways
Using some form of fmt.Printf
Using functions from the log package
Declaring a log variable at a package level
Passing in a log object to a library (concrete or interface)
The first is out of scope for this proposal.
Despite the success of 12 factor, the second method, the log package in the standard library is considered inadequate for developer's needs. This view has been cemented by proposals to add so called leveled logging to the standard library package being declined such as https://github.com/golang/go/issues/2236.
This leaves a common pattern of declaring one or more private variables to hold a per package logger, a la
package foo
import “mylogger”
var log = mylogger.GetLogger(“github.com/project/foo”)
This pattern creates three issues.
There is a direct source level dependency between package foo package mylogger. A consumer of package foo is forced to take a dependency on mylogger.
This leads to projects composed of packages using multiple logging libraries, or fiefdoms of projects who can only consume packages that use the particular logging library.
--
One goal I have for any standard logging interface (and also interfaces for tracing, error reporting, and metrics) is that they are platform-agnostic. The particular use case I have in mind is enabling application developers and, especially, library developers to do logging (and tracing, error reporting, etc) without knowing or caring about the specific implementation that's linked into the program. From a business standpoint, I want to enable Go programs written to run on private machines or one Cloud platform to be easy to migrate to other platforms.
This has little to do with the design of the API, of course, but it's at least a concrete and testable goal for any solution.
Goals for the API might include:
- support for passing key-value pairs to the implementation
- support for passing Context to the implementation
- support for printf-style formatting
- support for low-allocation logging (like Uber zap)
Additional question: are logging and tracing essentially the same API? That is, is tracing essentially "logging to a request context"?
--
You received this message because you are subscribed to a topic in the Google Groups "golang-dev" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-dev/F3l9Iz1JX4g/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-dev+...@googlegroups.com.
Can you prioritize:
import (
"strings"
)
// LogFunc is a standard interface
type LogLevel int
func (this *LogLevel) WrapFunc(level int, fn LogFunc, prefix string) LogFunc {
prefix = strings.Replace(prefix, "%", "", -1)
return func(s string, as ...interface{}) {
if level <= int(*this) {
fn(prefix+s, as...)
}
}
}
func (this *LogLevel) SetLevel(n int) {
*this = (LogLevel)(n)
}
In addition please add a PrintStacktrace() for LogFunc:
func (this LogFunc) PrintStackTrace(n int) {this("%s", _stackTrace(n, 4))}
Example implementation:
github.com/noypi/logfn
Thanks,
s
In addition please add a PrintStacktrace() for LogFunc:
Example: https://github.com/noypi/logfn
<span style="color: #0
We (Ross Light, Herbie Ong and myself), responsible for this proposal for a standard logger interface, have decided to drop the proposal. The proposal aimed to reduce the number of Go libraries1 importing concrete loggers. The solution was to introduce a standard interface which library authors could log to instead, allowing the application author to plumb down their preferred concrete logger implementation.
We have not found enough Go libraries which import and use a concrete logger to justify continued work on this proposal.
Thank you to all of you who helped us create this proposal. We have learned much about community needs for logging, logger APIs and implementations during this process.
Should anyone have a different take on the need for a standard logging interface, we encourage you to please continue this discussion and submit your own proposals.
- Go on Cloud team at Google