feat: tracing

This commit is contained in:
Dawid Wysokiński 2022-09-11 07:49:01 +02:00
parent ef9b72973e
commit 679cd032f8
Signed by: Kichiyaki
GPG Key ID: B5445E357FB8B892
5 changed files with 124 additions and 21 deletions

View File

@ -5,6 +5,8 @@ import (
"time"
"gitea.dwysokinski.me/twhelp/core/internal/tw"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/propagation"
)
const (
@ -13,7 +15,13 @@ const (
func NewTWClient(version string) *tw.Client {
return tw.NewClient(
tw.WithHTTPClient(&http.Client{Timeout: twClientTimeout}),
tw.WithHTTPClient(&http.Client{
Timeout: twClientTimeout,
Transport: otelhttp.NewTransport(
http.DefaultTransport,
otelhttp.WithPropagators(propagation.NewCompositeTextMapPropagator()),
),
}),
tw.WithUserAgent("tribalwarshelp/"+version),
)
}

5
go.mod
View File

@ -27,10 +27,12 @@ require (
github.com/uptrace/bun/driver/pgdriver v1.1.8
github.com/uptrace/bun/extra/bunotel v1.1.8
github.com/urfave/cli/v2 v2.14.1
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.34.0
go.opentelemetry.io/otel v1.9.0
go.opentelemetry.io/otel/exporters/jaeger v1.9.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.9.0
go.opentelemetry.io/otel/sdk v1.9.0
go.opentelemetry.io/otel/trace v1.9.0
go.uber.org/zap v1.23.0
)
@ -47,7 +49,7 @@ require (
github.com/docker/docker v20.10.7+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/felixge/httpsnoop v1.0.2 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
@ -85,7 +87,6 @@ require (
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.opentelemetry.io/contrib v1.0.0 // indirect
go.opentelemetry.io/otel/metric v0.31.0 // indirect
go.opentelemetry.io/otel/trace v1.9.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect

5
go.sum
View File

@ -52,8 +52,9 @@ github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/elliotchance/phpserialize v1.3.3 h1:hV4QVmGdCiYgoBbw+ADt6fNgyZ2mYX0OgpnON1adTCM=
github.com/elliotchance/phpserialize v1.3.3/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs=
github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/garsue/watermillzap v1.1.1 h1:qReFh5goSOoub8x0cc+iSaaeIMJF0m66nmdesqG7VBg=
github.com/garsue/watermillzap v1.1.1/go.mod h1:6wdF9VgwK7JMANUtlpUUfNwkrFRWQ4G+U/dgBcWpvTA=
@ -249,6 +250,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/contrib v1.0.0 h1:khwDCxdSspjOLmFnvMuSHd/5rPzbTx0+l6aURwtQdfE=
go.opentelemetry.io/contrib v1.0.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2bvnvzBlGM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.34.0 h1:9NkMW03wwEzPtP/KciZ4Ozu/Uz5ZA7kfqXJIObnrjGU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.34.0/go.mod h1:548ZsYzmT4PL4zWKRd8q/N4z0Wxzn/ZxUE+lkEpwWQA=
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
go.opentelemetry.io/otel v1.9.0 h1:8WZNQFIB2a71LnANS9JeyidJKKGOOremcUtb/OtHISw=
go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo=

View File

@ -7,6 +7,7 @@ import (
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
"gitea.dwysokinski.me/twhelp/core/internal/domain"
@ -113,6 +114,7 @@ func (e *Ennoblement) List(ctx context.Context, params domain.ListEnnoblementsPa
},
}
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, 0, err
}
@ -122,6 +124,7 @@ func (e *Ennoblement) List(ctx context.Context, params domain.ListEnnoblementsPa
if err := validatePagination(params.Pagination, ennoblementMaxLimit); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, 0, fmt.Errorf("validatePagination: %w", err)
}

View File

@ -14,9 +14,11 @@ import (
"strings"
"time"
"github.com/elliotchance/phpserialize"
"gitea.dwysokinski.me/twhelp/core/internal/domain"
"github.com/elliotchance/phpserialize"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)
const (
@ -88,25 +90,32 @@ func NewClient(opts ...ClientOption) *Client {
}
func (c *Client) GetOpenServers(ctx context.Context, baseURL string) ([]domain.OpenServer, error) {
ctx, span := tracer.Start(ctx, "Client.GetBuildingInfo", trace.WithAttributes(
attribute.String("base_url", baseURL),
))
defer span.End()
resp, err := c.get(ctx, buildURL(baseURL, endpointGetServers))
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("c.get: %w", err)
}
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
_, _ = io.Copy(io.Discard, resp.Body)
return nil, fmt.Errorf("Non-OK HTTP status: %d", resp.StatusCode)
}
b, err := io.ReadAll(resp.Body)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("couldn't read response body: %w", err)
}
m, err := phpserialize.UnmarshalAssociativeArray(b)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("phpserialize.UnmarshalAssociativeArray: %w", err)
}
@ -114,12 +123,18 @@ func (c *Client) GetOpenServers(ctx context.Context, baseURL string) ([]domain.O
for key, val := range m {
keyStr, ok := key.(string)
if !ok || keyStr == "" {
return nil, fmt.Errorf("%w: %v", ErrInvalidServerKey, key)
err = fmt.Errorf("%w: %v", ErrInvalidServerKey, key)
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, err
}
urlStr, ok := val.(string)
if !ok || urlStr == "" {
return nil, fmt.Errorf("%w: %v", ErrInvalidServerURL, val)
err = fmt.Errorf("%w: %v", ErrInvalidServerURL, val)
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, err
}
servers = append(servers, domain.OpenServer{
@ -132,9 +147,16 @@ func (c *Client) GetOpenServers(ctx context.Context, baseURL string) ([]domain.O
}
func (c *Client) GetServerConfig(ctx context.Context, baseURL string) (domain.ServerConfig, error) {
ctx, span := tracer.Start(ctx, "Client.GetServerConfig", trace.WithAttributes(
attribute.String("base_url", baseURL),
))
defer span.End()
var cfg serverConfig
if err := c.getXML(ctx, buildURL(baseURL, endpointConfig), &cfg); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return domain.ServerConfig{}, fmt.Errorf("c.getXML: %w", err)
}
@ -142,9 +164,16 @@ func (c *Client) GetServerConfig(ctx context.Context, baseURL string) (domain.Se
}
func (c *Client) GetBuildingInfo(ctx context.Context, baseURL string) (domain.BuildingInfo, error) {
ctx, span := tracer.Start(ctx, "Client.GetBuildingInfo", trace.WithAttributes(
attribute.String("base_url", baseURL),
))
defer span.End()
var info buildingInfo
if err := c.getXML(ctx, buildURL(baseURL, endpointBuildingInfo), &info); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return domain.BuildingInfo{}, fmt.Errorf("c.getXML: %w", err)
}
@ -152,9 +181,16 @@ func (c *Client) GetBuildingInfo(ctx context.Context, baseURL string) (domain.Bu
}
func (c *Client) GetUnitInfo(ctx context.Context, baseURL string) (domain.UnitInfo, error) {
ctx, span := tracer.Start(ctx, "Client.GetUnitInfo", trace.WithAttributes(
attribute.String("base_url", baseURL),
))
defer span.End()
var info unitInfo
if err := c.getXML(ctx, buildURL(baseURL, endpointUnitInfo), &info); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return domain.UnitInfo{}, fmt.Errorf("c.getXML: %w", err)
}
@ -162,13 +198,22 @@ func (c *Client) GetUnitInfo(ctx context.Context, baseURL string) (domain.UnitIn
}
func (c *Client) GetTribes(ctx context.Context, baseURL string) ([]domain.BaseTribe, error) {
ctx, span := tracer.Start(ctx, "Client.GetTribes", trace.WithAttributes(
attribute.String("base_url", baseURL),
))
defer span.End()
od, err := c.getOD(ctx, baseURL, true)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("c.getOD: %w", err)
}
records, err := c.getCSV(ctx, buildURL(baseURL, endpointTribes), fieldsPerRecordTribe)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("c.getCSV: %w", err)
}
@ -176,6 +221,8 @@ func (c *Client) GetTribes(ctx context.Context, baseURL string) ([]domain.BaseTr
for _, rec := range records {
tribe, err := parseTribeRecord(rec, od)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("parseTribeRecord: %w", err)
}
@ -190,13 +237,22 @@ func (c *Client) GetTribes(ctx context.Context, baseURL string) ([]domain.BaseTr
}
func (c *Client) GetPlayers(ctx context.Context, baseURL string) ([]domain.BasePlayer, error) {
ctx, span := tracer.Start(ctx, "Client.GetPlayers", trace.WithAttributes(
attribute.String("base_url", baseURL),
))
defer span.End()
od, err := c.getOD(ctx, baseURL, false)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("c.getOD: %w", err)
}
records, err := c.getCSV(ctx, buildURL(baseURL, endpointPlayers), fieldsPerRecordPlayer)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("c.getCSV: %w", err)
}
@ -204,6 +260,8 @@ func (c *Client) GetPlayers(ctx context.Context, baseURL string) ([]domain.BaseP
for _, rec := range records {
player, err := parsePlayerRecord(rec, od)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("parsePlayerRecord: %w", err)
}
@ -218,6 +276,12 @@ func (c *Client) GetPlayers(ctx context.Context, baseURL string) ([]domain.BaseP
}
func (c *Client) getOD(ctx context.Context, baseURL string, tribe bool) (map[int64]domain.OpponentsDefeated, error) {
ctx, span := tracer.Start(ctx, "Client.getOD", trace.WithAttributes(
attribute.String("base_url", baseURL),
attribute.Bool("tribe", tribe),
))
defer span.End()
m := make(map[int64]domain.OpponentsDefeated)
urls := buildODURLs(baseURL, tribe)
@ -228,12 +292,16 @@ func (c *Client) getOD(ctx context.Context, baseURL string, tribe bool) (map[int
records, err := c.getCSV(ctx, u, fieldsPerRecordOD)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("c.getCSV: %w", err)
}
for _, rec := range records {
parsed, err := parseODRecord(rec)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("parseODRecord: %w", err)
}
@ -262,8 +330,15 @@ func (c *Client) getOD(ctx context.Context, baseURL string, tribe bool) (map[int
}
func (c *Client) GetVillages(ctx context.Context, baseURL string) ([]domain.BaseVillage, error) {
ctx, span := tracer.Start(ctx, "Client.GetVillages", trace.WithAttributes(
attribute.String("base_url", baseURL),
))
defer span.End()
records, err := c.getCSV(ctx, buildURL(baseURL, endpointVillages), fieldsPerRecordVillage)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("c.getCSV: %w", err)
}
@ -271,6 +346,8 @@ func (c *Client) GetVillages(ctx context.Context, baseURL string) ([]domain.Base
for _, rec := range records {
village, err := parseVillageRecord(rec)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("parseVillageRecord: %w", err)
}
@ -285,8 +362,16 @@ func (c *Client) GetVillages(ctx context.Context, baseURL string) ([]domain.Base
}
func (c *Client) GetEnnoblements(ctx context.Context, baseURL string, since time.Time) ([]domain.BaseEnnoblement, error) {
ctx, span := tracer.Start(ctx, "Client.GetEnnoblements", trace.WithAttributes(
attribute.String("base_url", baseURL),
attribute.Int64("since", since.Unix()),
))
defer span.End()
records, err := c.getCSV(ctx, buildEnnoblementURL(baseURL, since), fieldsPerRecordEnnoblement)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("c.getCSV: %w", err)
}
@ -295,7 +380,10 @@ func (c *Client) GetEnnoblements(ctx context.Context, baseURL string, since time
for _, rec := range records {
createdAt, err := parseTimestamp(rec[1])
if err != nil {
return nil, NewParseError(err, rec, "ennoblement.CreatedAt")
err = NewParseError(err, rec, "ennoblement.CreatedAt")
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, err
}
if !createdAt.After(since) {
@ -309,6 +397,8 @@ func (c *Client) GetEnnoblements(ctx context.Context, baseURL string, since time
for _, rec := range records {
ennoblement, err := parseEnnoblementRecord(rec)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("parseEnnoblementRecord: %w", err)
}
@ -330,10 +420,6 @@ func (c *Client) getXML(ctx context.Context, url string, v any) error {
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
_, _ = io.Copy(io.Discard, resp.Body)
return fmt.Errorf("got non-ok HTTP status: %d", resp.StatusCode)
}
if err := xml.NewDecoder(resp.Body).Decode(v); err != nil {
return fmt.Errorf("xml.Decode: %w", err)
@ -350,10 +436,6 @@ func (c *Client) getCSV(ctx context.Context, url string, fieldsPerRecord int) ([
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
_, _ = io.Copy(io.Discard, resp.Body)
return nil, fmt.Errorf("got non-ok HTTP status: %d", resp.StatusCode)
}
r := csv.NewReader(resp.Body)
r.Comma = ','
@ -381,6 +463,12 @@ func (c *Client) get(ctx context.Context, url string) (*http.Response, error) {
return nil, fmt.Errorf("client.Do: %w", err)
}
if resp.StatusCode != http.StatusOK {
_, _ = io.Copy(io.Discard, resp.Body)
_ = resp.Body.Close()
return nil, fmt.Errorf("got non-ok HTTP status: %d", resp.StatusCode)
}
return resp, nil
}