chore: updated intrumentation with otelchi

This commit is contained in:
William Artero 2023-08-14 17:20:27 +02:00
parent 92c50cd0c7
commit 8cdab0bbd4
Signed by: wwmoraes
GPG key ID: 4180618C988F24A3
11 changed files with 134 additions and 12 deletions

View file

@ -8,6 +8,7 @@ import (
"time"
"github.com/go-chi/chi/v5"
"github.com/riandyrn/otelchi"
"github.com/wwmoraes/anilistarr/internal/drivers/caches"
"github.com/wwmoraes/anilistarr/internal/telemetry"
"github.com/wwmoraes/anilistarr/internal/usecases"
@ -22,6 +23,10 @@ func main() {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
shutdown, err := telemetry.InstrumentAll(ctx, os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT"))
assert(err)
defer shutdown(context.Background())
log := telemetry.DefaultLogger()
log.Info("staring up", "name", telemetry.NAME, "version", telemetry.VERSION)
@ -45,13 +50,10 @@ func main() {
api, err := NewRestAPI(mapper)
assert(err)
shutdown, err := telemetry.InstrumentAll(ctx, os.Getenv("OTLP_ENDPOINT"))
assert(err)
defer shutdown(context.Background())
ctx = telemetry.ContextWithLogger(ctx)
r := chi.NewRouter()
r.Use(otelchi.Middleware(telemetry.NAME, otelchi.WithChiRoutes(r)))
r.Use(telemetry.NewHandlerMiddleware)
r.Use(Limiter(rate.NewLimiter(rate.Every(time.Minute), 1000)))
r.Get("/list", api.GetList)

8
doc.go Normal file
View file

@ -0,0 +1,8 @@
// Package anilistarr provides interfaces and a REST API that converts IDs from
// anime tracking services such as Anilist to the TVDB IDs wanted by *arr tools.
//
// It relies on community-provided mappings to match the IDs. Tracker
// information uses the upstream service directly.
//
// Copyright (c) William Artero. MIT License
package anilistarr // import "github.com/wwmoraes/anilistarr"

2
gen.go
View file

@ -1,6 +1,6 @@
package anilistarr
//go:generate go run ./cmd/version/... -package telemetry -name handler -namespace media -version "$VERSION" -output internal/telemetry/constants.go
//go:generate go run ./cmd/version/... -package telemetry -name anilistarr -namespace api -version "$VERSION" -output internal/telemetry/constants.go
//// DISABLED: this generator needs a db to derive the code from
// go:generate xo schema "file:tmp/media.db?loc=auto" -o internal/drivers/stores/models

4
go.mod
View file

@ -8,8 +8,10 @@ require (
github.com/XSAM/otelsql v0.23.0
github.com/go-chi/chi/v5 v5.0.10
github.com/go-logr/logr v1.2.4
github.com/pyroscope-io/client v0.7.1
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5
github.com/redis/go-redis/v9 v9.0.5
github.com/riandyrn/otelchi v0.5.1
go.etcd.io/bbolt v1.3.7
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0
go.opentelemetry.io/contrib/instrumentation/runtime v0.42.0
@ -38,9 +40,11 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pyroscope-io/godeltaprof v0.1.0 // indirect
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/vektah/gqlparser/v2 v2.5.1 // indirect
go.opentelemetry.io/contrib v1.0.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.39.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect

17
go.sum
View file

@ -80,17 +80,22 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@ -172,6 +177,10 @@ github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwp
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/pyroscope-io/client v0.7.1 h1:yFRhj3vbgjBxehvxQmedmUWJQ4CAfCHhn+itPsuWsHw=
github.com/pyroscope-io/client v0.7.1/go.mod h1:4h21iOU4pUOq0prKyDlvYRL+SCKsBc5wKiEtV+rJGqU=
github.com/pyroscope-io/godeltaprof v0.1.0 h1:UBqtjt0yZi4jTxqZmLAs34XG6ycS3vUTlhEUSq4NHLE=
github.com/pyroscope-io/godeltaprof v0.1.0/go.mod h1:psMITXp90+8pFenXkKIpNhrfmI9saQnPbba27VIaiQE=
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho=
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U=
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc=
@ -181,6 +190,8 @@ github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDO
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/riandyrn/otelchi v0.5.1 h1:0/45omeqpP7f/cvdL16GddQBfAEmZvUyl2QzLSE6uYo=
github.com/riandyrn/otelchi v0.5.1/go.mod h1:ZxVxNEl+jQ9uHseRYIxKWRb3OY8YXFEu+EkNiiSNUEA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
@ -203,10 +214,13 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
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.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8=
go.opentelemetry.io/contrib/instrumentation/runtime v0.42.0 h1:EbmAUG9hEAMXyfWEasIt2kmh/WmXUznUksChApTgBGc=
go.opentelemetry.io/contrib/instrumentation/runtime v0.42.0/go.mod h1:rD9feqRYP24P14t5kmhNMqsqm1jvKmpx2H2rKVw52V8=
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 h1:t4ZwRPU+emrcvM2e9DHd0Fsf0JTPVcbfa/BhTDF03d0=
@ -221,10 +235,12 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 h1:TVQp/
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0/go.mod h1:I33vtIe0sR96wfrUcilIzLoA3mLHhRmz9S9Te0S3gDo=
go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo=
go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4=
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE=
go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4=
go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI=
go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI=
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
@ -340,6 +356,7 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=

View file

@ -49,9 +49,9 @@ func (tracker *Tracker) GetUserID(ctx context.Context, name string) (string, err
}
func (tracker *Tracker) GetMediaListIDs(ctx context.Context, userId string) ([]string, error) {
log := telemetry.LoggerFromContext(ctx)
ctx, span := telemetry.StartFunction(ctx)
defer span.End()
log := telemetry.LoggerFromContext(ctx)
userIdInt, err := strconv.Atoi(userId)
if err != nil {

View file

@ -6,6 +6,6 @@ const (
ENVIRONMENT = "development"
MODULE = "github.com/wwmoraes/anilistarr"
VERSION = "0.1.0-rc.1"
NAME = "handler"
NAMESPACE = "media"
NAME = "anilistarr"
NAMESPACE = "api"
)

View file

@ -2,12 +2,34 @@ package telemetry
import (
"net/http"
"time"
"github.com/go-chi/chi/v5/middleware"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/metric"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
"go.opentelemetry.io/otel/trace"
)
var (
requestCounter metric.Int64Counter
requestSeconds metric.Float64Histogram
)
func initMetrics(meter metric.Meter) {
requestCounter = Must(globalMeter.Int64Counter(
"http.request.total",
metric.WithDescription("HTTP requests received"),
metric.WithUnit("requests"),
))
requestSeconds = Must(globalMeter.Float64Histogram(
"http.request.duration.seconds",
metric.WithDescription("HTTP request duration"),
metric.WithUnit("seconds"),
))
}
func NewHandler(handler http.Handler, operation string) http.Handler {
return otelhttp.NewHandler(handler, operation)
}
@ -16,8 +38,18 @@ func NewHandlerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx, span := globalTracer.StartHTTPResponse(r)
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
start := time.Now()
next.ServeHTTP(ww, r.WithContext(ctx))
attrs := metric.WithAttributes(
semconv.HTTPMethod(r.Method),
semconv.HTTPRoute(r.URL.Path),
semconv.HTTPStatusCode(ww.Status()),
semconv.ServiceName(NAME),
)
requestCounter.Add(ctx, 1, attrs)
requestSeconds.Record(ctx, time.Since(start).Seconds(), attrs)
span.EndWithStatus(ww.Status())
})
}

View file

@ -5,11 +5,13 @@ import (
"fmt"
"log"
"os"
"runtime"
"sync"
"time"
"github.com/MrAlias/otlpr"
"github.com/go-logr/logr"
"github.com/pyroscope-io/client/pyroscope"
otelruntime "go.opentelemetry.io/contrib/instrumentation/runtime"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
@ -59,6 +61,8 @@ func init() {
globalTracer = newTracer()
globalMeter = newMeter()
globalLogger = logr.New(NewStdLogSink())
initMetrics(globalMeter)
}
func getOTLPConnGRPC(ctx context.Context, otlpEndpoint string) (*grpc.ClientConn, error) {
@ -99,11 +103,9 @@ func InstrumentTracing(ctx context.Context, otlpEndpoint string) (func(context.C
bsp := sdktrace.NewBatchSpanProcessor(traceExporter)
traceProvider := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithResource(otlpResource),
sdktrace.WithSpanProcessor(bsp),
// flow.WithSpanProcessor(bsp),
)
otel.SetTracerProvider(traceProvider)
@ -147,7 +149,15 @@ func InstrumentLogging(ctx context.Context, otlpEndpoint string) error {
return err
}
logger := otlpr.WithResource(otlpr.New(conn), otlpResource)
logger := otlpr.NewWithOptions(conn, otlpr.Options{
LogCaller: otlpr.All,
LogCallerFunc: true,
Batcher: otlpr.Batcher{
Messages: 10,
Timeout: 10 * time.Second,
},
})
logger = otlpr.WithResource(logger, otlpResource)
otlpSink := logger.GetSink()
globalLogger = logger.WithSink(TeeSink(globalLogger.GetSink(), otlpSink))
@ -157,6 +167,28 @@ func InstrumentLogging(ctx context.Context, otlpEndpoint string) error {
return nil
}
func InstrumentProfiling(ctx context.Context) (func() error, error) {
runtime.SetMutexProfileFraction(5)
runtime.SetBlockProfileRate(5)
// TODO or https://pkg.go.dev/net/http/pprof
profiler, err := pyroscope.Start(pyroscope.Config{
ApplicationName: NAME,
ServerAddress: "",
AuthToken: "",
Logger: pyroscope.StandardLogger,
ProfileTypes: []pyroscope.ProfileType{
pyroscope.ProfileCPU,
pyroscope.ProfileAllocObjects,
},
})
if err != nil {
return nil, err
}
return profiler.Stop, nil
}
func InstrumentAll(ctx context.Context, otlpEndpoint string) (func(context.Context), error) {
tracingShutdown, err := InstrumentTracing(ctx, otlpEndpoint)
if err != nil {

View file

@ -0,0 +1,27 @@
package telemetry
import (
"os"
"go.opentelemetry.io/otel/metric"
)
var WithDescription = metric.WithDescription
var WithUnit = metric.WithUnit
func Must[M any](metric M, err error) M {
if err != nil {
globalLogger.Error(err, "failed to create metric")
os.Exit(1)
}
return metric
}
func Int64Counter(name string, options ...metric.Int64CounterOption) metric.Int64Counter {
return Must(globalMeter.Int64Counter(name, options...))
}
func Float64Histogram(name string, options ...metric.Float64HistogramOption) metric.Float64Histogram {
return Must(globalMeter.Float64Histogram(name, options...))
}

View file

@ -15,9 +15,9 @@ type MediaBridge struct {
}
func (linker *MediaBridge) GenerateCustomList(ctx context.Context, name string) (entities.SonarrCustomList, error) {
log := telemetry.LoggerFromContext(ctx).WithValues("username", name)
ctx, span := telemetry.StartFunction(ctx)
defer span.End()
log := telemetry.LoggerFromContext(ctx).WithValues("username", name)
log.Info("retrieving user ID")
userId, err := linker.GetUserID(ctx, name)