You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

89 lines
3.5 KiB

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