在 Golang 服務中整合 tracing
的那件小事
David Chou @ Golang Taipei / Crescendo Lab

CC-BY-SA-3.0-TW
@ Crescendo Lab
@ Golang Taipei Co-organizer 🙋♂️
Software engineer, DevOps, and Gopher 👨💻

david74.chou @ gmail
david74.chou @ facebook
david74chou @ telegram



想在 服務之間 整合好 tracing 該怎麼做?
以 Python 為例
$ ddtrace-run python app.py以 datadog 為例
以 Golang / Gin 為例
r := gin.New()
r.Use(gintrace.Middleware("web-app"))以 Golang / HTTP Request 為例
if s, ok := tracer.SpanFromContext(ctx); ok {
_ = tracer.Inject(s.Context(),
tracer.HTTPHeadersCarrier(req.Header),
)
}想在 服務內部 整合好 tracing 該怎麼做?
func doSomething() {
span := tracer.StartSpan("do.something")
defer span.Finish()
// do something cool
}一個一個加,很累
或者只加在 component 的邊界?

type SearchRepository interface {
SearchMessages(ctx context.Context, ...) (*chat.Result, error)
SearchMembersByName(ctx context.Context, ...) (*chat.Result, error)
... more ...
}
func NewSearchService(r SearchRepository) SearchService {
return SearchService {
searchRepo: r,
}
}今晚我想來點
Decorator Pattern
type SearchRepository interface {
SearchMessages(ctx context.Context, ...) (*chat.Result, error)
SearchMembersByName(ctx context.Context, ...) (*chat.Result, error)
... more ...
}
func NewSearchService(r SearchRepository) SearchService {
return SearchService {
searchRepo: autotrace.NewSearchRepositoryTracer(r),
}
}手寫 Decorator 也很累,能不能 Codegen?
//go:generate gowrap gen -g -p . -i SearchRepository
// -t file://template/ddog-trace.tmpl -o ./autotrace/search_repository.go
type SearchRepository interface {
SearchMessages(ctx context.Context, ...) (*chat.Result, error)
SearchMembersByName(ctx context.Context, ...) (*chat.Result, error)
... more ...
}只要多一行!
// Code generated by gowrap. DO NOT EDIT.
// template: ../../../../../template/ddog-trace.tmpl
package autotrace
type SearchRepositoryTracer struct {
SearchRepository
}
func NewSearchRepositoryTracer(base SearchRepository) SearchRepositoryTracer {
return SearchRepositoryTracer{
SearchRepository: base,
}
}
// SearchMessages implements chat.SearchRepository
func (d SearchRepositoryTracer) SearchMessages(ctx context.Context, ...)
(rp1 *chat.ResultMessageSearch, e1 error) {
span, ctx := tracer.StartSpanFromContext(ctx, "SearchRepository.SearchMessages")
defer func() {
span.Finish(d.withError(map[string]interface{}{
"rp1": rp1,
"e1": e1}))
}()
return d.SearchRepository.SearchMessages(ctx, channelID, searchText)
}
... more ...看看 template 長怎樣...
type {{$decorator}} struct {
{{.Interface.Name}}
}
func New{{$decorator}}(base {{.Interface.Name}}) {{$decorator}} {
return {{$decorator}}{
{{.Interface.Name}}: base,
}
}
{{range $method := .Interface.Methods}}
{{if $method.AcceptsContext}}
// {{$method.Name}} implements {{$.Interface.Type}}
func (d {{$decorator}}) {{$method.Declaration}} {
span, ctx := tracer.StartSpanFromContext(ctx, "{{$.Interface.Name}}.{{$method.Name}}")
defer func() {
span.Finish(d.withError({{$method.ResultsMap}}))
}()
{{$method.Pass (printf "d.%s." $.Interface.Name) }}
}
{{end}}
{{end}}gowrap 還有一堆好用的 template:
timeout, retry, ratelimit, opentracing, etc
希望沒有講太久QQ
Question?


在 Golang 服務中整合 tracing 的那件小事
By Ting-Li Chou
在 Golang 服務中整合 tracing 的那件小事
- 136