89 lines
3.5 KiB
Markdown
89 lines
3.5 KiB
Markdown
gls
|
|
===
|
|
|
|
Goroutine local storage
|
|
|
|
### IMPORTANT NOTE ###
|
|
|
|
It is my duty to point you to https://blog.golang.org/context, which is how
|
|
Google solves all of the problems you'd perhaps consider using this package
|
|
for at scale.
|
|
|
|
One downside to Google's approach is that *all* of your functions must have
|
|
a new first argument, but after clearing that hurdle everything else is much
|
|
better.
|
|
|
|
If you aren't interested in this warning, read on.
|
|
|
|
### Huhwaht? Why? ###
|
|
|
|
Every so often, a thread shows up on the
|
|
[golang-nuts](https://groups.google.com/d/forum/golang-nuts) asking for some
|
|
form of goroutine-local-storage, or some kind of goroutine id, or some kind of
|
|
context. There are a few valid use cases for goroutine-local-storage, one of
|
|
the most prominent being log line context. One poster was interested in being
|
|
able to log an HTTP request context id in every log line in the same goroutine
|
|
as the incoming HTTP request, without having to change every library and
|
|
function call he was interested in logging.
|
|
|
|
This would be pretty useful. Provided that you could get some kind of
|
|
goroutine-local-storage, you could call
|
|
[log.SetOutput](http://golang.org/pkg/log/#SetOutput) with your own logging
|
|
writer that checks goroutine-local-storage for some context information and
|
|
adds that context to your log lines.
|
|
|
|
But alas, Andrew Gerrand's typically diplomatic answer to the question of
|
|
goroutine-local variables was:
|
|
|
|
> We wouldn't even be having this discussion if thread local storage wasn't
|
|
> useful. But every feature comes at a cost, and in my opinion the cost of
|
|
> threadlocals far outweighs their benefits. They're just not a good fit for
|
|
> Go.
|
|
|
|
So, yeah, that makes sense. That's a pretty good reason for why the language
|
|
won't support a specific and (relatively) unuseful feature that requires some
|
|
runtime changes, just for the sake of a little bit of log improvement.
|
|
|
|
But does Go require runtime changes?
|
|
|
|
### How it works ###
|
|
|
|
Go has pretty fantastic introspective and reflective features, but one thing Go
|
|
doesn't give you is any kind of access to the stack pointer, or frame pointer,
|
|
or goroutine id, or anything contextual about your current stack. It gives you
|
|
access to your list of callers, but only along with program counters, which are
|
|
fixed at compile time.
|
|
|
|
But it does give you the stack.
|
|
|
|
So, we define 16 special functions and embed base-16 tags into the stack using
|
|
the call order of those 16 functions. Then, we can read our tags back out of
|
|
the stack looking at the callers list.
|
|
|
|
We then use these tags as an index into a traditional map for implementing
|
|
this library.
|
|
|
|
### What are people saying? ###
|
|
|
|
"Wow, that's horrifying."
|
|
|
|
"This is the most terrible thing I have seen in a very long time."
|
|
|
|
"Where is it getting a context from? Is this serializing all the requests?
|
|
What the heck is the client being bound to? What are these tags? Why does he
|
|
need callers? Oh god no. No no no."
|
|
|
|
### Docs ###
|
|
|
|
Please see the docs at http://godoc.org/github.com/jtolds/gls
|
|
|
|
### Related ###
|
|
|
|
If you're okay relying on the string format of the current runtime stacktrace
|
|
including a unique goroutine id (not guaranteed by the spec or anything, but
|
|
very unlikely to change within a Go release), you might be able to squeeze
|
|
out a bit more performance by using this similar library, inspired by some
|
|
code Brad Fitzpatrick wrote for debugging his HTTP/2 library:
|
|
https://github.com/tylerb/gls (in contrast, jtolds/gls doesn't require
|
|
any knowledge of the string format of the runtime stacktrace, which
|
|
probably adds unnecessary overhead).
|