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
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
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
### 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).