From 3be94cfda1683bbb4e80d3f23bfcf219eabc3d07 Mon Sep 17 00:00:00 2001 From: Nicholas Wiersma Date: Thu, 28 Mar 2024 10:01:37 +0200 Subject: [PATCH] feat: support profiling (#250) Co-authored-by: Brendan Le Glaunec --- .golangci.yml | 1 + README.md | 28 ++++++++++ example_test.go | 21 +++++++ go.mod | 4 ++ go.sum | 22 ++++++++ profile.go | 142 ++++++++++++++++++++++++++++++++++++++++++++++++ profile_test.go | 83 ++++++++++++++++++++++++++++ trace.go | 2 +- 8 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 profile.go create mode 100644 profile_test.go diff --git a/.golangci.yml b/.golangci.yml index a37defa..5f397b6 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -26,6 +26,7 @@ linters: - exhaustive - exhaustruct - forcetypeassert + - funlen - gochecknoglobals - gochecknoinits - goerr113 diff --git a/README.md b/README.md index 21d8b88..49f7811 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,34 @@ This flag sets tag key value pairs to set on all stats. This flag can be specifi Example: `--stats.tags="app=my-app" --stats.tags="zone=eu-west"` +### Profiler + +The profiler flags are used by `cmd.NewProfiler` to create a Pyroscope `*pyroscope.Profiler`. + +#### FlagProfilingDSN: *--profiling.dsn, $PROFILING_DSN* + +This flag configures the URL, authentication and optionally the Tenant ID for Pyroscope. + +Example: `--profiling.dsn=https://user:pass@host/path?token=auth-token&tenantid=my-tenant-id` + +#### FlagProfileUploadRate: *--profiling.upload-rate, $PROFILING_UPLOAD_RATE* + +This flag configures the rate at which profiles are uploaded. + +Example: `--profiling.upload-rate=10s` + +#### FlagProfilingTags: *--profiling.tags, $PROFILING_TAGS* + +This configures a list of tags appended to every profile. This flag can be specified multiple times. + +Example: `--profiling.tags="app=my-app" --profiling.tags="zone=eu-west"` + +#### FlagProfilingTypes: *--profiling.types, $PROFILING_TYPES* + +This configures the profile types that are captured. By default all supported types are captured. This flag can be specified multiple times. + +Example: `--profiling.types=cpu --profiling.types=inuse_object` + ### Tracer The tracing flags are used by `cmd.NewTracer` to create a new open telemetry `trace.TraceProvider`. diff --git a/example_test.go b/example_test.go index 832837e..8047d41 100644 --- a/example_test.go +++ b/example_test.go @@ -40,6 +40,27 @@ func ExampleNewStatter() { _ = stats } +func ExampleNewProfiler() { + var c *cli.Context // Get this from your action + + log, err := cmd.NewLogger(c) + if err != nil { + // Handle error. + return + } + + prof, err := cmd.NewProfiler(c, "my-service", log) + if err != nil { + // Handle error. + return + } + if prof != nil { + defer func() { _ = prof.Stop() }() + } + + _ = prof +} + func ExampleNewTracer() { var c *cli.Context // Get this from your action diff --git a/go.mod b/go.mod index d293531..ad7159b 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.21 require ( github.com/fatih/color v1.16.0 + github.com/grafana/otel-profiling-go v0.5.1 + github.com/grafana/pyroscope-go v1.1.1 github.com/hamba/logger/v2 v2.5.1 github.com/hamba/statter/v2 v2.3.5 github.com/stretchr/testify v1.9.0 @@ -28,7 +30,9 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/grafana/pyroscope-go/godeltaprof v0.1.6 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect + github.com/klauspost/compress v1.17.3 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 8313f41..860615e 100644 --- a/go.sum +++ b/go.sum @@ -10,11 +10,13 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -27,6 +29,12 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= +github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= +github.com/grafana/pyroscope-go v1.1.1 h1:PQoUU9oWtO3ve/fgIiklYuGilvsm8qaGhlY4Vw6MAcQ= +github.com/grafana/pyroscope-go v1.1.1/go.mod h1:Mw26jU7jsL/KStNSGGuuVYdUq7Qghem5P8aXYXSXG88= +github.com/grafana/pyroscope-go/godeltaprof v0.1.6 h1:nEdZ8louGAplSvIJi1HVp7kWvFvdiiYg3COLlTwJiFo= +github.com/grafana/pyroscope-go/godeltaprof v0.1.6/go.mod h1:Tk376Nbldo4Cha9RgiU7ik8WKFkNpfds98aUzS8omLE= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= github.com/hamba/logger/v2 v2.5.1 h1:EM+S6CPYIs66XmW9kK/RBghgtkLcO4kQWWPAlsIwNR4= @@ -34,6 +42,8 @@ github.com/hamba/logger/v2 v2.5.1/go.mod h1:b+y7XddZMTTSIjKdOOIHWlhg1hMoE9eKhanB github.com/hamba/statter/v2 v2.3.5 h1:qAFu+n4Q08LtrBdcBYexItfOoJJ16D/m/boq8fm5hn4= github.com/hamba/statter/v2 v2.3.5/go.mod h1:LS7DqETxoVpRraL3tn9O70zYCaVRL2A98SvZw/LZkAI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/klauspost/compress v1.17.3 h1:qkRjuerhUU1EmXLYGkSH6EZL+vPSxIrYjLNAK4slzwA= +github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -61,8 +71,14 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= @@ -73,6 +89,7 @@ github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OL github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY= github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8= @@ -83,10 +100,13 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM= go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY= go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= @@ -97,6 +117,7 @@ golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= @@ -117,5 +138,6 @@ google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/profile.go b/profile.go new file mode 100644 index 0000000..50b22ec --- /dev/null +++ b/profile.go @@ -0,0 +1,142 @@ +package cmd + +import ( + "errors" + "fmt" + "net/url" + "time" + + "github.com/grafana/pyroscope-go" + "github.com/hamba/logger/v2" + "github.com/urfave/cli/v2" +) + +var allProfilingTypes = []pyroscope.ProfileType{ + pyroscope.ProfileCPU, + pyroscope.ProfileInuseObjects, + pyroscope.ProfileAllocObjects, + pyroscope.ProfileInuseSpace, + pyroscope.ProfileAllocSpace, + pyroscope.ProfileGoroutines, + pyroscope.ProfileMutexCount, + pyroscope.ProfileMutexDuration, + pyroscope.ProfileBlockCount, + pyroscope.ProfileBlockDuration, +} + +// Tracing flag constants declared for CLI use. +const ( + FlagProfilingDSN = "profiling.dsn" + FlagProfileUploadRate = "profiling.upload-rate" + FlagProfilingTags = "profiling.tags" + FlagProfilingTypes = "profiling.types" +) + +// ProfilingFlags are flags that configure profiling. +var ProfilingFlags = Flags{ + &cli.StringFlag{ + Name: FlagProfilingDSN, + Usage: "The address to the Pyroscope server, in the format " + + "'http://basic:auth@server:port?token=auth-token&tenantid=tenant-id'.", + EnvVars: []string{"PROFILING_DSN"}, + }, + &cli.DurationFlag{ + Name: FlagProfileUploadRate, + Usage: "The rate at which profiles are uploaded.", + Value: 15 * time.Second, + EnvVars: []string{"PROFILING_UPLOAD_RATE"}, + }, + &cli.StringSliceFlag{ + Name: FlagProfilingTags, + Usage: "A list of tags appended to every profile. Format: key=value.", + EnvVars: []string{"PROFILING_TAGS"}, + }, + &cli.StringSliceFlag{ + Name: FlagProfilingTypes, + Usage: "The type of profiles to include. Defaults to all.", + EnvVars: []string{"PROFILING_TYPES"}, + }, +} + +// NewProfiler returns a profiler configured from the cli. +// If no profiler is configured, nil is returned. +func NewProfiler(c *cli.Context, svc string, log *logger.Logger) (*pyroscope.Profiler, error) { + dsn := c.String(FlagProfilingDSN) + if dsn == "" { + //nolint:nilnil // There is no sentinel in this case. + return nil, nil + } + + u, err := url.Parse(dsn) + if err != nil { + return nil, fmt.Errorf("parsing profiling DSN: %w", err) + } + + tenantID := u.Query().Get("tenantid") + + authToken := u.Query().Get("token") + var username, password string + if u.User != nil { + username = u.User.Username() + password, _ = u.User.Password() + } + if (username != "" || password != "") && authToken != "" { + return nil, errors.New("cannot set auth token and basic auth") + } + + srvURL := &url.URL{ + Scheme: u.Scheme, + Host: u.Host, + Path: u.Path, + } + + tags := map[string]string{} + if pairs := c.StringSlice(FlagProfilingTags); len(pairs) > 0 { + strTags, err := Split(pairs, "=") + if err != nil { + return nil, err + } + for _, kv := range strTags { + tags[kv[0]] = kv[1] + } + } + + types := allProfilingTypes + if newTypes := c.StringSlice(FlagProfilingTypes); len(newTypes) > 0 { + types = make([]pyroscope.ProfileType, len(newTypes)) + for i, typ := range newTypes { + types[i] = pyroscope.ProfileType(typ) + } + } + + cfg := pyroscope.Config{ + ApplicationName: svc, + Tags: tags, + ServerAddress: srvURL.String(), + AuthToken: authToken, + BasicAuthUser: username, + BasicAuthPassword: password, + TenantID: tenantID, + UploadRate: c.Duration(FlagProfileUploadRate), + Logger: pyroLogAdapter{log: log}, + ProfileTypes: types, + } + + return pyroscope.Start(cfg) +} + +type pyroLogAdapter struct { + log *logger.Logger +} + +func (a pyroLogAdapter) Infof(format string, args ...any) { + a.log.Info(fmt.Sprintf(format, args...)) +} + +func (a pyroLogAdapter) Debugf(format string, args ...any) { + a.log.Debug(fmt.Sprintf(format, args...)) +} + +func (a pyroLogAdapter) Errorf(format string, args ...any) { + a.log.Error(fmt.Sprintf(format, args...)) +} diff --git a/profile_test.go b/profile_test.go new file mode 100644 index 0000000..be25294 --- /dev/null +++ b/profile_test.go @@ -0,0 +1,83 @@ +package cmd_test + +import ( + "io" + "testing" + "time" + + "github.com/hamba/cmd/v2" + "github.com/hamba/logger/v2" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v2" +) + +func TestNewProfiler(t *testing.T) { + log := logger.New(io.Discard, logger.LogfmtFormat(), logger.Error) + + tests := []struct { + name string + dsn string + uploadRate time.Duration + tags []string + types []string + wantErr assert.ErrorAssertionFunc + }{ + { + name: "ignores no profiling", + wantErr: assert.NoError, + }, + { + name: "starts profiler", + dsn: "https://example.com?token=test&tenantid=me", + wantErr: assert.NoError, + }, + { + name: "supports tags", + dsn: "https://example.com?token=test&tenantid=me", + tags: []string{"cluster=test", "namespace=num"}, + wantErr: assert.NoError, + }, + { + name: "supports types", + dsn: "https://example.com?token=test&tenantid=me", + types: []string{"cpu"}, + wantErr: assert.NoError, + }, + { + name: "handles bad dsn", + dsn: ":/", + wantErr: assert.Error, + }, + { + name: "handles basic and token auth", + dsn: "https://test:test@example.com?token=test&tenantid=me", + wantErr: assert.Error, + }, + { + name: "handles bad tags", + dsn: "https://example.com?token=test&tenantid=me", + tags: []string{"cluster", "namespace=num"}, + wantErr: assert.Error, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + // t.Parallel() + + c, fs := newTestContext() + fs.String(cmd.FlagProfilingDSN, test.dsn, "doc") + fs.Duration(cmd.FlagProfileUploadRate, test.uploadRate, "doc") + fs.Var(cli.NewStringSlice(test.tags...), cmd.FlagProfilingTags, "doc") + fs.Var(cli.NewStringSlice(test.types...), cmd.FlagProfilingTypes, "doc") + + profiler, err := cmd.NewProfiler(c, "my-service", log) + if profiler != nil { + _ = profiler.Stop() + } + + test.wantErr(t, err) + }) + } +} diff --git a/trace.go b/trace.go index e025e1a..d96b832 100644 --- a/trace.go +++ b/trace.go @@ -55,7 +55,7 @@ var TracingFlags = Flags{ }, } -// NewTracer returns a tracer configures from the cli. +// NewTracer returns a tracer configured from the cli. func NewTracer(c *cli.Context, log *logger.Logger, resAttributes ...attribute.KeyValue) (*trace.TracerProvider, error) { otel.SetErrorHandler(logErrorHandler{log: log})