tech: adds/removes in vendor folder according to dep 0.4.0.
This commit is contained in:
-89
@@ -1,89 +0,0 @@
|
||||
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).
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
package gls_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/jtolds/gls"
|
||||
)
|
||||
|
||||
func TestContexts(t *testing.T) {
|
||||
mgr1 := gls.NewContextManager()
|
||||
mgr2 := gls.NewContextManager()
|
||||
|
||||
CheckVal := func(mgr *gls.ContextManager, key, exp_val string) {
|
||||
val, ok := mgr.GetValue(key)
|
||||
if len(exp_val) == 0 {
|
||||
if ok {
|
||||
t.Fatalf("expected no value for key %s, got %s", key, val)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("expected value %s for key %s, got no value",
|
||||
exp_val, key)
|
||||
}
|
||||
if exp_val != val {
|
||||
t.Fatalf("expected value %s for key %s, got %s", exp_val, key,
|
||||
val)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Check := func(exp_m1v1, exp_m1v2, exp_m2v1, exp_m2v2 string) {
|
||||
CheckVal(mgr1, "key1", exp_m1v1)
|
||||
CheckVal(mgr1, "key2", exp_m1v2)
|
||||
CheckVal(mgr2, "key1", exp_m2v1)
|
||||
CheckVal(mgr2, "key2", exp_m2v2)
|
||||
}
|
||||
|
||||
Check("", "", "", "")
|
||||
mgr2.SetValues(gls.Values{"key1": "val1c"}, func() {
|
||||
Check("", "", "val1c", "")
|
||||
mgr1.SetValues(gls.Values{"key1": "val1a"}, func() {
|
||||
Check("val1a", "", "val1c", "")
|
||||
mgr1.SetValues(gls.Values{"key2": "val1b"}, func() {
|
||||
Check("val1a", "val1b", "val1c", "")
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
Check("", "", "", "")
|
||||
}()
|
||||
gls.Go(func() {
|
||||
defer wg.Done()
|
||||
Check("val1a", "val1b", "val1c", "")
|
||||
})
|
||||
wg.Wait()
|
||||
Check("val1a", "val1b", "val1c", "")
|
||||
})
|
||||
Check("val1a", "", "val1c", "")
|
||||
})
|
||||
Check("", "", "val1c", "")
|
||||
})
|
||||
Check("", "", "", "")
|
||||
}
|
||||
|
||||
func ExampleContextManager_SetValues() {
|
||||
var (
|
||||
mgr = gls.NewContextManager()
|
||||
request_id_key = gls.GenSym()
|
||||
)
|
||||
|
||||
MyLog := func() {
|
||||
if request_id, ok := mgr.GetValue(request_id_key); ok {
|
||||
fmt.Println("My request id is:", request_id)
|
||||
} else {
|
||||
fmt.Println("No request id found")
|
||||
}
|
||||
}
|
||||
|
||||
mgr.SetValues(gls.Values{request_id_key: "12345"}, func() {
|
||||
MyLog()
|
||||
})
|
||||
MyLog()
|
||||
|
||||
// Output: My request id is: 12345
|
||||
// No request id found
|
||||
}
|
||||
|
||||
func ExampleGo() {
|
||||
var (
|
||||
mgr = gls.NewContextManager()
|
||||
request_id_key = gls.GenSym()
|
||||
)
|
||||
|
||||
MyLog := func() {
|
||||
if request_id, ok := mgr.GetValue(request_id_key); ok {
|
||||
fmt.Println("My request id is:", request_id)
|
||||
} else {
|
||||
fmt.Println("No request id found")
|
||||
}
|
||||
}
|
||||
|
||||
mgr.SetValues(gls.Values{request_id_key: "12345"}, func() {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
MyLog()
|
||||
}()
|
||||
wg.Wait()
|
||||
wg.Add(1)
|
||||
gls.Go(func() {
|
||||
defer wg.Done()
|
||||
MyLog()
|
||||
})
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
// Output: No request id found
|
||||
// My request id is: 12345
|
||||
}
|
||||
|
||||
func BenchmarkGetValue(b *testing.B) {
|
||||
mgr := gls.NewContextManager()
|
||||
mgr.SetValues(gls.Values{"test_key": "test_val"}, func() {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
val, ok := mgr.GetValue("test_key")
|
||||
if !ok || val != "test_val" {
|
||||
b.FailNow()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSetValues(b *testing.B) {
|
||||
mgr := gls.NewContextManager()
|
||||
for i := 0; i < b.N/2; i++ {
|
||||
mgr.SetValues(gls.Values{"test_key": "test_val"}, func() {
|
||||
mgr.SetValues(gls.Values{"test_key2": "test_val2"}, func() {})
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user