Earlier quoted context omitted.
> providing a core library way of wrapping with stacktraces would be a very useful next step What eventually became the standard library error wrapping proposal evolved from the work done on the Upspin project. It did include stacktraces, and believed like you that it would be useful to have them. But analysis of the data showed that nobody ever really used them in practice and, for that reason, was removed from the…
In the last few months I've realized what I desperately need: a way to wrap an error with a call stack at the point where it enters our code base. This would probably save me on average 20-30 minutes a week. I see this all the time: main.go:141 error: could not transmogrify the thing: a144cd21c48 And then I literally grep the code base to find the error message. That works ~50% of the time, but the other 50%, I see t…
Structured logging with slog
121–130 of 174 posts
Re: Structured logging with slog
#122Earlier quoted context omitted.
> providing a core library way of wrapping with stacktraces would be a very useful next step What eventually became the standard library error wrapping proposal evolved from the work done on the Upspin project. It did include stacktraces, and believed like you that it would be useful to have them. But analysis of the data showed that nobody ever really used them in practice and, for that reason, was removed from the…
In the last few months I've realized what I desperately need: a way to wrap an error with a call stack at the point where it enters our code base. This would probably save me on average 20-30 minutes a week. I see this all the time: main.go:141 error: could not transmogrify the thing: a144cd21c48 And then I literally grep the code base to find the error message. That works ~50% of the time, but the other 50%, I see t…
func bar() error {
err := baz.Transmogrify()
return fmt.Errorf("transmogrify: %w", err)
}
func foo() error {
err := bar()
return fmt.Errorf("bar: %w", err)
}
func main() {
err := foo()
fmt.Printf("foo: %v", err)
// foo: bar: transmogrify: not found
}
There's your callstack, without the cost of carrying around the actual callstack.Re: Structured logging with slog
#123With this another most requested feature is covered by Go. This leaves error handling, enum type which are often asked by users but are not actively being worked on for now.
The lack of Error Handling in Go is a feature, not a bug. See here: https://go.dev/doc/faq#exceptions . I think I'd be disappointed if Try/Catch ever made their way into the language.
Re: Structured logging with slog
#124Earlier quoted context omitted.
It sure has maps though... logrus famously uses `logrus.Fields{"key": "value"}`
And logrus is one of the slowest loggers by far, in part because of its heavy map usage.
Re: Structured logging with slog
#125Earlier quoted context omitted.
Passing in a map would require an extra allocation for the map memory for each log line. I think the performance would probably not be great?
it depends. I believe map literals are stack allocated if they aren't shared across goroutines or globals.
Re: Structured logging with slog
#126I must admit: I'm not a huge fan of structured logging, beyond simple use cases like tagging messages by the thread that produced them. If you want something machine-readable, use a dedicated metrics system, analytics database, or document store. If you want something human-readable, structured logging will only make things worse.
I feel what is missing here is message templates [1] - the logging API should permit the key-value pairs to be substituted into a template which results in a human-readable message, while preserving the KV data separately. Take a hash of the template and add it as a KV pair so that messages of the same type can be easily filtered. [1] https://messagetemplates.org
It’s really designed to be a minimum necessary package to allow interop (via handlers) and a baseline of standalone usability (via loggers). The stdlib only provides a text and a json handler, not even a no-op handler which I think is sorely neededor a multi handler which I think would make a lot of sense.
But nothing precludes you publishing a messagetemplates handler, or whatever else you may want.
Re: Structured logging with slog
#127It's nice to have this in the standard library, but it doesn't solve any existing pain points around structured log metadata and contexts. We use zap [0] and store a zap logger on the request context which allows different parts of the request pipeline to log with things like tenantId, traceId, and correlationId automatically appended. But getting a logger off the context is annoying, leads to inconsistent logging pr…
> As the call to LogAttrs shows, you can pass a context.Context to some log functions so a handler can extract context information like trace IDs.
I’m not a fan of slog’s syntax, but the convenience of having it in the stdlib trumps that, for me.
Re: Structured logging with slog
#128The new structured logging library is a great addition, its nice to have structured logging in the standard lib. It's easy to get started with log/slog and one of the built in handlers, but as soon as you want to change something the library design pushes you towards implementing an entire handler. For example, if I want the built in JSON format, but with a different formatting of the Time field, that's not easy to d…
Yes, it annoyed me to no end. But OTOH I think it may be wise of them to see what the ecosystem finds and provide more convenience later. After all, it's std we're talking about, and this takes time to get right.
I'm personally missing:
- A goddamn default no-op/nil logger, that can be declared before initialized in e.g. structs.
- Customization to TextHandler for changing format easily, and omit keys (AIUI ReplaceAttr cannot omit stuff like `time="..."` on each line), which is critical since CLI screen real estate is incredibly sparse.
- (Controversial) but I would like opinionated logger initialization guidance for package authors, so that you get consistency across the ecosystem. Doesn't have to be exactly one way, but say.. two ways? E.g. a package-global and a struct-initialized version? Right now, people are even confusingly wondering if they should accept slog.Handler or *slog.Logger.
Re: Structured logging with slog
#129Earlier quoted context omitted.
> Passing in an optional `map[string]string` or something would be better It would definitely not be better from the point of view of > We wanted slog to be fast.
A better interface/API is really what I meant. The performance characteristics are probably worth the tradeoff.
Most log metadata will be attached by libraries and middleware, so service/application devs won't even see most of it.
Re: Structured logging with slog
#130Earlier quoted context omitted.
Rust's slog ( https://docs.rs/slog/ ) does: use slog::info; ... info!("hello, world"; "user" => std::env::var("USER"));
It can do that because Rust’s macros can have their own mini language at the top level, and can transform that into whatever data structure they want under the cover. For better or for worse, Go doesn’t have that.
my %hash = (
Foo => “bar”,
Baz => “qux”,
);
the behaviour is no different than my %hash = (
Foo, “bar”,
Baz, “qux”,
);
it’s just that a literal plist in a hash context is interpreted as a hash.Hence “=>” being called “fat comma” in perl.
Which means you can writer
my %h = (a => b => c => d);
or my %h = (a, b => c, d);
they all mean the same thing.