├── .github
└── workflows
│ └── go.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── go.mod
├── go.sum
├── hook.go
└── hook_test.go
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | name: Go
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 |
11 | build:
12 | name: Build
13 | runs-on: ubuntu-latest
14 | steps:
15 |
16 | - name: Set up Go 1.x
17 | uses: actions/setup-go@v2
18 | with:
19 | go-version: ^1.14
20 | id: go
21 |
22 | - name: Check out code into the Go module directory
23 | uses: actions/checkout@v2
24 |
25 | - name: Get dependencies
26 | run: |
27 | go get -v -t -d ./...
28 | - name: Build
29 | run: go build -v .
30 |
31 | - name: Test
32 | run: make test
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vscode/
3 |
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Globo.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | test:
2 | go test ./...
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-redis-opentracing
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | [go-redis](https://github.com/go-redis/redis) hook to collect OpenTracing spans.
16 |
17 | There are similar older libs that do not benefit from go-redis newer hooks feature.
18 | This is heavily inspired by https://github.com/go-redis/redis/blob/master/extra/redisotel/redisotel.go,
19 | but with support for OpenTracing instead of OpenTelemetry.
20 |
21 | Also check out our lib https://github.com/globocom/go-redis-prometheus.
22 |
23 | ## Installation
24 |
25 | go get github.com/globocom/go-redis-opentracing
26 |
27 | ## Usage
28 |
29 | ```golang
30 | package main
31 |
32 | import (
33 | redisopentracing "github.com/globocom/go-redis-opentracing"
34 | "github.com/go-redis/redis/v8"
35 | jaegerConfig "github.com/uber/jaeger-client-go/config"
36 | )
37 |
38 | func main() {
39 | cfg := &jaegerConfig.Configuration{
40 | ServiceName: "my-service-name",
41 | }
42 | tracer, _, _ := cfg.NewTracer()
43 |
44 | hook := redisopentracing.NewHook(tracer)
45 |
46 | client := redis.NewClient(&redis.Options{
47 | Addr: "localhost:6379",
48 | Password: "",
49 | })
50 | client.AddHook(hook)
51 |
52 | // run redis commands...
53 | }
54 | ```
55 |
56 | ## Note on pipelines
57 |
58 | Pipelines generate a single span. For each error that occurs on the pipeline, a tag `db.error` will be set.
59 |
60 | ## API stability
61 |
62 | The API is unstable at this point, and it might change before `v1.0.0` is released.
63 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/globocom/go-redis-opentracing
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/go-redis/redis/extra/rediscmd v0.2.0
7 | github.com/go-redis/redis/v8 v8.4.0
8 | github.com/opentracing/opentracing-go v1.2.0
9 | github.com/stretchr/testify v1.6.1
10 | )
11 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
2 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
3 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
6 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
7 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
8 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
9 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
10 | github.com/go-redis/redis/extra/rediscmd v0.2.0 h1:A3bhCsCKsedClEH9/jYlcKqOuBoeeV+H0yDie5t+a6w=
11 | github.com/go-redis/redis/extra/rediscmd v0.2.0/go.mod h1:Z5bP1EHl9PvWhx/DupfCdZwB0JgOO3aVxWc/PFux+BE=
12 | github.com/go-redis/redis/v8 v8.3.2/go.mod h1:jszGxBCez8QA1HWSmQxJO9Y82kNibbUmeYhKWrBejTU=
13 | github.com/go-redis/redis/v8 v8.4.0 h1:J5NCReIgh3QgUJu398hUncxDExN4gMOHI11NVbVicGQ=
14 | github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M=
15 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
16 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
17 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
18 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
19 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
20 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
21 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
22 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
23 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
24 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
25 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
26 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
27 | github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo=
28 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
29 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
30 | github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
31 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
32 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
33 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
34 | github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
35 | github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
36 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
37 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
38 | github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
39 | github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
40 | github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
41 | github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
42 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
43 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
44 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
45 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
46 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
47 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
48 | go.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY=
49 | go.opentelemetry.io/otel v0.14.0 h1:YFBEfjCk9MTjaytCNSUkp9Q8lF7QJezA06T71FbQxLQ=
50 | go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw=
51 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
52 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
53 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
54 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
55 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
56 | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M=
57 | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
58 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
59 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
60 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
61 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
62 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
63 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
64 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
65 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
66 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
67 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
68 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
69 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
70 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
71 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
72 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
73 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
74 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
75 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
76 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
77 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
78 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
79 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
80 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
81 | google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
82 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
83 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
84 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
85 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
86 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
87 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
88 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
89 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
90 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
91 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
92 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
93 |
--------------------------------------------------------------------------------
/hook.go:
--------------------------------------------------------------------------------
1 | package redisopentracing
2 |
3 | import (
4 | "context"
5 | "strconv"
6 |
7 | "github.com/go-redis/redis/v8"
8 | "github.com/opentracing/opentracing-go"
9 | "github.com/opentracing/opentracing-go/ext"
10 | )
11 |
12 | type RedisTracingHook struct {
13 | tracer opentracing.Tracer
14 | }
15 |
16 | var _ redis.Hook = RedisTracingHook{}
17 |
18 | // NewHook creates a new go-redis hook instance and that will collect spans using the provided tracer.
19 | func NewHook(tracer opentracing.Tracer) redis.Hook {
20 | return &RedisTracingHook{
21 | tracer: tracer,
22 | }
23 | }
24 |
25 | func (hook RedisTracingHook) createSpan(ctx context.Context, operationName string) (opentracing.Span, context.Context) {
26 | span := opentracing.SpanFromContext(ctx)
27 | if span != nil {
28 | childSpan := hook.tracer.StartSpan(operationName, opentracing.ChildOf(span.Context()))
29 | return childSpan, opentracing.ContextWithSpan(ctx, childSpan)
30 | }
31 |
32 | return opentracing.StartSpanFromContextWithTracer(ctx, hook.tracer, operationName)
33 | }
34 |
35 | func (hook RedisTracingHook) BeforeProcess(ctx context.Context, cmd redis.Cmder) (context.Context, error) {
36 | span, ctx := hook.createSpan(ctx, cmd.FullName())
37 | span.SetTag("db.type", "redis")
38 | return ctx, nil
39 | }
40 |
41 | func (hook RedisTracingHook) AfterProcess(ctx context.Context, cmd redis.Cmder) error {
42 | span := opentracing.SpanFromContext(ctx)
43 | defer span.Finish()
44 |
45 | if err := cmd.Err(); err != nil {
46 | recordError(ctx, "db.error", span, err)
47 | }
48 | return nil
49 | }
50 |
51 | func (hook RedisTracingHook) BeforeProcessPipeline(ctx context.Context, cmds []redis.Cmder) (context.Context, error) {
52 | span, ctx := hook.createSpan(ctx, "pipeline")
53 | span.SetTag("db.type", "redis")
54 | span.SetTag("db.redis.num_cmd", len(cmds))
55 | return ctx, nil
56 | }
57 |
58 | func (hook RedisTracingHook) AfterProcessPipeline(ctx context.Context, cmds []redis.Cmder) error {
59 | span := opentracing.SpanFromContext(ctx)
60 | defer span.Finish()
61 |
62 | for i, cmd := range cmds {
63 | if err := cmd.Err(); err != nil {
64 | recordError(ctx, "db.error"+strconv.Itoa(i), span, err)
65 | }
66 | }
67 | return nil
68 | }
69 |
70 | func recordError(ctx context.Context, errorTag string, span opentracing.Span, err error) {
71 | if err != redis.Nil {
72 | span.SetTag(string(ext.Error), true)
73 | span.SetTag(errorTag, err.Error())
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/hook_test.go:
--------------------------------------------------------------------------------
1 | package redisopentracing_test
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "testing"
7 |
8 | "github.com/go-redis/redis/v8"
9 | "github.com/opentracing/opentracing-go"
10 | "github.com/opentracing/opentracing-go/ext"
11 | "github.com/opentracing/opentracing-go/mocktracer"
12 | "github.com/stretchr/testify/assert"
13 |
14 | redisopentracing "github.com/globocom/go-redis-opentracing"
15 | )
16 |
17 | func TestHook(t *testing.T) {
18 | assert := assert.New(t)
19 |
20 | t.Run("create a new hook", func(t *testing.T) {
21 | // act
22 | sut := redisopentracing.NewHook(opentracing.NoopTracer{})
23 |
24 | // assert
25 | assert.NotNil(sut)
26 | })
27 |
28 | t.Run("starts a span before a command and finishes it after the command", func(t *testing.T) {
29 | // arrange
30 | tracer := mocktracer.New()
31 | sut := redisopentracing.NewHook(tracer)
32 |
33 | cmd := redis.NewStringCmd(context.Background(), "get")
34 |
35 | // act
36 | ctx, err1 := sut.BeforeProcess(context.Background(), cmd)
37 |
38 | // assert
39 | assert.Nil(err1)
40 | assert.Len(tracer.FinishedSpans(), 0)
41 |
42 | // act
43 | err2 := sut.AfterProcess(ctx, cmd)
44 |
45 | // assert
46 | assert.Nil(err2)
47 | assert.Len(tracer.FinishedSpans(), 1)
48 | span := tracer.FinishedSpans()[0]
49 | assert.Equal("get", span.OperationName)
50 | assert.Len(span.Tags(), 1)
51 | assert.Equal("redis", span.Tags()["db.type"])
52 | assert.Equal(nil, span.Tags()["db.error"])
53 | assert.Equal(nil, span.Tags()[string(ext.Error)])
54 | })
55 |
56 | t.Run("starts a span before a command and finishes it after the command, with error", func(t *testing.T) {
57 | // arrange
58 | tracer := mocktracer.New()
59 | sut := redisopentracing.NewHook(tracer)
60 |
61 | cmd := redis.NewStringCmd(context.Background(), "get")
62 | cmd.SetErr(errors.New("some error"))
63 |
64 | // act
65 | ctx, err1 := sut.BeforeProcess(context.Background(), cmd)
66 |
67 | // assert
68 | assert.Nil(err1)
69 | assert.Len(tracer.FinishedSpans(), 0)
70 |
71 | // act
72 | err2 := sut.AfterProcess(ctx, cmd)
73 |
74 | // assert
75 | assert.Nil(err2)
76 | assert.Len(tracer.FinishedSpans(), 1)
77 | span := tracer.FinishedSpans()[0]
78 | assert.Equal("get", span.OperationName)
79 | assert.Len(span.Tags(), 3)
80 | assert.Equal("redis", span.Tags()["db.type"])
81 | assert.Equal("some error", span.Tags()["db.error"])
82 | assert.Equal(true, span.Tags()[string(ext.Error)])
83 | })
84 |
85 | t.Run("starts a span before a pipeline and finishes it after the pipeline", func(t *testing.T) {
86 | // arrange
87 | tracer := mocktracer.New()
88 | sut := redisopentracing.NewHook(tracer)
89 |
90 | cmd1 := redis.NewStringCmd(context.Background(), "get")
91 | cmd2 := redis.NewStringCmd(context.Background(), "dbsize")
92 | cmd3 := redis.NewStringCmd(context.Background(), "set x y")
93 | cmds := []redis.Cmder{cmd1, cmd2, cmd3}
94 |
95 | // act
96 | ctx, err1 := sut.BeforeProcessPipeline(context.Background(), cmds)
97 |
98 | // assert
99 | assert.Nil(err1)
100 | assert.Len(tracer.FinishedSpans(), 0)
101 |
102 | // act
103 | err2 := sut.AfterProcessPipeline(ctx, cmds)
104 |
105 | // assert
106 | assert.Nil(err2)
107 | assert.Len(tracer.FinishedSpans(), 1)
108 |
109 | span := tracer.FinishedSpans()[0]
110 | assert.Equal("pipeline", span.OperationName)
111 |
112 | assert.Len(span.Tags(), 2)
113 | assert.Equal("redis", span.Tags()["db.type"])
114 | assert.Equal(3, span.Tags()["db.redis.num_cmd"])
115 |
116 | assert.Equal(nil, span.Tags()["db.error0"])
117 | assert.Equal(nil, span.Tags()["db.error1"])
118 | assert.Equal(nil, span.Tags()["db.error2"])
119 | assert.Equal(nil, span.Tags()[string(ext.Error)])
120 | })
121 |
122 | t.Run("starts a span before a pipeline and finishes it after the pipeline, with error", func(t *testing.T) {
123 | // arrange
124 | tracer := mocktracer.New()
125 | sut := redisopentracing.NewHook(tracer)
126 |
127 | cmd1 := redis.NewStringCmd(context.Background(), "get")
128 | cmd2 := redis.NewStringCmd(context.Background(), "dbsize")
129 | cmd2.SetErr(errors.New("error 1 in pipeline cmd"))
130 | cmd3 := redis.NewStringCmd(context.Background(), "set x y")
131 | cmd3.SetErr(errors.New("error 2 in pipeline cmd"))
132 | cmds := []redis.Cmder{cmd1, cmd2, cmd3}
133 | // act
134 | ctx, err1 := sut.BeforeProcessPipeline(context.Background(), cmds)
135 |
136 | // assert
137 | assert.Nil(err1)
138 | assert.Len(tracer.FinishedSpans(), 0)
139 |
140 | // act
141 | err2 := sut.AfterProcessPipeline(ctx, cmds)
142 |
143 | // assert
144 | assert.Nil(err2)
145 | assert.Len(tracer.FinishedSpans(), 1)
146 |
147 | span := tracer.FinishedSpans()[0]
148 | assert.Equal("pipeline", span.OperationName)
149 |
150 | assert.Len(span.Tags(), 5)
151 | assert.Equal("redis", span.Tags()["db.type"])
152 | assert.Equal(3, span.Tags()["db.redis.num_cmd"])
153 |
154 | assert.Equal(nil, span.Tags()["db.error0"])
155 | assert.Equal("error 1 in pipeline cmd", span.Tags()["db.error1"])
156 | assert.Equal("error 2 in pipeline cmd", span.Tags()["db.error2"])
157 | assert.Equal(true, span.Tags()[string(ext.Error)])
158 | })
159 | }
160 |
--------------------------------------------------------------------------------