feat: tracing
This commit is contained in:
parent
ef9b72973e
commit
679cd032f8
|
@ -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
5
go.mod
|
@ -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
5
go.sum
|
@ -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=
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Reference in New Issue
Block a user