| ▲ | aleksi 2 days ago |
| Well, is fmt.Stringer supported? The result might surprise you: req := expvar.NewInt("requests")
req.Add(1)
attr := slog.Any("requests", req)
slog.New(slog.NewTextHandler(os.Stderr, nil)).Info("text", attr)
slog.New(slog.NewJSONHandler(os.Stderr, nil)).Info("json", attr)
This code produces time=2025-09-12T13:15:42.125+02:00 level=INFO msg=text requests=1
{"time":"2025-09-12T13:15:42.125555+02:00","level":"INFO","msg":"json","requests":{}}
So the code that uses slog but does not know what handler will be used can't rely on it lazily calling the `String() string` method: half of the standard handlers do that, half don't. |
|
| ▲ | 0x696C6961 2 days ago | parent | next [-] |
| If you need more control, you can create a wrapper type that implements `slog.LogValuer` type StringerValue struct {
fmt.Stringer
}
func (v StringerValue) LogValue() slog.Value {
return slog.StringValue(v.String())
}
Usage example: slog.Any("requests", StringerValue{req})
There might be a case for making the expvar types implement `slog.LogValuer` directly. |
| |
| ▲ | aleksi 2 days ago | parent [-] | | So clearly not all values are supported. And I know that I can create a wrapper for unsupported types. My problem is exactly that – I don't know what types are supported. Is error supported, for example? Should I create a wrapper for it? And, as a handler author, should I support it directly or not? | | |
| ▲ | 0x696C6961 2 days ago | parent [-] | | Not sure what your definition of "supported" is, but I'm afraid you're going to have to bite the bullet and ... gasp ... read the documentation https://pkg.go.dev/log/slog | | |
| ▲ | aleksi 2 days ago | parent [-] | | Not sure I understand your sarcasm. I read the documentation, source code, handler writing guide, and issues in the Go repository multiple times over two years, and I use slog extensively. Go is my primary language since r60. I think I know how to read Go docs. Now, please point me to the place in the documentation that says if I can or can't use a value implementing the error interface as an attribute value, and will the handler or something else would call the `Error() string` method. My definition of "supported" is simple – I could pass a supported value to the logger and get a reasonable representation from any handler. In my example, the JSON handler does not provide it for the fmt.Stringer. | | |
| ▲ | arccy 2 days ago | parent [-] | | https://pkg.go.dev/log/slog#JSONHandler.Handle > Values are formatted as with an encoding/json.Encoder with SetEscapeHTML(false), with two exceptions. > First, an Attr whose Value is of type error is formatted as a string, by calling its Error method. Only errors in Attrs receive this special treatment, not errors embedded in structs, slices, maps or other data structures that are processed by the encoding/json package. So the json handler more or less works as if you called json.Marshal, which sounds pretty reasonable. | | |
| ▲ | aleksi a day ago | parent [-] | | I think you missed the “any handler” part. Currently, the types that my library package could use depend on the handler used by the caller. This limits types to an unspecified subset, making things quite impractical. | | |
| ▲ | arccy 6 hours ago | parent [-] | | any handler is too broad, maybe my custom handler only logs strings and ignores ints. for a reasonable substitute subset, use the core language types, and implement LogValuer for anything complex. |
|
|
|
|
|
|
|
| ▲ | Philip-J-Fry 2 days ago | parent | prev [-] |
| That seems to work as expected? The output of data is handled by the handler. Such behaviour is clearly outlined in the documentation by the JSONHandler. I wouldn't expect a JSONHandler to use Stringer. I'd expect it to use the existing JSON interfaces, which it does. I'd expect the Text handler to use TextMarshaller. Which it does. Or Stringer, which it does implicitly via fmt.Sprintf. |
| |
| ▲ | aleksi 2 days ago | parent [-] | | My problem with that is that it makes it impossible to use slog logger safely without knowing what handler is being used. Which kind of defeats the purpose of defining the common structured logging interface. | | |
| ▲ | 2 days ago | parent | next [-] | | [deleted] | |
| ▲ | 9rx 2 days ago | parent | prev [-] | | > Which kind of defeats the purpose of defining the common structured logging interface. Does it, though? Why would the log producer care about how the log entires are formatted? Only the log consumer cares about that. | | |
| ▲ | aleksi a day ago | parent | next [-] | | As a producer of the response, if I didn't care about being understood, I would use a made-up language. As a consumer, you may care about understanding my response, but you cannot do anything about it. | | |
| ▲ | 9rx a day ago | parent [-] | | Hence the design. The producer coming up with a made up language that makes sense to the producer, but probably doesn't make sense to the consumer — especially when you have many different consumers with very different needs — is far more problematic than the producer providing an abstract representation and allowing the consumer to dig into the specific details it wants. As with everything in life, there are tradeoffs to that approach, of course, and it might be hard to grasp if you come from languages which different idioms that prioritize producer over consumer, but if you look closely everything about Go is designed to prioritize the needs of the consumer over the needs of the producer. That does seem to confuse a lot of people, interestingly. I expect because it isn't idiomatic to prioritize the consumer in a lot of other languages and people get caught up in trying to write code in those other languages using Go syntax instead of actually learning Go. |
| |
| ▲ | skeezyboy 2 days ago | parent | prev [-] | | [flagged] | | |
| ▲ | 9rx 2 days ago | parent [-] | | A good, if a bit strange, example. A CPI logger wouldn't need to log the same thing as an access logger, but the producer need not care about who is consuming the logs. Consumers might even want to see both and, given the design, can have both. | | |
| ▲ | skeezyboy 2 days ago | parent [-] | | [flagged] | | |
| ▲ | 9rx 2 days ago | parent [-] | | Certainly logs lose their value if they are wrong. And either approach is ripe for getting things wrong. But the idea is that the consumer is more in tune with getting what the consumer needs right. The producer's assumptions are most likely to be wrong, fundamentally, not having the full picture of what is needed. What is the counter suggestion? | | |
|
|
|
|
|
|