Skip to content

Commit 6097a2d

Browse files
authored
Merge pull request #6 from friendsofgo/playing_with_zipkin
Adapt gopherAPI to use Zipkin
2 parents df17abe + 92619e9 commit 6097a2d

File tree

10 files changed

+168
-12
lines changed

10 files changed

+168
-12
lines changed

.env

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
GOPHERAPI_NAME=GOPERAPI
22
GOPHERAPI_SERVER_HOST=localhost
3-
GOPHERAPI_SERVER_PORT=3000
3+
GOPHERAPI_SERVER_PORT=3000
4+
5+
ZIPKIN_ENDPOINT=http://localhost:9411

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
[![CircleCI](https://circleci.com/gh/friendsofgo/gopherapi/tree/master.svg?style=svg)](https://circleci.com/gh/friendsofgo/gopherapi/tree/master)
22

33
# Gopher API
4-
The Gopher API, is a simple CRUD API for formative purpose, we're building it while writing the posts of the [blog](https://blog.friendsofgo.tech).
4+
The Gopher API, is a evolutive simple CRUD API for formative purpose, we're building it while writing the posts of the [blog](https://blog.friendsofgo.tech).
5+
6+
In this API we've learnt differents, features and patterns in Go:
7+
8+
* Using Gorilla Mux to create an simple API
9+
* Using a SOLID, Hexagonal Architecture
10+
* Testing HTTP handlers
11+
* Integration with CircleCI
12+
* Using Wire to build dependencies [only in v0.3.1](https://github.com/friendsofgo/gopherapi/releases/tag/v0.3.1)
13+
* Using pattern contextkey
14+
* Using instrumenting with Zipkin
515

616
## How can I use it?
717

cmd/gopherapi/main.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/friendsofgo/gopherapi/pkg/removing"
1818
"github.com/friendsofgo/gopherapi/pkg/server"
1919
"github.com/friendsofgo/gopherapi/pkg/storage/inmem"
20+
"github.com/friendsofgo/gopherapi/pkg/tracer"
2021

2122
_ "github.com/joho/godotenv/autoload"
2223
)
@@ -28,6 +29,8 @@ func main() {
2829
defaultServerID = fmt.Sprintf("%s-%s", os.Getenv("GOPHERAPI_NAME"), hostName)
2930
defaultHost = os.Getenv("GOPHERAPI_SERVER_HOST")
3031
defaultPort, _ = strconv.Atoi(os.Getenv("GOPHERAPI_SERVER_PORT"))
32+
33+
zipkinURL = os.Getenv("ZIPKIN_ENDPOINT")
3134
)
3235

3336
host := flag.String("host", defaultHost, "define host of the server")
@@ -42,8 +45,12 @@ func main() {
4245
}
4346

4447
logger := logrus.NewLogger()
48+
trc, err := tracer.NewTracer(*serverID, zipkinURL)
49+
if err != nil {
50+
log.Fatal(err)
51+
}
4552

46-
repo := inmem.NewRepository(gophers)
53+
repo := inmem.NewRepository(gophers, trc)
4754
fetchingService := fetching.NewService(repo, logger)
4855
addingService := adding.NewService(repo)
4956
modifyingService := modifying.NewService(repo)
@@ -53,6 +60,7 @@ func main() {
5360

5461
s := server.New(
5562
*serverID,
63+
trc,
5664
fetchingService,
5765
addingService,
5866
modifyingService,

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ module github.com/friendsofgo/gopherapi
33
require (
44
github.com/gorilla/mux v1.7.0
55
github.com/joho/godotenv v1.3.0
6+
github.com/openzipkin/zipkin-go v0.2.0
67
github.com/sirupsen/logrus v1.4.2
78
)

go.sum

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,62 @@
1+
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2+
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
3+
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
4+
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
5+
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
16
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
8+
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
9+
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
10+
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
11+
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
12+
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
13+
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
14+
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
15+
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
16+
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
17+
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
18+
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
19+
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
220
github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
321
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
22+
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
423
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
524
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
625
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
26+
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
27+
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
28+
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
29+
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
30+
github.com/openzipkin/zipkin-go v0.2.0 h1:33/f6xXB6YlOQ9tgTsXVOkdLCJsHTcZJnMy4DnSd6FU=
31+
github.com/openzipkin/zipkin-go v0.2.0/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
32+
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
33+
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
734
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
35+
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
836
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
937
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
38+
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
1039
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1140
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
41+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
42+
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
43+
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
44+
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
45+
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
46+
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
47+
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
48+
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
49+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1250
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
1351
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
52+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
53+
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
54+
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
55+
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
56+
google.golang.org/grpc v1.20.0 h1:DlsSIrgEBuZAUFJcta2B5i/lzeHHbnfkNFAfFXLVFYQ=
57+
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
58+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
59+
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
60+
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
61+
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
62+
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

pkg/server/middleware.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ package server
22

33
import (
44
"context"
5+
"fmt"
56
"net"
67
"net/http"
8+
9+
"github.com/gorilla/mux"
10+
"github.com/openzipkin/zipkin-go"
711
)
812

913
type handler struct {
@@ -47,6 +51,18 @@ func (h handler) createRequestContext(req *http.Request) context.Context {
4751
ctx = context.WithValue(ctx, contextKeyEndpoint, req.URL.RequestURI())
4852

4953
ctx = context.WithValue(ctx, contextKeyServerID, h.serverID)
50-
54+
zipkinSpanHttpName(ctx, req)
5155
return ctx
5256
}
57+
58+
func zipkinSpanHttpName(ctx context.Context, req *http.Request) {
59+
if span := zipkin.SpanFromContext(ctx); span != nil {
60+
if currentRoute := mux.CurrentRoute(req); currentRoute != nil {
61+
if routePath, err := currentRoute.GetPathTemplate(); err == nil {
62+
zipkin.TagHTTPRoute.Set(span, routePath)
63+
span.SetName(fmt.Sprintf("%s %s", req.Method, routePath))
64+
65+
}
66+
}
67+
}
68+
}

pkg/server/server.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import (
44
"encoding/json"
55
"net/http"
66

7+
"github.com/openzipkin/zipkin-go"
8+
zipkinhttp "github.com/openzipkin/zipkin-go/middleware/http"
9+
710
"github.com/friendsofgo/gopherapi/pkg/adding"
811
"github.com/friendsofgo/gopherapi/pkg/fetching"
912
"github.com/friendsofgo/gopherapi/pkg/modifying"
@@ -15,9 +18,11 @@ import (
1518
// server all server necessary dependencies
1619
type server struct {
1720
serverID string
18-
httpAddr string
1921

20-
router http.Handler
22+
tracer *zipkin.Tracer
23+
24+
router http.Handler
25+
2126
fetching fetching.Service
2227
adding adding.Service
2328
modifying modifying.Service
@@ -37,12 +42,15 @@ type Server interface {
3742
// New initialize the server
3843
func New(
3944
serverID string,
45+
tracer *zipkin.Tracer,
4046
fS fetching.Service,
4147
aS adding.Service,
4248
mS modifying.Service,
43-
rS removing.Service) Server {
49+
rS removing.Service,
50+
) Server {
4451
a := &server{
4552
serverID: serverID,
53+
tracer: tracer,
4654
fetching: fS,
4755
adding: aS,
4856
modifying: mS,
@@ -55,7 +63,10 @@ func New(
5563
func router(s *server) {
5664
r := mux.NewRouter()
5765

58-
r.Use(newServerMiddleware(s.serverID))
66+
r.Use(
67+
zipkinhttp.NewServerMiddleware(s.tracer),
68+
newServerMiddleware(s.serverID),
69+
)
5970

6071
r.HandleFunc("/gophers", s.FetchGophers).Methods(http.MethodGet)
6172
r.HandleFunc("/gophers/{ID:[a-zA-Z0-9_]+}", s.FetchGopher).Methods(http.MethodGet)

pkg/server/server_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/friendsofgo/gopherapi/pkg/log"
1313
"github.com/friendsofgo/gopherapi/pkg/removing"
14+
"github.com/friendsofgo/gopherapi/pkg/tracer"
1415

1516
"github.com/friendsofgo/gopherapi/pkg/adding"
1617
"github.com/friendsofgo/gopherapi/pkg/fetching"
@@ -185,11 +186,13 @@ func gopherSample() *gopher.Gopher {
185186
}
186187

187188
func buildServer() Server {
188-
repo := inmem.NewRepository(sample.Gophers)
189+
190+
noopTracer := tracer.NewNoopTracer()
191+
repo := inmem.NewRepository(sample.Gophers, tracer.NewNoopTracer())
189192
fS := fetching.NewService(repo, log.NewNoopLogger())
190193
aS := adding.NewService(repo)
191194
mS := modifying.NewService(repo)
192195
rS := removing.NewService(repo)
193196

194-
return New("test", fS, aS, mS, rS)
197+
return New("test", noopTracer, fS, aS, mS, rS)
195198
}

pkg/storage/inmem/repository.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,29 @@ import (
44
"context"
55
"fmt"
66
"sync"
7+
"time"
8+
9+
"github.com/openzipkin/zipkin-go"
710

811
gopher "github.com/friendsofgo/gopherapi/pkg"
912
)
1013

1114
type gopherRepository struct {
12-
mtx sync.RWMutex
15+
mtx sync.RWMutex
16+
tracer *zipkin.Tracer
17+
1318
gophers map[string]gopher.Gopher
1419
}
1520

1621
// NewRepository creates a inmem repository with the necessary dependencies
17-
func NewRepository(gophers map[string]gopher.Gopher) gopher.Repository {
22+
func NewRepository(gophers map[string]gopher.Gopher, tracer *zipkin.Tracer) gopher.Repository {
1823
if gophers == nil {
1924
gophers = make(map[string]gopher.Gopher)
2025
}
2126

2227
return &gopherRepository{
2328
gophers: gophers,
29+
tracer: tracer,
2430
}
2531
}
2632

@@ -60,6 +66,15 @@ func (r *gopherRepository) UpdateGopher(ctx context.Context, ID string, g gopher
6066
}
6167

6268
func (r *gopherRepository) FetchGopherByID(ctx context.Context, ID string) (*gopher.Gopher, error) {
69+
span, _ := r.tracer.StartSpanFromContext(ctx, "FetchGopherByID")
70+
span.Tag("Repository", "in memory")
71+
span.Annotate(time.Now(), "Transaction Start")
72+
73+
defer func() {
74+
span.Annotate(time.Now(), "Transaction End")
75+
span.Finish()
76+
}()
77+
6378
r.mtx.Lock()
6479
defer r.mtx.Unlock()
6580

pkg/tracer/tracer.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package tracer
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/openzipkin/zipkin-go"
7+
"github.com/openzipkin/zipkin-go/model"
8+
"github.com/openzipkin/zipkin-go/reporter/http"
9+
)
10+
11+
// NewTracer creates a new tracer with the necessary dependencies
12+
func NewTracer(serviceName, reporterURL string) (*zipkin.Tracer, error) {
13+
14+
reporter := http.NewReporter(fmt.Sprintf("%s/api/v2/spans", reporterURL))
15+
16+
endpoint := &model.Endpoint{
17+
ServiceName: serviceName,
18+
}
19+
20+
// sampler indicate the range of how many traces are going to be sampled
21+
sampler, err := zipkin.NewCountingSampler(1)
22+
if err != nil {
23+
return nil, err
24+
}
25+
26+
t, err := zipkin.NewTracer(
27+
reporter,
28+
zipkin.WithSampler(sampler),
29+
zipkin.WithLocalEndpoint(endpoint),
30+
)
31+
if err != nil {
32+
return nil, err
33+
}
34+
return t, nil
35+
}
36+
37+
// NewNoopTracer creates a new no operational tracer with the necessary dependencies
38+
func NewNoopTracer() *zipkin.Tracer {
39+
noopTracer, _ := zipkin.NewTracer(nil)
40+
return noopTracer
41+
}

0 commit comments

Comments
 (0)