diff --git a/cmd/example/apis/archive/zz_generated.operator.go b/cmd/example/apis/archive/zz_generated.operator.go index 5b4db7f..8f9b5fe 100644 --- a/cmd/example/apis/archive/zz_generated.operator.go +++ b/cmd/example/apis/archive/zz_generated.operator.go @@ -1,5 +1,5 @@ /* -Package archive GENERATED BY gengo:operator +Package archive GENERATED BY gengo:operator DON'T EDIT THIS FILE */ package archive diff --git a/cmd/example/apis/archive/zz_generated.runtimedoc.go b/cmd/example/apis/archive/zz_generated.runtimedoc.go index 2e25a48..7452e9f 100644 --- a/cmd/example/apis/archive/zz_generated.runtimedoc.go +++ b/cmd/example/apis/archive/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package archive GENERATED BY gengo:runtimedoc +Package archive GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package archive diff --git a/cmd/example/apis/org/zz_generated.enum.go b/cmd/example/apis/org/zz_generated.enum.go index 2d13985..53027d2 100644 --- a/cmd/example/apis/org/zz_generated.enum.go +++ b/cmd/example/apis/org/zz_generated.enum.go @@ -1,5 +1,5 @@ /* -Package org GENERATED BY gengo:enum +Package org GENERATED BY gengo:enum DON'T EDIT THIS FILE */ package org @@ -43,8 +43,9 @@ func ParseTypeFromString(s string) (Type, error) { case "COMPANY": return TYPE__COMPANY, nil + default: + return TYPE_UNKNOWN, InvalidType } - return TYPE_UNKNOWN, InvalidType } func (v Type) String() string { @@ -54,8 +55,9 @@ func (v Type) String() string { case TYPE__COMPANY: return "COMPANY" + default: + return "UNKNOWN" } - return "UNKNOWN" } func ParseTypeLabelString(label string) (Type, error) { @@ -65,8 +67,9 @@ func ParseTypeLabelString(label string) (Type, error) { case "企事业单位": return TYPE__COMPANY, nil + default: + return TYPE_UNKNOWN, InvalidType } - return TYPE_UNKNOWN, InvalidType } func (v Type) Label() string { @@ -76,8 +79,9 @@ func (v Type) Label() string { case TYPE__COMPANY: return "企事业单位" + default: + return "UNKNOWN" } - return "UNKNOWN" } func (v Type) Value() (database_sql_driver.Value, error) { diff --git a/cmd/example/apis/org/zz_generated.operator.go b/cmd/example/apis/org/zz_generated.operator.go index f0dd270..c7f475a 100644 --- a/cmd/example/apis/org/zz_generated.operator.go +++ b/cmd/example/apis/org/zz_generated.operator.go @@ -1,5 +1,5 @@ /* -Package org GENERATED BY gengo:operator +Package org GENERATED BY gengo:operator DON'T EDIT THIS FILE */ package org @@ -30,7 +30,7 @@ func init() { } func (*GetOrg) ResponseContent() any { - return nil + return new(Detail) } func (*GetOrg) ResponseErrors() []error { diff --git a/cmd/example/apis/org/zz_generated.runtimedoc.go b/cmd/example/apis/org/zz_generated.runtimedoc.go index 68ef167..264b75f 100644 --- a/cmd/example/apis/org/zz_generated.runtimedoc.go +++ b/cmd/example/apis/org/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package org GENERATED BY gengo:runtimedoc +Package org GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package org diff --git a/cmd/example/serve.go b/cmd/example/serve.go index 59de489..0274f6b 100644 --- a/cmd/example/serve.go +++ b/cmd/example/serve.go @@ -31,6 +31,5 @@ func init() { type Serve struct { cli.C `component:"server"` otel.Otel - Metric otel.Metric Server http.Server } diff --git a/cmd/example/zz_generated.runtimedoc.go b/cmd/example/zz_generated.runtimedoc.go index e319eed..e247097 100644 --- a/cmd/example/zz_generated.runtimedoc.go +++ b/cmd/example/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package main GENERATED BY gengo:runtimedoc +Package main GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package main diff --git a/go.mod b/go.mod index d90a086..59bbdd3 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,14 @@ module github.com/innoai-tech/infra go 1.22 require ( - cuelang.org/go v0.8.0 + cuelang.org/go v0.8.1 github.com/andybalholm/brotli v1.1.0 + github.com/fatih/color v1.16.0 github.com/felixge/httpsnoop v1.0.4 github.com/go-courier/logr v0.3.0 - github.com/octohelm/courier v0.0.0-20240326021148-b5564592fbe1 - github.com/octohelm/gengo v0.0.0-20240227021734-f42e02623187 + github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 + github.com/octohelm/courier v0.0.0-20240409065337-58f586c9d64c + github.com/octohelm/gengo v0.0.0-20240409082121-aeffa5400f19 github.com/octohelm/storage v0.0.0-20240309041530-950a8155cecf github.com/octohelm/x v0.0.0-20240219080259-91528f21e203 github.com/pkg/errors v0.9.1 @@ -16,45 +18,45 @@ require ( github.com/prometheus/prometheus v0.51.1 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 - go.opentelemetry.io/contrib/propagators/b3 v1.24.0 - go.opentelemetry.io/otel v1.24.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.24.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 - go.opentelemetry.io/otel/exporters/prometheus v0.46.0 - go.opentelemetry.io/otel/metric v1.24.0 - go.opentelemetry.io/otel/sdk v1.24.0 - go.opentelemetry.io/otel/sdk/metric v1.24.0 - go.opentelemetry.io/otel/trace v1.24.0 - golang.org/x/net v0.22.0 - golang.org/x/sync v0.6.0 + go.opentelemetry.io/contrib/propagators/b3 v1.25.0 + go.opentelemetry.io/otel v1.25.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.25.0 + go.opentelemetry.io/otel/exporters/prometheus v0.47.0 + go.opentelemetry.io/otel/log v0.1.0-alpha + go.opentelemetry.io/otel/metric v1.25.0 + go.opentelemetry.io/otel/sdk v1.25.0 + go.opentelemetry.io/otel/sdk/log v0.0.0-20240409082250-b9752eb5dc19 + go.opentelemetry.io/otel/sdk/metric v1.25.0 + go.opentelemetry.io/otel/trace v1.25.0 + golang.org/x/net v0.24.0 + golang.org/x/sync v0.7.0 + golang.org/x/term v0.19.0 ) require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect - github.com/aws/aws-sdk-go v1.51.11 // indirect + github.com/aws/aws-sdk-go v1.51.17 // indirect github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect - github.com/fatih/color v1.16.0 // indirect - github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.6.0 // indirect @@ -75,26 +77,25 @@ require ( github.com/onsi/gomega v1.31.1 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_model v0.6.0 // indirect - github.com/prometheus/common v0.51.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.52.2 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect github.com/prometheus/procfs v0.13.0 // indirect github.com/stretchr/testify v1.9.0 // indirect - go.opentelemetry.io/proto/otlp v1.1.0 // indirect + go.opentelemetry.io/proto/otlp v1.2.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.3.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect - golang.org/x/mod v0.16.0 // indirect - golang.org/x/oauth2 v0.18.0 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/oauth2 v0.19.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.19.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa // indirect - google.golang.org/grpc v1.62.1 // indirect + golang.org/x/tools v0.20.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect + google.golang.org/grpc v1.63.2 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index fcb4749..e86ec4d 100644 --- a/go.sum +++ b/go.sum @@ -32,11 +32,11 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e h1:GwCVItFUPxwdsEYnlUcJ6PJxOjTeFFCKOh6QWg4oAzQ= cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e/go.mod h1:ApHceQLLwcOkCEXM1+DyCXTHEJhNGDpJ2kmV6axsx24= -cuelang.org/go v0.8.0 h1:fO1XPe/SUGtc7dhnGnTPbpIDoQm/XxhDtoSF7jzO01c= -cuelang.org/go v0.8.0/go.mod h1:CoDbYolfMms4BhWUlhD+t5ORnihR7wvjcfgyO9lL5FI= +cuelang.org/go v0.8.1 h1:VFYsxIFSPY5KgSaH1jQ2GxHOrbu6Ga3kEI70yCZwnOg= +cuelang.org/go v0.8.1/go.mod h1:CoDbYolfMms4BhWUlhD+t5ORnihR7wvjcfgyO9lL5FI= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 h1:n1DH8TPV4qqPTje2RcUBYwtrTWlabVp4n46+74X2pn4= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0/go.mod h1:HDcZnuGbiyppErN6lB+idp4CKhjbc8gwjto6OPpyggM= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo= github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ= @@ -65,8 +65,8 @@ github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer5 github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.51.11 h1:El5VypsMIz7sFwAAj/j06JX9UGs4KAbAIEaZ57bNY4s= -github.com/aws/aws-sdk-go v1.51.11/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.51.17 h1:Cfa40lCdjv9OxC3X1Ks3a6O1Tu3gOANSyKHOSw/zuWU= +github.com/aws/aws-sdk-go v1.51.17/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3/go.mod h1:CIWtjkly68+yqLPbvwwR/fjNJA/idrtULjZWh2v1ys0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -77,8 +77,8 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -196,8 +196,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -244,6 +242,7 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww= github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/hashicorp/consul/api v1.28.2 h1:mXfkRHrpHN4YY3RqL09nXU1eHKLNiuAN4kHvDQ16k/8= @@ -357,10 +356,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/octohelm/courier v0.0.0-20240326021148-b5564592fbe1 h1:fnEwooRevMouBky7ZBC8qEn6BD+F7jJJDQz9UiiO2fw= -github.com/octohelm/courier v0.0.0-20240326021148-b5564592fbe1/go.mod h1:pYLVU0csQKN91hCmXfmtdMrPjWzu0BcGTf5Ma7elLKQ= -github.com/octohelm/gengo v0.0.0-20240227021734-f42e02623187 h1:Wh5gi+T8ILp1C0xCE6lMeTdWENigyu4d6Wt8pInU5Lk= -github.com/octohelm/gengo v0.0.0-20240227021734-f42e02623187/go.mod h1:XOAb8VZg5nS/Ihol8stTTqLkuXccrHdERi2LGhyMdgM= +github.com/octohelm/courier v0.0.0-20240409065337-58f586c9d64c h1:UE2/8zQheC5sLjT3hsd0f/KKWmgdNUkdw9nhAoZYaVU= +github.com/octohelm/courier v0.0.0-20240409065337-58f586c9d64c/go.mod h1:pYLVU0csQKN91hCmXfmtdMrPjWzu0BcGTf5Ma7elLKQ= +github.com/octohelm/gengo v0.0.0-20240409082121-aeffa5400f19 h1:l8ahFVtDhXAcKFmR1/6Sevs1R71Kai7dfp95ZlG7raY= +github.com/octohelm/gengo v0.0.0-20240409082121-aeffa5400f19/go.mod h1:Q0f7oofuZJ6T2vk5vsXgjKwKNlOJO0TTrjGfA9yRJwA= github.com/octohelm/storage v0.0.0-20240309041530-950a8155cecf h1:4nYa5jmuEYkP4xN3DbNVikjXmwevde07JwZPXMgn+oI= github.com/octohelm/storage v0.0.0-20240309041530-950a8155cecf/go.mod h1:6l/cYsD85nR5VP5j9JRYvRP0HKZWT+cH8P1Wg+JRbc0= github.com/octohelm/x v0.0.0-20240219080259-91528f21e203 h1:ldyiEQngq5u7IajycYf35EUXeMDeYFtsjj2aEYTabsI= @@ -396,14 +395,14 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= -github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.51.1 h1:eIjN50Bwglz6a/c3hAgSMcofL3nD+nFQkV6Dd4DsQCw= -github.com/prometheus/common v0.51.1/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= +github.com/prometheus/common v0.52.2 h1:LW8Vk7BccEdONfrJBDffQGRtpSzi5CQaRZGtboOO2ck= +github.com/prometheus/common v0.52.2/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -445,7 +444,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -455,26 +453,52 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/contrib/propagators/b3 v1.24.0 h1:n4xwCdTx3pZqZs2CjS/CUZAs03y3dZcGhC/FepKtEUY= go.opentelemetry.io/contrib/propagators/b3 v1.24.0/go.mod h1:k5wRxKRU2uXx2F8uNJ4TaonuEO/V7/5xoz7kdsDACT8= +go.opentelemetry.io/contrib/propagators/b3 v1.25.0 h1:QU8UEKyPqgr/8vCC9LlDmkPnfFmiWAUF9GtJdcLz+BU= +go.opentelemetry.io/contrib/propagators/b3 v1.25.0/go.mod h1:qonC7wyvtX1E6cEpAR+bJmhcGr6IVRGc/f6ZTpvi7jA= 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 v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= +go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.24.0 h1:f2jriWfOdldanBwS9jNBdeOKAQN7b4ugAMaNu1/1k9g= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.24.0/go.mod h1:B+bcQI1yTY+N0vqMpoZbEN7+XU4tNM0DmUiOwebFJWI= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0 h1:hDKnobznDpcdTlNzO0S/owRB8tyVr1OoeZZhDoqY+Cs= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0/go.mod h1:kUDQaUs1h8iTIHbQTk+iJRiUvSfJYMMKTtMCaiVu7B0= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.25.0 h1:vOL89uRfOCCNIjkisd0r7SEdJF3ZJFyCNY34fdZs8eU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.25.0/go.mod h1:8GlBGcDk8KKi7n+2S4BT/CPZQYH3erLu0/k64r1MYgo= go.opentelemetry.io/otel/exporters/prometheus v0.46.0 h1:I8WIFXR351FoLJYuloU4EgXbtNX2URfU/85pUPheIEQ= go.opentelemetry.io/otel/exporters/prometheus v0.46.0/go.mod h1:ztwVUHe5DTR/1v7PeuGRnU5Bbd4QKYwApWmuutKsJSs= +go.opentelemetry.io/otel/exporters/prometheus v0.47.0 h1:OL6yk1Z/pEGdDnrBbxSsH+t4FY1zXfBRGd7bjwhlMLU= +go.opentelemetry.io/otel/exporters/prometheus v0.47.0/go.mod h1:xF3N4OSICZDVbbYZydz9MHFro1RjmkPUKEvar2utG+Q= +go.opentelemetry.io/otel/log v0.1.0-alpha h1:CDbo8tCcR2ACXH4YkJnyLM9URo79LLxxAL1TG2AoACw= +go.opentelemetry.io/otel/log v0.1.0-alpha/go.mod h1:B4xaNmGRNECaOqny/Z7lym0i/p/apNGF8e/9KStHWp0= 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/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= +go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= 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/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= +go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= +go.opentelemetry.io/otel/sdk/log v0.0.0-20240408165649-e028d07d0d1d h1:aPoc9IswH5uFhN+I/EDcJ/Bniwt0VypbBVXN/EKs5Dw= +go.opentelemetry.io/otel/sdk/log v0.0.0-20240408165649-e028d07d0d1d/go.mod h1:hhtugoOtNL5LhgMYdSshVsCw6VqcKLlQGnhlq5YroMc= +go.opentelemetry.io/otel/sdk/log v0.0.0-20240409082250-b9752eb5dc19 h1:BV8ZTerNMlMsdma81/IKCTJjPI0/lIxT9Y0yahUV2GY= +go.opentelemetry.io/otel/sdk/log v0.0.0-20240409082250-b9752eb5dc19/go.mod h1:hhtugoOtNL5LhgMYdSshVsCw6VqcKLlQGnhlq5YroMc= go.opentelemetry.io/otel/sdk/metric v1.24.0 h1:yyMQrPzF+k88/DbH7o4FMAs80puqd+9osbiBrJrz/w8= go.opentelemetry.io/otel/sdk/metric v1.24.0/go.mod h1:I6Y5FjH6rvEnTTAYQz3Mmv2kl6Ek5IIrmwTLqMrrOE0= +go.opentelemetry.io/otel/sdk/metric v1.25.0 h1:7CiHOy08LbrxMAp4vWpbiPcklunUshVpAvGBrdDRlGw= +go.opentelemetry.io/otel/sdk/metric v1.25.0/go.mod h1:LzwoKptdbBBdYfvtGCzGwk6GWMA3aUzBOwtQpR6Nz7o= 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= -go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= +go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= +go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= +go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -485,9 +509,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -498,8 +521,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= -golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -520,9 +543,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -553,19 +575,17 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= +golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -576,9 +596,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -616,27 +635,21 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -686,9 +699,8 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -715,8 +727,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -746,10 +756,10 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa h1:Jt1XW5PaLXF1/ePZrznsh/aAUvI7Adfc3LY1dAKlzRs= -google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:K4kfzHtI0kqWA79gecJarFtDn/Mls+GxQcg3Zox91Ac= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa h1:RBgMaUMP+6soRkik4VoN8ojR2nex2TqZwjSSogic+eo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda h1:b6F6WIV4xHHD0FA4oIyzU6mHWg2WI2X1RBehwa5QN38= +google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda/go.mod h1:AHcE/gZH76Bk/ROZhQphlRoWo5xKDEtz3eVEO1LfA8c= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -762,8 +772,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -775,7 +785,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -813,6 +822,7 @@ k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= diff --git a/internal/cmd/tool/main.go b/internal/cmd/tool/main.go index c786bbc..345dad2 100644 --- a/internal/cmd/tool/main.go +++ b/internal/cmd/tool/main.go @@ -17,11 +17,15 @@ import ( var App = cli.NewApp("tool", "dev") func init() { - cli.AddTo(App, &struct { + c := &struct { cli.C `name:"gen"` otel.Otel gengo.Gengo - }{}) + }{} + + c.LogLevel = otel.DebugLevel + + cli.AddTo(App, c) } func main() { diff --git a/internal/otel/logger.go b/internal/otel/logger.go new file mode 100644 index 0000000..0a65cbd --- /dev/null +++ b/internal/otel/logger.go @@ -0,0 +1,199 @@ +package otel + +import ( + "context" + "fmt" + "time" + + "github.com/go-courier/logr" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/log" + "go.opentelemetry.io/otel/trace" +) + +func NewLogger(ctx context.Context, levelEnabled logr.Level) logr.Logger { + return &logger{ + loggerContext: loggerContext{ + ctx: ctx, + enabled: levelEnabled, + }, + } +} + +type logger struct { + spanContext + loggerContext + + keyValues []log.KeyValue +} + +func (t *logger) WithValues(keyAndValues ...any) logr.Logger { + if len(keyAndValues) == 0 { + return t + } + + return &logger{ + spanContext: t.spanContext, + loggerContext: t.loggerContext, + keyValues: append(t.keyValues, normalizeKeyValues(keyAndValues)...), + } +} + +func (t *logger) Start(ctx context.Context, name string, keyAndValues ...any) (context.Context, logr.Logger) { + var parentID trace.SpanID + + parentSpan := trace.SpanContextFromContext(ctx) + if parentSpan.HasSpanID() { + parentID = parentSpan.SpanID() + } + + spanCtx, c := t.spanContext.Start(ctx, name) + + lgr := &logger{ + keyValues: append(t.keyValues, normalizeKeyValues(keyAndValues)...), + spanContext: spanCtx, + loggerContext: t.loggerContext.Start(c, name, parentID), + } + + return logr.WithLogger(c, lgr), lgr +} + +func (t *logger) End() { + endAt := time.Now() + t.span().End(trace.WithTimestamp(endAt)) +} + +func (t *logger) span() trace.Span { + return t.spanContext.span +} + +func (t *logger) Debug(msgOrFormat string, args ...any) { + t.info(logr.DebugLevel, sprintf(msgOrFormat, args...), t.keyValues) +} + +func (t *logger) Info(msgOrFormat string, args ...any) { + t.info(logr.InfoLevel, sprintf(msgOrFormat, args...), t.keyValues) +} + +func (t *logger) Warn(err error) { + t.error(logr.WarnLevel, err, t.keyValues, func(err error) { + errMsg := err.Error() + t.span().RecordError(err) + t.span().SetStatus(codes.Error, errMsg) + }) +} + +func (t *logger) Error(err error) { + t.error(logr.ErrorLevel, err, t.keyValues, func(err error) { + errMsg := err.Error() + t.span().RecordError(err) + t.span().SetStatus(codes.Error, errMsg) + }) +} + +type loggerContext struct { + ctx context.Context + enabled logr.Level + + log.Logger + + startedAt time.Time + parentID trace.SpanID +} + +func (l *loggerContext) emit(level logr.Level, msg fmt.Stringer, keyValues []log.KeyValue) { + if l.Logger == nil { + return + } + + var rec log.Record + + switch level { + case logr.DebugLevel: + rec.SetSeverity(log.SeverityDebug) + case logr.InfoLevel: + rec.SetSeverity(log.SeverityInfo) + case logr.WarnLevel: + rec.AddAttributes(GetSource(3).AsKeyValues()...) + rec.SetSeverity(log.SeverityWarn) + case logr.ErrorLevel: + rec.AddAttributes(GetSource(3).AsKeyValues()...) + rec.SetSeverity(log.SeverityError) + } + + if len(keyValues) > 0 { + rec.AddAttributes(keyValues...) + } + + if !l.startedAt.IsZero() { + rec.AddAttributes(log.String("cost", time.Since(l.startedAt).String())) + } + + if l.parentID.IsValid() { + rec.AddAttributes(log.String("parentSpanID", l.parentID.String())) + } + + rec.SetTimestamp(time.Now()) + rec.SetBody(log.StringValue(msg.String())) + + l.Emit(l.ctx, rec) +} + +func (l *loggerContext) info(level logr.Level, msg fmt.Stringer, keyValues []log.KeyValue) { + if level > l.enabled { + return + } + + l.emit(level, msg, keyValues) +} + +func (l *loggerContext) error(level logr.Level, err error, keyValues []log.KeyValue, postDo func(err error)) { + if level > l.enabled { + return + } + + if err == nil { + return + } + + l.emit(level, sprintf("%s", err), keyValues) + + postDo(err) +} + +func (l loggerContext) Start(ctx context.Context, name string, parentID trace.SpanID) loggerContext { + l.ctx = ctx + l.Logger = Logger(ctx, name) + l.startedAt = time.Now() + l.parentID = parentID + return l +} + +type spanContext struct { + name string + span trace.Span + tracer trace.Tracer +} + +func (c spanContext) Start(ctx context.Context, spanName string) (spanContext, context.Context) { + cc, span := Tracer(ctx).Start(ctx, c.name, trace.WithTimestamp(time.Now())) + c.span = span + c.name = spanName + return c, cc +} + +func sprintf(format string, args ...any) fmt.Stringer { + return &printer{format: format, args: args} +} + +type printer struct { + format string + args []any +} + +func (p *printer) String() string { + if len(p.args) == 0 { + return p.format + } + return fmt.Sprintf(p.format, p.args...) +} diff --git a/internal/otel/logger_exporter.go b/internal/otel/logger_exporter.go new file mode 100644 index 0000000..67f0aaa --- /dev/null +++ b/internal/otel/logger_exporter.go @@ -0,0 +1,134 @@ +package otel + +import ( + "bytes" + "context" + "cuelang.org/go/cue/cuecontext" + cueformat "cuelang.org/go/cue/format" + "cuelang.org/go/encoding/gocode/gocodec" + "fmt" + "github.com/fatih/color" + "go.opentelemetry.io/otel/log" + sdklog "go.opentelemetry.io/otel/sdk/log" + "golang.org/x/term" + "io" + "os" + "strings" + "sync" + "sync/atomic" +) + +func findTTY() (*os.File, bool) { + // some of these may be redirected + for _, f := range []*os.File{os.Stderr, os.Stdout, os.Stdin} { + if term.IsTerminal(int(f.Fd())) { + return f, true + } + } + return nil, false +} + +var isTTY = false + +func init() { + _, tty := findTTY() + if tty { + isTTY = true + } + if os.Getenv("TTY") == "0" { + isTTY = false + } +} + +func SlogExporter() sdklog.Exporter { + if isTTY { + return &prettyExporter{} + } + + return &jsonExporter{} +} + +type prettyExporter struct { + done atomic.Bool + wg sync.WaitGroup +} + +func (e *prettyExporter) ForceFlush(ctx context.Context) error { + return nil +} + +func (e *prettyExporter) Shutdown(ctx context.Context) error { + return nil +} + +func (e *prettyExporter) Export(ctx context.Context, records []sdklog.Record) error { + for _, r := range records { + if r.Severity() >= log.SeverityWarn1 { + if err := e.print(os.Stderr, r); err != nil { + return err + } + continue + } + if err := e.print(os.Stdout, r); err != nil { + return err + } + } + + return nil +} + +func (e *prettyExporter) print(f io.Writer, r sdklog.Record) error { + w := bytes.NewBuffer(nil) + + prefix := color.CyanString("%s:", r.SpanID().String()) + _, _ = fmt.Fprint(w, prefix) + _, _ = fmt.Fprint(w, " ") + _, _ = fmt.Fprint(w, strings.ToUpper(severityText(r))[0:4]) + _, _ = fmt.Fprint(w, " ") + _, _ = fmt.Fprint(w, color.WhiteString(r.Timestamp().Format("15:04:05"))) + _, _ = fmt.Fprint(w, " ") + + _, _ = fmt.Fprint(w, r.Body().AsString()) + + b := bytes.NewBuffer(nil) + + written := map[string]bool{} + + for attr := range r.WalkAttributes { + if written[attr.Key] { + continue + } + written[attr.Key] = true + + _, _ = fmt.Fprint(b, " ") + _, _ = fmt.Fprint(b, attr.Key) + _, _ = fmt.Fprint(b, "=") + v, err := marshal(LogValue(attr.Value)) + if err != nil { + return err + } + _, _ = fmt.Fprint(b, string(v)) + } + + if name := r.InstrumentationScope().Name; name != "" { + _, _ = fmt.Fprintf(b, " spanName=%s", name) + } + + if b.Len() > 0 { + _, _ = fmt.Fprint(w, color.WhiteString(b.String())) + } + + _, _ = fmt.Fprintln(w) + + _, err := io.Copy(f, w) + return err +} + +func marshal(v any) ([]byte, error) { + codec := gocodec.New(cuecontext.New(), nil) + val, err := codec.Decode(v) + if err != nil { + return nil, err + } + return cueformat.Node(val.Syntax(), cueformat.Simplify()) +} diff --git a/internal/otel/logger_exporter_json.go b/internal/otel/logger_exporter_json.go new file mode 100644 index 0000000..610fc61 --- /dev/null +++ b/internal/otel/logger_exporter_json.go @@ -0,0 +1,194 @@ +package otel + +import ( + "bytes" + "context" + "encoding/base64" + "github.com/go-json-experiment/json/jsontext" + "go.opentelemetry.io/otel/log" + sdklog "go.opentelemetry.io/otel/sdk/log" + "io" + "os" + "time" +) + +type jsonExporter struct { +} + +func (e *jsonExporter) Export(ctx context.Context, records []sdklog.Record) error { + for _, r := range records { + if r.Severity() >= log.SeverityWarn1 { + if err := e.print(os.Stderr, r); err != nil { + return err + } + continue + } + if err := e.print(os.Stdout, r); err != nil { + return err + } + } + + return nil +} + +func (e *jsonExporter) print(w io.Writer, r sdklog.Record) error { + b := bytes.NewBuffer(nil) + enc := jsontext.NewEncoder(b) + + if err := enc.WriteToken(jsontext.ObjectStart); err != nil { + return err + } + + if err := e.keyValueTo(enc, "time", r.Timestamp().Format(time.RFC3339)); err != nil { + return err + } + + if err := e.keyValueTo(enc, "level", severityText(r)); err != nil { + return err + } + + if err := e.keyValueTo(enc, "msg", r.Body().AsString()); err != nil { + return err + } + + written := map[string]bool{} + + for attr := range r.WalkAttributes { + if written[attr.Key] { + continue + } + written[attr.Key] = true + + if err := e.keyValueTo(enc, attr.Key, LogValue(attr.Value)); err != nil { + return err + } + } + + res := r.Resource() + for _, attr := range res.Attributes() { + switch attr.Key { + case "service.name", "service.version": + if err := e.keyValueTo(enc, string(attr.Key), attr.Value.AsInterface()); err != nil { + return err + } + } + } + + if err := e.keyValueTo(enc, "spanName", r.InstrumentationScope().Name); err != nil { + return err + } + + if err := e.keyValueTo(enc, "traceID", r.TraceID().String()); err != nil { + return err + } + + if err := e.keyValueTo(enc, "spanID", r.SpanID().String()); err != nil { + return err + } + + if err := enc.WriteToken(jsontext.ObjectEnd); err != nil { + return err + } + + _, err := io.Copy(w, b) + return err +} + +func (e *jsonExporter) keyValueTo(enc *jsontext.Encoder, key string, value any) error { + if err := writeJSONValue(enc, key); err != nil { + return err + } + return writeJSONValue(enc, value) +} + +func (e jsonExporter) Shutdown(ctx context.Context) error { + return nil +} + +func (e jsonExporter) ForceFlush(ctx context.Context) error { + return nil +} + +func writeJSONValue(enc *jsontext.Encoder, value any) error { + switch x := value.(type) { + case string: + return enc.WriteToken(jsontext.String(x)) + case int: + return enc.WriteToken(jsontext.Int(int64(x))) + case int8: + return enc.WriteToken(jsontext.Int(int64(x))) + case int16: + return enc.WriteToken(jsontext.Int(int64(x))) + case int32: + return enc.WriteToken(jsontext.Int(int64(x))) + case int64: + return enc.WriteToken(jsontext.Int(int64(x))) + case uint: + return enc.WriteToken(jsontext.Uint(uint64(x))) + case uint8: + return enc.WriteToken(jsontext.Uint(uint64(x))) + case uint16: + return enc.WriteToken(jsontext.Uint(uint64(x))) + case uint32: + return enc.WriteToken(jsontext.Uint(uint64(x))) + case uint64: + return enc.WriteToken(jsontext.Uint(uint64(x))) + case float32: + return enc.WriteToken(jsontext.Float(float64(x))) + case float64: + return enc.WriteToken(jsontext.Float(x)) + case bool: + return enc.WriteToken(jsontext.Bool(x)) + case map[string]any: + if err := enc.WriteToken(jsontext.ObjectStart); err != nil { + return err + } + for key, value := range x { + if err := writeJSONValue(enc, key); err != nil { + return err + } + if err := writeJSONValue(enc, value); err != nil { + return err + } + } + return enc.WriteToken(jsontext.ObjectEnd) + case []any: + if err := enc.WriteToken(jsontext.ArrayStart); err != nil { + return err + } + for i := range x { + if err := writeJSONValue(enc, x[i]); err != nil { + return err + } + } + return enc.WriteToken(jsontext.ArrayEnd) + case []byte: + return enc.WriteToken(jsontext.String(base64.StdEncoding.EncodeToString(x))) + } + return nil +} + +func severityText(r sdklog.Record) string { + if txt := r.SeverityText(); txt != "" { + return txt + } + + s := r.Severity() + + if s >= log.SeverityFatal { + return "fatal" + } + if s >= log.SeverityError { + return "error" + } + if s >= log.SeverityWarn { + return "warn" + } + if s >= log.SeverityInfo { + return "info" + } + if s >= log.SeverityDebug { + return "debug" + } + return "trace" +} diff --git a/internal/otel/logger_provider.go b/internal/otel/logger_provider.go new file mode 100644 index 0000000..4ac3906 --- /dev/null +++ b/internal/otel/logger_provider.go @@ -0,0 +1,25 @@ +package otel + +import ( + "context" + contextx "github.com/octohelm/x/context" + "go.opentelemetry.io/otel/log" +) + +type LoggerProvider = log.LoggerProvider + +var LoggerProviderContext = contextx.New[LoggerProvider]() + +// +gengo:enum +type LogLevel string + +const ( + ErrorLevel LogLevel = "error" + WarnLevel LogLevel = "warn" + InfoLevel LogLevel = "info" + DebugLevel LogLevel = "debug" +) + +func Logger(ctx context.Context, name string) log.Logger { + return LoggerProviderContext.From(ctx).Logger(name) +} diff --git a/internal/otel/meter.go b/internal/otel/meter.go deleted file mode 100644 index ca812b0..0000000 --- a/internal/otel/meter.go +++ /dev/null @@ -1,39 +0,0 @@ -package otel - -import ( - "context" - - "github.com/prometheus/client_golang/prometheus" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/noop" -) - -var emptyMetricProvider = noop.NewMeterProvider() - -type contextMetricProvider struct { -} - -func MeterProviderFromContext(ctx context.Context) metric.MeterProvider { - if mp, ok := ctx.Value(contextMetricProvider{}).(metric.MeterProvider); ok { - return mp - } - return emptyMetricProvider -} - -func ContextWithMeterProvider(ctx context.Context, meterProvider metric.MeterProvider) context.Context { - return context.WithValue(ctx, contextMetricProvider{}, meterProvider) -} - -type contextGatherer struct { -} - -func GathererFromContext(ctx context.Context) prometheus.Gatherer { - if mp, ok := ctx.Value(contextGatherer{}).(prometheus.Gatherer); ok { - return mp - } - return prometheus.DefaultGatherer -} - -func ContextWithGatherer(ctx context.Context, gatherer prometheus.Gatherer) context.Context { - return context.WithValue(ctx, contextGatherer{}, gatherer) -} diff --git a/internal/otel/meter_provider.go b/internal/otel/meter_provider.go new file mode 100644 index 0000000..6399a5b --- /dev/null +++ b/internal/otel/meter_provider.go @@ -0,0 +1,24 @@ +package otel + +import ( + "context" + contextx "github.com/octohelm/x/context" + "github.com/prometheus/client_golang/prometheus" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/noop" +) + +type MeterProvider = metric.MeterProvider +type Reader = sdkmetric.Reader + +var GathererContext = contextx.New[prometheus.Gatherer]() + +var MeterProviderContext = contextx.New[MeterProvider](contextx.WithDefaultsFunc(func() metric.MeterProvider { + return noop.NewMeterProvider() +})) + +func Meter(ctx context.Context) metric.Meter { + return MeterProviderContext.From(ctx).Meter("") +} diff --git a/internal/otel/source.go b/internal/otel/source.go new file mode 100644 index 0000000..c3ecc2b --- /dev/null +++ b/internal/otel/source.go @@ -0,0 +1,30 @@ +package otel + +import ( + "fmt" + "go.opentelemetry.io/otel/log" + "log/slog" + "path/filepath" + "runtime" +) + +func GetSource(skip int) Source { + pc, _, _, _ := runtime.Caller(skip + 1) + fs := runtime.CallersFrames([]uintptr{pc}) + f, _ := fs.Next() + + return Source{ + Function: f.Function, + File: f.File, + Line: f.Line, + } +} + +type Source slog.Source + +func (s Source) AsKeyValues() []log.KeyValue { + return []log.KeyValue{ + log.String("source.func", s.Function), + log.String("source.file", fmt.Sprintf("%s:%d", filepath.Base(s.File), s.Line)), + } +} diff --git a/internal/otel/span_export__slog.go b/internal/otel/span_export__slog.go deleted file mode 100644 index 47dbc54..0000000 --- a/internal/otel/span_export__slog.go +++ /dev/null @@ -1,115 +0,0 @@ -package otel - -import ( - "context" - "log/slog" - "os" - "runtime" - "sync" - - _ "time/tzdata" - - "github.com/go-courier/logr" - sdktrace "go.opentelemetry.io/otel/sdk/trace" -) - -func Source(skip int) *slog.Source { - pc, _, _, _ := runtime.Caller(skip) - fs := runtime.CallersFrames([]uintptr{pc}) - f, _ := fs.Next() - - return &slog.Source{ - Function: f.Function, - File: f.File, - Line: f.Line, - } -} - -func NewLogger() *slog.Logger { - if os.Getenv("GOENV") == "DEV" { - return slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ - Level: slog.LevelDebug, - })) - } - return slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ - Level: slog.LevelDebug, - })) -} - -func SlogSpanExporter(log *slog.Logger) sdktrace.SpanExporter { - return &stdoutSpanExporter{log: log} -} - -type stdoutSpanExporter struct { - log *slog.Logger - wg sync.WaitGroup -} - -func (e *stdoutSpanExporter) Shutdown(ctx context.Context) error { - return nil -} - -func (e *stdoutSpanExporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error { - for i := range spans { - span := spans[i] - - for _, event := range span.Events() { - attrs := make([]slog.Attr, 0, len(event.Attributes)+4) - - level := logr.DebugLevel - - for _, kv := range event.Attributes { - k := string(kv.Key) - - switch k { - case "@level": - lvl, err := logr.ParseLevel(kv.Value.AsString()) - if err != nil { - continue - } - level = lvl - default: - attrs = append(attrs, slog.Any(k, kv.Value.AsInterface())) - } - } - - spanName := span.Name() - - if spanName != "" { - attrs = append(attrs, slog.String("traceID", span.SpanContext().TraceID().String())) - - if span.SpanContext().HasSpanID() { - attrs = append( - attrs, - slog.String("spanName", spanName), - slog.String("spanID", span.SpanContext().SpanID().String()), - slog.String("spanCost", span.EndTime().Sub(span.StartTime()).String()), - ) - } - - if span.Parent().IsValid() { - attrs = append(attrs, slog.String("parentSpanID", span.Parent().SpanID().String())) - } - } - - msg := event.Name - lvl := slog.LevelDebug - - switch level { - case logr.DebugLevel: - lvl = slog.LevelDebug - case logr.InfoLevel: - lvl = slog.LevelInfo - case logr.WarnLevel: - lvl = slog.LevelWarn - case logr.ErrorLevel: - lvl = slog.LevelError - } - - r := slog.NewRecord(event.Time, lvl, msg, 0) - r.AddAttrs(attrs...) - _ = e.log.Handler().Handle(ctx, r) - } - } - return nil -} diff --git a/internal/otel/span_filter.go b/internal/otel/span_filter.go deleted file mode 100644 index 82c0d5e..0000000 --- a/internal/otel/span_filter.go +++ /dev/null @@ -1,71 +0,0 @@ -package otel - -import ( - "context" - - "go.opentelemetry.io/otel/codes" - sdktrace "go.opentelemetry.io/otel/sdk/trace" -) - -// +gengo:enum -type OutputFilterType string - -const ( - OutputFilterAlways OutputFilterType = "Always" - OutputFilterOnFailure OutputFilterType = "OnFailure" - OutputFilterNever OutputFilterType = "Never" -) - -func OutputFilter(outputType OutputFilterType) SpanMapper { - return func(span sdktrace.ReadOnlySpan) sdktrace.ReadOnlySpan { - switch outputType { - case OutputFilterOnFailure: - if span.Status().Code == codes.Ok { - return nil - } - case OutputFilterNever: - return nil - } - return span - } -} - -type SpanMapper = func(data sdktrace.ReadOnlySpan) sdktrace.ReadOnlySpan - -func WithSpanMapExporter(mappers ...SpanMapper) func(spanExporter sdktrace.SpanExporter) sdktrace.SpanExporter { - return func(spanExporter sdktrace.SpanExporter) sdktrace.SpanExporter { - return &spanMapExporter{ - mappers: mappers, - SpanExporter: spanExporter, - } - } -} - -type spanMapExporter struct { - mappers []SpanMapper - sdktrace.SpanExporter -} - -func (e *spanMapExporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error { - finalSpanSnapshot := make([]sdktrace.ReadOnlySpan, 0, len(spans)) - - mappers := e.mappers - - for i := range spans { - span := spans[i] - - for _, m := range mappers { - span = m(span) - } - - if span != nil { - finalSpanSnapshot = append(finalSpanSnapshot, span) - } - } - - if len(finalSpanSnapshot) == 0 { - return nil - } - - return e.SpanExporter.ExportSpans(ctx, finalSpanSnapshot) -} diff --git a/internal/otel/tracer_provider.go b/internal/otel/tracer_provider.go index 1f45065..8723267 100644 --- a/internal/otel/tracer_provider.go +++ b/internal/otel/tracer_provider.go @@ -2,22 +2,14 @@ package otel import ( "context" - - "go.opentelemetry.io/otel" - contextx "github.com/octohelm/x/context" "go.opentelemetry.io/otel/trace" ) -type tpCtx struct{} +var TracerProviderContext = contextx.New[TracerProvider]() -func ContextWithTracerProvider(ctx context.Context, tp trace.TracerProvider) context.Context { - return contextx.WithValue(ctx, tpCtx{}, tp) -} +type TracerProvider = trace.TracerProvider -func TracerProviderFromContext(ctx context.Context) trace.TracerProvider { - if tp, ok := ctx.Value(tpCtx{}).(trace.TracerProvider); ok { - return tp - } - return otel.GetTracerProvider() +func Tracer(ctx context.Context) trace.Tracer { + return TracerProviderContext.From(ctx).Tracer("") } diff --git a/internal/otel/span_exporter__err_ignore.go b/internal/otel/tracer_span_exporter__err_ignore.go similarity index 57% rename from internal/otel/span_exporter__err_ignore.go rename to internal/otel/tracer_span_exporter__err_ignore.go index 36b9c1d..fc177dc 100644 --- a/internal/otel/span_exporter__err_ignore.go +++ b/internal/otel/tracer_span_exporter__err_ignore.go @@ -6,11 +6,9 @@ import ( sdktrace "go.opentelemetry.io/otel/sdk/trace" ) -func WithErrIgnoreExporter() func(spanExporter sdktrace.SpanExporter) sdktrace.SpanExporter { - return func(spanExporter sdktrace.SpanExporter) sdktrace.SpanExporter { - return &errIgnoreExporter{ - SpanExporter: spanExporter, - } +func IgnoreErrSpanExporter(spanExporter sdktrace.SpanExporter) sdktrace.SpanExporter { + return &errIgnoreExporter{ + SpanExporter: spanExporter, } } diff --git a/internal/otel/util.go b/internal/otel/util.go new file mode 100644 index 0000000..52a0fe6 --- /dev/null +++ b/internal/otel/util.go @@ -0,0 +1,127 @@ +package otel + +import ( + "fmt" + "log/slog" + "time" + + "github.com/octohelm/x/slices" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/log" +) + +func normalizeKeyValues(keysAndValues []any) []log.KeyValue { + keyValues := make([]log.KeyValue, 0, len(keysAndValues)) + + for i := 0; i < len(keysAndValues); i++ { + switch x := keysAndValues[i].(type) { + case []slog.Attr: + slog.GroupValue() + + keyValues = append(keyValues, slices.Map(x, func(e slog.Attr) log.KeyValue { + return log.KeyValue{ + Key: e.Key, + Value: LogAnyValue(e.Value.Any()), + } + })...) + case slog.Attr: + keyValues = append(keyValues, log.KeyValue{ + Key: x.Key, + Value: LogAnyValue(x.Value.Any()), + }) + case []attribute.KeyValue: + keyValues = append(keyValues, slices.Map(x, func(e attribute.KeyValue) log.KeyValue { + return log.KeyValue{ + Key: string(e.Key), + Value: LogAnyValue(e.Value.AsInterface()), + } + })...) + case attribute.KeyValue: + keyValues = append(keyValues, log.KeyValue{ + Key: string(x.Key), + Value: LogAnyValue(x.Value.AsInterface()), + }) + case string: + // "key", value + if i+1 < len(keysAndValues) { + i++ + keyValues = append(keyValues, log.KeyValue{ + Key: x, + Value: LogAnyValue(keysAndValues[i]), + }) + } + default: + panic(fmt.Errorf("unsupported log attr values %T", x)) + } + } + + return keyValues +} + +func LogValue(v log.Value) any { + switch v.Kind() { + case log.KindBool: + return v.AsBool() + case log.KindFloat64: + return v.AsFloat64() + case log.KindInt64: + return v.AsInt64() + case log.KindString: + return v.AsString() + case log.KindBytes: + return v.AsBytes() + case log.KindSlice: + list := v.AsSlice() + values := make([]any, len(list)) + for i := range list { + values[i] = LogValue(list[i]) + } + return values + case log.KindMap: + values := map[string]any{} + for _, k := range v.AsMap() { + values[k.Key] = LogValue(k.Value) + } + return values + default: + return nil + } +} + +func LogAnyValue(value any) log.Value { + switch v := value.(type) { + case time.Time: + return log.StringValue(slog.TimeValue(v).String()) + case time.Duration: + return log.StringValue(slog.DurationValue(v).String()) + case fmt.Stringer: + return log.StringValue(v.String()) + case []byte: + return log.BytesValue(v) + case string: + return log.StringValue(v) + case int: + return log.IntValue(v) + case float64: + return log.Float64Value(v) + case bool: + return log.BoolValue(v) + case []any: + list := make([]log.Value, len(v)) + for i, item := range v { + list[i] = LogAnyValue(item) + } + return log.SliceValue(list...) + case map[string]any: + keyValues := make([]log.KeyValue, 0, len(v)) + for k, item := range v { + keyValues = append(keyValues, log.KeyValue{ + Key: k, + Value: LogAnyValue(item), + }) + } + return log.MapValue(keyValues...) + default: + return log.StringValue(slog.AnyValue(v).String()) + } +} diff --git a/internal/otel/zz_generated.enum.go b/internal/otel/zz_generated.enum.go index b363a0a..13c89af 100644 --- a/internal/otel/zz_generated.enum.go +++ b/internal/otel/zz_generated.enum.go @@ -1,11 +1,48 @@ /* -Package otel GENERATED BY gengo:enum +Package otel GENERATED BY gengo:enum DON'T EDIT THIS FILE */ package otel -func (OutputFilterType) EnumValues() []any { +import ( + github_com_pkg_errors "github.com/pkg/errors" +) + +var InvalidLogLevel = github_com_pkg_errors.New("invalid LogLevel") + +func (LogLevel) EnumValues() []any { return []any{ - OutputFilterAlways, OutputFilterNever, OutputFilterOnFailure, + DebugLevel, ErrorLevel, InfoLevel, WarnLevel, + } +} +func ParseLogLevelLabelString(label string) (LogLevel, error) { + switch label { + case "debug": + return DebugLevel, nil + case "error": + return ErrorLevel, nil + case "info": + return InfoLevel, nil + case "warn": + return WarnLevel, nil + + default: + return "", InvalidLogLevel + } +} + +func (v LogLevel) Label() string { + switch v { + case DebugLevel: + return "debug" + case ErrorLevel: + return "error" + case InfoLevel: + return "info" + case WarnLevel: + return "warn" + + default: + return "UNKNOWN" } } diff --git a/internal/otel/zz_generated.runtimedoc.go b/internal/otel/zz_generated.runtimedoc.go index 36b1cf5..10e57d2 100644 --- a/internal/otel/zz_generated.runtimedoc.go +++ b/internal/otel/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package otel GENERATED BY gengo:runtimedoc +Package otel GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package otel @@ -14,6 +14,29 @@ func runtimeDoc(v any, names ...string) ([]string, bool) { return nil, false } -func (OutputFilterType) RuntimeDoc(names ...string) ([]string, bool) { +func (LogLevel) RuntimeDoc(names ...string) ([]string, bool) { + return []string{}, true +} +func (v Source) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Function": + return []string{ + "Function is the package path-qualified function name containing the", + "source line. If non-empty, this string uniquely identifies a single", + "function in the program. This may be the empty string if not known.", + }, true + case "File": + return []string{ + "File and Line are the file name and line number (1-based) of the source", + "line. These may be the empty string and zero, respectively, if not known.", + }, true + case "Line": + return []string{}, true + + } + + return nil, false + } return []string{}, true } diff --git a/pkg/cli/execute.go b/pkg/cli/execute.go index fe50e08..ba5697a 100644 --- a/pkg/cli/execute.go +++ b/pkg/cli/execute.go @@ -15,11 +15,10 @@ func Exec(ctx context.Context, app Command) { } func Execute(ctx context.Context, c Command, args []string) error { - if inputs, ok := c.(interface { - ParseArgs(args []string) - }); ok { + if inputs, ok := c.(interface{ ParseArgs(args []string) }); ok { inputs.ParseArgs(args) } + if e, ok := c.(interface { ExecuteContext(ctx context.Context) error }); ok { diff --git a/pkg/cli/zz_generated.runtimedoc.go b/pkg/cli/zz_generated.runtimedoc.go index 6b16ece..7d11b8a 100644 --- a/pkg/cli/zz_generated.runtimedoc.go +++ b/pkg/cli/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package cli GENERATED BY gengo:runtimedoc +Package cli GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package cli diff --git a/pkg/configuration/lifecycle.go b/pkg/configuration/lifecycle.go index 4a72566..37cdee9 100644 --- a/pkg/configuration/lifecycle.go +++ b/pkg/configuration/lifecycle.go @@ -2,23 +2,41 @@ package configuration import ( "context" + "fmt" + "golang.org/x/sync/errgroup" + "log/slog" "os" "os/signal" "syscall" "time" - - "github.com/go-courier/logr" - "golang.org/x/sync/errgroup" ) +var log *slog.Logger + +func init() { + opt := &slog.HandlerOptions{ + Level: slog.LevelError, + } + + if os.Getenv("INFRA_CLI_DEBUG") == "1" { + opt.Level = slog.LevelDebug + } + + log = slog.New(slog.NewTextHandler(os.Stdout, opt)) +} + func RunOrServe(ctx context.Context, configurators ...any) error { configuratorRunners := make([]Runner, 0, len(configurators)) configuratorServers := make([]Server, 0, len(configurators)) + configuratorCanShutdowns := make([]CanShutdown, 0, len(configurators)) for i := range configurators { if x, ok := configurators[i].(Runner); ok { configuratorRunners = append(configuratorRunners, x) } + if x, ok := configurators[i].(CanShutdown); ok { + configuratorCanShutdowns = append(configuratorCanShutdowns, x) + } if x, ok := configurators[i].(Server); ok { configuratorServers = append(configuratorServers, x) } @@ -28,51 +46,61 @@ func RunOrServe(ctx context.Context, configurators ...any) error { cc := ci.InjectContext(ctx) - g, c := errgroup.WithContext(cc) - if err := run(cc, configuratorRunners...); err != nil { return err } - // Shutdown for cleanup when no server - if len(configuratorServers) == 0 { - return Shutdown(cc, configurators...) - } + if len(configuratorServers) > 0 { + stopCh := make(chan os.Signal, 1) - stopCh := make(chan os.Signal, 1) + g, c := errgroup.WithContext(cc) - g.Go(func() error { - signal.Notify(stopCh, - os.Interrupt, os.Kill, - syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, - syscall.SIGILL, syscall.SIGABRT, syscall.SIGFPE, syscall.SIGSEGV, - ) - <-stopCh + g.Go(func() error { + return serve(c, stopCh, configuratorServers...) + }) - timeout := 10 * time.Second + g.Go(func() error { + signal.Notify(stopCh, + os.Interrupt, os.Kill, + syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, + syscall.SIGILL, syscall.SIGABRT, syscall.SIGFPE, syscall.SIGSEGV, + ) - if len(configuratorServers) > 0 { - logr.FromContext(c).Info("shutdowning server in %s", timeout) - } + <-stopCh - cc, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() + timeout := 10 * time.Second - return Shutdown(cc, configurators...) - }) + cc, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() - g.Go(func() error { - return serve(c, stopCh, configuratorServers...) - }) + return Shutdown(cc, configuratorCanShutdowns...) + }) - return g.Wait() + return g.Wait() + } + + if len(configuratorCanShutdowns) > 0 { + // shutdown as cleanup + return Shutdown(cc, configuratorCanShutdowns...) + } + + return nil } func run(ctx context.Context, configuratorRunners ...Runner) error { for i := range configuratorRunners { + l := log.With( + slog.String("type", fmt.Sprintf("%T", configuratorRunners[i])), + slog.String("lifecycle", "Run"), + ) + + l.Debug("staring") + if err := configuratorRunners[i].Run(ctx); err != nil { return err } + + l.Debug("done") } return nil } @@ -90,6 +118,11 @@ func serve(ctx context.Context, stopCh chan os.Signal, configuratorServers ...Se } g.Go(func() error { + log.With( + slog.String("type", fmt.Sprintf("%T", server)), + slog.String("lifecycle", "Serve"), + ).Debug("serving") + err := server.Serve(c) go func() { stopCh <- syscall.SIGTERM @@ -101,15 +134,18 @@ func serve(ctx context.Context, stopCh chan os.Signal, configuratorServers ...Se return g.Wait() } -func Shutdown(ctx context.Context, configuratorServers ...any) error { +func Shutdown(ctx context.Context, configuratorServers ...CanShutdown) error { g, c := errgroup.WithContext(ctx) - for i := range configuratorServers { - if canShutdown, ok := configuratorServers[i].(CanShutdown); ok { - g.Go(func() error { - return canShutdown.Shutdown(c) - }) - } + for _, canShutdown := range configuratorServers { + g.Go(func() error { + log.With( + slog.String("type", fmt.Sprintf("%T", canShutdown)), + slog.String("lifecycle", "Shutdown"), + ).Debug("shutting down") + + return canShutdown.Shutdown(c) + }) } return g.Wait() @@ -121,6 +157,8 @@ func Init(ctx context.Context, configurators ...any) error { for i := range configurators { configurator := configurators[i] + log.With(slog.String("type", fmt.Sprintf("%T", configurator))).Debug("init") + if c, ok := configurator.(Defaulter); ok { c.SetDefaults() } diff --git a/pkg/configuration/zz_generated.runtimedoc.go b/pkg/configuration/zz_generated.runtimedoc.go index fb887bc..28fb79c 100644 --- a/pkg/configuration/zz_generated.runtimedoc.go +++ b/pkg/configuration/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package configuration GENERATED BY gengo:runtimedoc +Package configuration GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package configuration diff --git a/pkg/http/middleware/otel_client.go b/pkg/http/middleware/otel_client.go index 05d6814..febfabf 100644 --- a/pkg/http/middleware/otel_client.go +++ b/pkg/http/middleware/otel_client.go @@ -1,17 +1,16 @@ package middleware import ( - "fmt" + "log/slog" "net/http" "strconv" "time" - "github.com/innoai-tech/infra/pkg/http/middleware/metrichttp" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" - semconv "go.opentelemetry.io/otel/semconv/v1.20.0" "github.com/go-courier/logr" + "github.com/innoai-tech/infra/pkg/http/middleware/metrichttp" "github.com/pkg/errors" "go.opentelemetry.io/contrib/propagators/b3" "go.opentelemetry.io/otel/propagation" @@ -45,19 +44,19 @@ func (rt *LogRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) cost := time.Since(startedAt) l := log.WithValues( - semconv.HTTPMethod(req.Method), - semconv.HTTPURL(omitAuthorization(req.URL)), - attribute.Key("http.client.duration").String(fmt.Sprintf("%s", cost)), + slog.String("http.method", req.Method), + slog.String("http.url", omitAuthorization(req.URL)), + slog.String("http.client.duration", cost.String()), ) if resp != nil { p, _ := strconv.ParseInt(req.URL.Port(), 10, 64) attrs := []attribute.KeyValue{ - attribute.Key("http.request.method").String(req.Method), - attribute.Key("http.response.status_code").Int(resp.StatusCode), - attribute.Key("server.address").String(req.URL.Hostname()), - attribute.Key("server.port").Int(int(p)), + attribute.String("http.request.method", req.Method), + attribute.Int("http.response.status_code", resp.StatusCode), + attribute.String("server.address", req.URL.Hostname()), + attribute.Int("server.port", int(p)), } metrichttp.ClientDuration.Record(ctx, cost.Seconds(), metric.WithAttributes(attrs...)) @@ -65,14 +64,15 @@ func (rt *LogRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) metrichttp.ClientResponseSize.Record(ctx, resp.ContentLength, metric.WithAttributes(attrs...)) l = l.WithValues( - semconv.HTTPStatusCode(resp.StatusCode), + slog.String("http.method", req.Method), + slog.Int("http.status_code", resp.StatusCode), ) } if req.ContentLength > 0 { l = l.WithValues( - "http.content-type", req.Header.Get("Content-Type"), - semconv.HTTPResponseContentLength(int(req.ContentLength)), + slog.String("http.content-type", req.Header.Get("Content-Type")), + slog.Int("http.response_content_length", int(req.ContentLength)), ) } diff --git a/pkg/http/middleware/otel_server.go b/pkg/http/middleware/otel_server.go index 70745ed..7abe04f 100644 --- a/pkg/http/middleware/otel_server.go +++ b/pkg/http/middleware/otel_server.go @@ -2,6 +2,8 @@ package middleware import ( "fmt" + "github.com/prometheus/client_golang/prometheus" + "log/slog" "net/http" "net/url" "strconv" @@ -13,13 +15,11 @@ import ( "github.com/octohelm/courier/pkg/courierhttp" "github.com/octohelm/courier/pkg/courierhttp/util" "github.com/pkg/errors" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "go.opentelemetry.io/contrib/propagators/b3" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/propagation" - semconv "go.opentelemetry.io/otel/semconv/v1.20.0" ) func MetricHandler(gatherer prometheus.Gatherer) func(handler http.Handler) http.Handler { @@ -75,6 +75,7 @@ func LogAndMetricHandler() func(handler http.Handler) http.Handler { startAt := time.Now() info := courierhttp.OperationInfoFromContext(ctx) + ctx, span := logr.FromContext(ctx).Start(ctx, info.ID) defer func() { span.End() @@ -104,28 +105,26 @@ func LogAndMetricHandler() func(handler http.Handler) http.Handler { requestCost := time.Since(startAt) requestHeader := req.Header - keyAndValues := []any{ - semconv.HTTPClientIP(util.ClientIP(req)), - semconv.HTTPMethod(req.Method), - semconv.HTTPURL(omitAuthorization(req.URL)), - semconv.HTTPStatusCode(loggerRw.statusCode), - semconv.UserAgentOriginal(requestHeader.Get("User-Agent")), - "http.server.duration", fmt.Sprintf("%s", requestCost), - } - - l := logr.FromContext(ctx) + l := logr.FromContext(ctx).WithValues( + slog.String("http.client_ip", util.ClientIP(req)), + slog.String("http.method", req.Method), + slog.String("http.url", omitAuthorization(req.URL)), + slog.Int("http.status_code", loggerRw.statusCode), + slog.String("user_agent.original", requestHeader.Get("User-Agent")), + slog.String("http.server.duration", fmt.Sprintf("%s", requestCost)), + ) if loggerRw.err != nil { if loggerRw.statusCode >= http.StatusInternalServerError { - l.WithValues(keyAndValues...).Error(loggerRw.err) + l.Error(loggerRw.err) } else { if isLevelEnabled(logr.WarnLevel)(enabledLevel) { - l.WithValues(keyAndValues...).Warn(loggerRw.err) + l.Warn(loggerRw.err) } } } else { if isLevelEnabled(logr.InfoLevel)(enabledLevel) { - l.WithValues(keyAndValues...).Info("success") + l.Info("success") } } diff --git a/pkg/http/middleware/zz_generated.runtimedoc.go b/pkg/http/middleware/zz_generated.runtimedoc.go index 7ccf618..abef162 100644 --- a/pkg/http/middleware/zz_generated.runtimedoc.go +++ b/pkg/http/middleware/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package middleware GENERATED BY gengo:runtimedoc +Package middleware GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package middleware @@ -19,7 +19,6 @@ func (CORSOption) RuntimeDoc(names ...string) ([]string, bool) { "CORSOption represents a functional option for configuring the CORS middleware.", }, true } - func (OriginValidator) RuntimeDoc(names ...string) ([]string, bool) { return []string{ "OriginValidator takes an origin string and returns whether or not that origin is allowed.", diff --git a/pkg/http/server.go b/pkg/http/server.go index 303077e..20fdd9b 100644 --- a/pkg/http/server.go +++ b/pkg/http/server.go @@ -79,6 +79,7 @@ func (s *Server) Init(ctx context.Context) error { s.routerHandlers..., )..., ) + if err != nil { return err } @@ -86,7 +87,7 @@ func (s *Server) Init(ctx context.Context) error { globalHandlers := append([]handler.HandlerMiddleware{ middleware.CompressLevelHandlerMiddleware(gzip.DefaultCompression), middleware.DefaultCORS(s.corsOptions...), - middleware.MetricHandler(otel.GathererFromContext(ctx)), + middleware.MetricHandler(otel.GathererContext.From(ctx)), middleware.PProfHandler(s.EnableDebug), }, s.globalHandlers...) diff --git a/pkg/http/webapp/appconfig/zz_generated.runtimedoc.go b/pkg/http/webapp/appconfig/zz_generated.runtimedoc.go index 1afe0ed..8ce6dab 100644 --- a/pkg/http/webapp/appconfig/zz_generated.runtimedoc.go +++ b/pkg/http/webapp/appconfig/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package appconfig GENERATED BY gengo:runtimedoc +Package appconfig GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package appconfig diff --git a/pkg/http/webapp/zz_generated.runtimedoc.go b/pkg/http/webapp/zz_generated.runtimedoc.go index c5107a0..1183c57 100644 --- a/pkg/http/webapp/zz_generated.runtimedoc.go +++ b/pkg/http/webapp/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package webapp GENERATED BY gengo:runtimedoc +Package webapp GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package webapp diff --git a/pkg/http/zz_generated.runtimedoc.go b/pkg/http/zz_generated.runtimedoc.go index 2b3d6b7..2e42724 100644 --- a/pkg/http/zz_generated.runtimedoc.go +++ b/pkg/http/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package http GENERATED BY gengo:runtimedoc +Package http GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package http diff --git a/pkg/otel/log.go b/pkg/otel/log.go deleted file mode 100644 index 8a62d1b..0000000 --- a/pkg/otel/log.go +++ /dev/null @@ -1,134 +0,0 @@ -package otel - -import ( - "context" - "log/slog" - "time" - - "github.com/go-courier/logr" - "go.opentelemetry.io/otel/exporters/otlp/otlptrace" - "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" - - "go.opentelemetry.io/otel/sdk/resource" - sdktrace "go.opentelemetry.io/otel/sdk/trace" - semconv "go.opentelemetry.io/otel/semconv/v1.20.0" - "go.opentelemetry.io/otel/trace" - - "github.com/innoai-tech/infra/internal/otel" - "github.com/innoai-tech/infra/pkg/cli" - "github.com/innoai-tech/infra/pkg/configuration" -) - -type OtelWithBatchLog struct { - Otel -} - -func (o *OtelWithBatchLog) SetDefaults() { - o.batchLog = true - - o.Otel.SetDefaults() -} - -type Otel struct { - // Log level - LogLevel LogLevel `flag:",omitempty"` - // Log filter - LogFilter OutputFilterType `flag:",omitempty"` - // When set, will collect traces - TraceCollectorEndpoint string `flag:",omitempty"` - - batchLog bool - tp *sdktrace.TracerProvider - directLogger *slog.Logger - enabledLogLevel logr.Level -} - -func (o *Otel) SetDefaults() { - if o.LogLevel == "" { - o.LogLevel = InfoLevel - } - if o.LogFilter == "" { - o.LogFilter = OutputFilterAlways - } - if o.TraceCollectorEndpoint != "" { - o.batchLog = true - } -} - -func (o *Otel) Init(ctx context.Context) error { - if !o.batchLog { - o.directLogger = otel.NewLogger() - } - - if o.tp == nil { - opts := []sdktrace.TracerProviderOption{ - sdktrace.WithSampler(sdktrace.AlwaysSample()), - } - - if o.TraceCollectorEndpoint != "" { - client := otlptracegrpc.NewClient( - otlptracegrpc.WithEndpoint(o.TraceCollectorEndpoint), - otlptracegrpc.WithInsecure(), - otlptracegrpc.WithTimeout(3*time.Second), - ) - - exporter, err := otlptrace.New(ctx, client) - if err != nil { - return err - } - - opts = append(opts, sdktrace.WithBatcher( - otel.WithSpanMapExporter(otel.OutputFilter(o.LogFilter))( - otel.WithErrIgnoreExporter()(exporter), - ), - )) - } - - if o.directLogger == nil { - opts = append(opts, sdktrace.WithBatcher( - otel.WithSpanMapExporter(otel.OutputFilter(o.LogFilter))( - otel.SlogSpanExporter(otel.NewLogger()), - ), - )) - } - - if info := cli.InfoFromContext(ctx); info != nil { - opts = append( - opts, - sdktrace.WithResource( - resource.NewSchemaless( - semconv.ServiceName(info.App.Name), - semconv.ServiceVersion(info.App.Version), - ), - ), - ) - } - - o.enabledLogLevel, _ = logr.ParseLevel(string(o.LogLevel)) - o.tp = sdktrace.NewTracerProvider(opts...) - } - - return nil -} - -func (o *Otel) Shutdown(ctx context.Context) error { - if o.tp == nil { - return nil - } - _ = o.tp.ForceFlush(ctx) - return o.tp.Shutdown(ctx) -} - -func (o *Otel) InjectContext(ctx context.Context) context.Context { - log := newLogger(ctx, o.tp, o.directLogger, o.enabledLogLevel) - - if info := cli.InfoFromContext(ctx); info != nil { - log = log.WithValues("app", info.App) - } - - return configuration.InjectContext( - ctx, - configuration.InjectContextFunc(otel.ContextWithTracerProvider, trace.TracerProvider(o.tp)), - configuration.InjectContextFunc(logr.WithLogger, log), - ) -} diff --git a/pkg/otel/logger.go b/pkg/otel/logger.go deleted file mode 100644 index 721fbf9..0000000 --- a/pkg/otel/logger.go +++ /dev/null @@ -1,286 +0,0 @@ -package otel - -import ( - "context" - "fmt" - "log/slog" - "time" - - "github.com/go-courier/logr" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - - "github.com/innoai-tech/infra/internal/otel" -) - -func newLogger( - ctx context.Context, - tp trace.TracerProvider, - directLogger *slog.Logger, - levelEnabled logr.Level, -) logr.Logger { - return &logger{ - enabled: levelEnabled, - tp: tp, - directLogger: directLogger, - spanContext: &spanContext{span: trace.SpanFromContext(ctx)}, - } -} - -type logger struct { - enabled logr.Level - tp trace.TracerProvider - directLogger *slog.Logger - spanContext *spanContext - attributes []attribute.KeyValue -} - -type spanContext struct { - span trace.Span - name string - startedAt time.Time -} - -func (c *spanContext) toAttrs(attributes []attribute.KeyValue) []slog.Attr { - attrs := make([]slog.Attr, len(attributes)) - for i := range attrs { - a := attributes[i] - attrs[i] = slog.Any(string(a.Key), a.Value.AsInterface()) - } - - spanCtx := c.span.SpanContext() - if traceID := spanCtx.TraceID(); traceID.IsValid() { - attrs = append(attrs, slog.String("traceID", traceID.String())) - } - - if spanCtx.HasSpanID() { - attrs = append( - attrs, - slog.String("spanID", spanCtx.SpanID().String()), - ) - } - - if c.name != "" { - attrs = append(attrs, slog.String("spanName", c.name)) - } - - return attrs -} - -func (c spanContext) withName(name string) *spanContext { - c.name = name - return &c -} - -func (c spanContext) start(ctx context.Context, tp trace.TracerProvider, spanName string, keyAndValues ...any) (context.Context, []attribute.KeyValue, *spanContext) { - c.startedAt = time.Now() - c.name = appendName(c.name, spanName) - - n, attrs := attrsFromKeyAndValues(c.name, keyAndValues...) - c.name = n - - cc, span := tp.Tracer("").Start(ctx, c.name, trace.WithTimestamp(c.startedAt)) - c.span = span - return cc, attrs, &c -} - -func (t *logger) WithValues(keyAndValues ...any) logr.Logger { - if len(keyAndValues) == 0 { - return t - } - - name, attrs := attrsFromKeyAndValues(t.spanContext.name, keyAndValues...) - - return &logger{ - enabled: t.enabled, - tp: t.tp, - directLogger: t.directLogger, - spanContext: t.spanContext.withName(name), - attributes: append(t.attributes, attrs...), - } -} - -func (t *logger) Start(ctx context.Context, name string, keyAndValues ...any) (context.Context, logr.Logger) { - c, attrs, spanCtx := t.spanContext.start(ctx, t.tp, name, keyAndValues...) - - lgr := &logger{ - enabled: t.enabled, - tp: t.tp, - spanContext: spanCtx, - directLogger: t.directLogger, - attributes: append(t.attributes, attrs...), - } - - return logr.WithLogger(c, lgr), lgr -} - -func appendName(name string, name2 string) string { - if name == "" { - return name2 - } - return name + "/" + name2 -} - -func (t *logger) End() { - endAt := time.Now() - - t.spanContext.span.End(trace.WithTimestamp(endAt)) - - if l := t.directLogger; l != nil { - attrs := t.spanContext.toAttrs(t.attributes) - attrs = append(attrs, slog.String("spanCost", endAt.Sub(t.spanContext.startedAt).String())) - - if t.enabled >= logr.DebugLevel { - attrs = append(attrs, slog.Any("source", otel.Source(3))) - } - - l.LogAttrs(context.Background(), slog.LevelDebug, "done", attrs...) - } -} - -func (t *logger) span() trace.Span { - return t.spanContext.span -} - -func (t *logger) info(level logr.Level, msg fmt.Stringer) { - if level > t.enabled { - return - } - - msgStr := msg.String() - - t.span().AddEvent( - msgStr, - trace.WithTimestamp(time.Now()), - trace.WithAttributes(append(t.attributes, attribute.String("@level", level.String()))...), - ) - - if l := t.directLogger; l != nil { - attrs := t.spanContext.toAttrs(t.attributes) - - if t.enabled >= logr.DebugLevel { - attrs = append(attrs, slog.Any("source", otel.Source(3))) - } - - switch level { - case logr.DebugLevel: - l.LogAttrs(context.Background(), slog.LevelDebug, msgStr, attrs...) - case logr.InfoLevel: - l.LogAttrs(context.Background(), slog.LevelInfo, msgStr, attrs...) - default: - } - } -} - -func (t *logger) error(level logr.Level, err error) { - if level > t.enabled { - return - } - - if err == nil { - return - } - - attributes := t.attributes - - t.span().RecordError( - err, - trace.WithTimestamp(time.Now()), - trace.WithAttributes(append(t.attributes, attribute.String("@level", level.String()))...), - ) - - if l := t.directLogger; l != nil { - attrs := t.spanContext.toAttrs(attributes) - - if t.enabled >= logr.DebugLevel { - attrs = append(attrs, slog.Any("source", otel.Source(3))) - } - - switch level { - case logr.WarnLevel: - l.LogAttrs(context.Background(), slog.LevelWarn, err.Error(), attrs...) - case logr.ErrorLevel: - l.LogAttrs(context.Background(), slog.LevelError, err.Error(), attrs...) - default: - - } - } -} - -func (t *logger) Debug(msgOrFormat string, args ...any) { - t.info(logr.DebugLevel, Sprintf(msgOrFormat, args...)) -} - -func (t *logger) Info(msgOrFormat string, args ...any) { - t.info(logr.InfoLevel, Sprintf(msgOrFormat, args...)) -} - -func (t *logger) Warn(err error) { - t.error(logr.WarnLevel, err) -} - -func (t *logger) Error(err error) { - t.error(logr.ErrorLevel, err) -} - -func attrsFromKeyAndValues(name string, keysAndValues ...any) (string, []attribute.KeyValue) { - fields := make([]attribute.KeyValue, 0, len(keysAndValues)) - - i := 0 - for i < len(keysAndValues) { - k := keysAndValues[i] - - switch key := k.(type) { - case []attribute.KeyValue: - fields = append(fields, key...) - i++ - case attribute.KeyValue: - fields = append(fields, key) - i++ - case string: - if i+1 < len(keysAndValues) { - i++ - v := keysAndValues[i] - i++ - - if key == "@name" { - name = appendName(name, v.(string)) - continue - } - - switch x := v.(type) { - case fmt.Stringer: - fields = append(fields, attribute.Stringer(key, x)) - case string: - fields = append(fields, attribute.String(key, x)) - case int: - fields = append(fields, attribute.Int(key, x)) - case float64: - fields = append(fields, attribute.Float64(key, x)) - case bool: - fields = append(fields, attribute.Bool(key, x)) - default: - fields = append(fields, attribute.String(key, fmt.Sprintf("%v", x))) - } - } - } - } - - return name, fields -} - -func Sprintf(format string, args ...any) fmt.Stringer { - return &printer{format: format, args: args} -} - -type printer struct { - format string - args []any -} - -func (p *printer) String() string { - if len(p.args) == 0 { - return p.format - } - return fmt.Sprintf(p.format, p.args...) -} diff --git a/pkg/otel/metric.go b/pkg/otel/metric.go deleted file mode 100644 index dc7d4bb..0000000 --- a/pkg/otel/metric.go +++ /dev/null @@ -1,139 +0,0 @@ -package otel - -import ( - "context" - "time" - - "github.com/innoai-tech/infra/internal/otel" - "github.com/innoai-tech/infra/pkg/cli" - "github.com/innoai-tech/infra/pkg/configuration" - "github.com/innoai-tech/infra/pkg/otel/metric" - "github.com/innoai-tech/infra/pkg/otel/metric/aggregation" - "github.com/octohelm/x/ptr" - prometheusclient "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/collectors" - "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" - "go.opentelemetry.io/otel/exporters/prometheus" - otelmetric "go.opentelemetry.io/otel/metric" - sdkmetric "go.opentelemetry.io/otel/sdk/metric" - sdkresource "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.20.0" -) - -type Metric struct { - EnableSimpleAggregation *bool `flag:",omitempty"` - - CollectorEndpoint string `flag:",omitempty"` - CollectIntervalSeconds int `flag:",omitempty"` - - gather prometheusclient.Gatherer - registry metric.Registry - mp *sdkmetric.MeterProvider -} - -func (o *Metric) SetDefaults() { - if o.EnableSimpleAggregation == nil { - o.EnableSimpleAggregation = ptr.Ptr(false) - } - - if o.CollectorEndpoint != "" { - if o.CollectIntervalSeconds == 0 { - o.CollectIntervalSeconds = 60 - } - } -} - -func (o *Metric) Init(ctx context.Context) error { - if o.registry == nil { - pr := prometheusclient.NewRegistry() - - if err := pr.Register(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})); err != nil { - return err - } - if err := pr.Register(collectors.NewGoCollector()); err != nil { - return err - } - - o.gather = pr - - prometheusReader, err := prometheus.New( - prometheus.WithRegisterer(pr), - prometheus.WithoutScopeInfo(), - ) - if err != nil { - return err - } - - opts := []sdkmetric.Option{ - sdkmetric.WithReader(prometheusReader), - } - - appName := "" - - if info := cli.InfoFromContext(ctx); info != nil { - appName = info.Name - - opts = append( - opts, - sdkmetric.WithResource( - sdkresource.NewSchemaless( - semconv.ServiceName(info.App.Name), - semconv.ServiceVersion(info.App.Version), - ), - ), - ) - } - - if *o.EnableSimpleAggregation { - aggrReader, err := aggregation.NewReader(metric.RegisteredViews(), func() otelmetric.Meter { - return o.mp.Meter(appName) - }) - if err != nil { - return err - } - - opts = append( - opts, - sdkmetric.WithReader(aggrReader), - ) - } - - if o.CollectorEndpoint != "" { - exporter, err := otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithEndpoint(o.CollectorEndpoint)) - if err != nil { - return err - } - opts = append(opts, sdkmetric.WithReader(sdkmetric.NewPeriodicReader(exporter, sdkmetric.WithInterval(time.Duration(o.CollectIntervalSeconds)*time.Second)))) - } - - for _, v := range metric.RegisteredViews() { - opts = append(opts, sdkmetric.WithView(sdkmetric.NewView(v.Instrument, v.Stream))) - } - - o.mp = sdkmetric.NewMeterProvider(opts...) - m := o.mp.Meter(appName) - - r, err := metric.NewRegistry(m, metric.AddToRegistry) - if err != nil { - return err - } - o.registry = r - - } - return nil -} - -func (o *Metric) Shutdown(ctx context.Context) error { - if o.mp == nil { - return nil - } - _ = o.mp.ForceFlush(ctx) - return o.mp.Shutdown(ctx) -} - -func (o *Metric) InjectContext(ctx context.Context) context.Context { - return configuration.InjectContext(ctx, - configuration.InjectContextFunc(otel.ContextWithGatherer, o.gather), - configuration.InjectContextFunc(metric.ContextWithRegistry, o.registry), - ) -} diff --git a/pkg/otel/metric/instrument.go b/pkg/otel/metric/instrument.go index 2cee433..bfd424e 100644 --- a/pkg/otel/metric/instrument.go +++ b/pkg/otel/metric/instrument.go @@ -2,8 +2,8 @@ package metric import ( "context" - "sync" + "github.com/innoai-tech/infra/internal/otel" otelmetric "go.opentelemetry.io/otel/metric" ) @@ -15,291 +15,112 @@ type Int64Recorder interface { Record(ctx context.Context, incr int64, options ...otelmetric.RecordOption) } -type Int64Observable interface { - AddObserve(ctx context.Context, callback otelmetric.Int64Callback) func() -} - type Float64Counter interface { Add(ctx context.Context, incr float64, options ...otelmetric.AddOption) } + type Float64Recorder interface { Record(ctx context.Context, incr float64, options ...otelmetric.RecordOption) } -type Float64Observable interface { - AddObserve(ctx context.Context, callback otelmetric.Float64Callback) func() -} +func NewInt64UpDownCounter(name string, optFns ...OptionFunc) Int64Counter { + o := newOption(name, optFns...) -func NewInt64UpDownCounter(name string, optFuncs ...OptionFunc) Int64Counter { - o := newOption(name, optFuncs...) - - return register(&int64Instrument{ + return &int64Instrument{ option: o, - createCounter: func(meter otelmetric.Meter) (Int64Counter, error) { + counter: func(meter otelmetric.Meter) (Int64Counter, error) { return meter.Int64UpDownCounter(o.Name, otelmetric.WithUnit(o.Unit), otelmetric.WithDescription(o.Description)) }, - }) -} - -func NewInt64ObservableGauge(name string, optFuncs ...OptionFunc) Int64Observable { - o := newOption(name, optFuncs...) - - return register(&int64Instrument{ - option: o, - createObservable: func(meter otelmetric.Meter) (Int64Observable, error) { - io := &int64Observable{} - - _, err := meter.Int64ObservableGauge( - o.Name, - otelmetric.WithUnit(o.Unit), - otelmetric.WithDescription(o.Description), - otelmetric.WithInt64Callback(io.Callback), - ) - - return io, err - }, - }) + } } func NewInt64Counter(name string, optFuncs ...OptionFunc) Int64Counter { o := newOption(name, optFuncs...) - return register(&int64Instrument{ + return &int64Instrument{ option: o, - createCounter: func(meter otelmetric.Meter) (Int64Counter, error) { + counter: func(meter otelmetric.Meter) (Int64Counter, error) { return meter.Int64Counter(o.Name, otelmetric.WithUnit(o.Unit), otelmetric.WithDescription(o.Description)) }, - }) + } } func NewInt64Histogram(name string, optFuncs ...OptionFunc) Int64Recorder { o := newOption(name, optFuncs...) - return register(&int64Instrument{ + return &int64Instrument{ option: o, - createHistogram: func(meter otelmetric.Meter) (Int64Recorder, error) { + histogram: func(meter otelmetric.Meter) (Int64Recorder, error) { return meter.Int64Histogram(o.Name, otelmetric.WithUnit(o.Unit), otelmetric.WithDescription(o.Description)) }, - }) + } } func NewFloat64UpDownCounter(name string, optFuncs ...OptionFunc) Float64Counter { o := newOption(name, optFuncs...) - return register(&float64Instrument{ + return &float64Instrument{ option: o, - createCounter: func(meter otelmetric.Meter) (Float64Counter, error) { + counter: func(meter otelmetric.Meter) (Float64Counter, error) { return meter.Float64UpDownCounter(o.Name, otelmetric.WithUnit(o.Unit), otelmetric.WithDescription(o.Description)) }, - }) -} - -func NewFloat64ObservableGauge(name string, optFuncs ...OptionFunc) Float64Observable { - o := newOption(name, optFuncs...) - - return register(&float64Instrument{ - option: o, - createObservable: func(meter otelmetric.Meter) (Float64Observable, error) { - fo := &float64Observable{} - - _, err := meter.Float64ObservableGauge( - o.Name, - otelmetric.WithUnit(o.Unit), - otelmetric.WithDescription(o.Description), - otelmetric.WithFloat64Callback(fo.Callback), - ) - - return fo, err - }, - }) + } } func NewFloat64Counter(name string, optFuncs ...OptionFunc) Float64Counter { o := newOption(name, optFuncs...) - return register(&float64Instrument{ + return &float64Instrument{ option: o, - createCounter: func(meter otelmetric.Meter) (Float64Counter, error) { + counter: func(meter otelmetric.Meter) (Float64Counter, error) { return meter.Float64Counter(o.Name, otelmetric.WithUnit(o.Unit), otelmetric.WithDescription(o.Description)) }, - }) + } } func NewFloat64Histogram(name string, optFuncs ...OptionFunc) Float64Recorder { o := newOption(name, optFuncs...) - return register(&float64Instrument{ + return &float64Instrument{ option: o, - createHistogram: func(meter otelmetric.Meter) (Float64Recorder, error) { + histogram: func(meter otelmetric.Meter) (Float64Recorder, error) { return meter.Float64Histogram(o.Name, otelmetric.WithUnit(o.Unit), otelmetric.WithDescription(o.Description)) }, - }) + } } type int64Instrument struct { *option - createObservable func(meter otelmetric.Meter) (Int64Observable, error) - createCounter func(meter otelmetric.Meter) (Int64Counter, error) - createHistogram func(meter otelmetric.Meter) (Int64Recorder, error) -} - -func (i *int64Instrument) register(r RegistryAdder) error { - if i.createObservable != nil { - o, err := i.createObservable(r.Meter()) - if err != nil { - return err - } - r.AddInt64Observable(i.Name, o) - } - - if i.createCounter != nil { - c, err := i.createCounter(r.Meter()) - if err != nil { - return err - } - r.AddInt64Counter(i.Name, c) - } - - if i.createHistogram != nil { - h, err := i.createHistogram(r.Meter()) - if err != nil { - return err - } - r.AddInt64Histogram(i.Name, h) - } - return nil + counter func(meter otelmetric.Meter) (Int64Counter, error) + histogram func(meter otelmetric.Meter) (Int64Recorder, error) } func (i *int64Instrument) Add(ctx context.Context, incr int64, options ...otelmetric.AddOption) { - if c, ok := RegistryFromContext(ctx).Int64Counter(i.Name); ok { + if c, err := i.counter(otel.Meter(ctx)); err == nil { c.Add(ctx, incr, options...) } } func (i *int64Instrument) Record(ctx context.Context, incr int64, options ...otelmetric.RecordOption) { - if c, ok := RegistryFromContext(ctx).Int64Histogram(i.Name); ok { + if c, err := i.histogram(otel.Meter(ctx)); err == nil { c.Record(ctx, incr, options...) } } -func (i *int64Instrument) AddObserve(ctx context.Context, callback otelmetric.Int64Callback) func() { - if c, ok := RegistryFromContext(ctx).Int64Observable(i.Name); ok { - return c.AddObserve(ctx, callback) - } - return func() {} -} - -type int64Observable struct { - lock sync.Mutex - callbacks []otelmetric.Int64Callback -} - -func (o *int64Observable) Callback(ctx context.Context, observer otelmetric.Int64Observer) error { - for i := range o.callbacks { - if c := o.callbacks[i]; c != nil { - if err := c(ctx, observer); err != nil { - return err - } - } - } - return nil -} - -func (o *int64Observable) AddObserve(ctx context.Context, callback otelmetric.Int64Callback) func() { - o.lock.Lock() - defer o.lock.Unlock() - - i := len(o.callbacks) - o.callbacks = append(o.callbacks, callback) - - return func() { - o.lock.Lock() - defer o.lock.Unlock() - - o.callbacks[i] = nil - } -} - type float64Instrument struct { *option - createObservable func(meter otelmetric.Meter) (Float64Observable, error) - createCounter func(meter otelmetric.Meter) (Float64Counter, error) - createHistogram func(meter otelmetric.Meter) (Float64Recorder, error) -} - -func (i *float64Instrument) register(r RegistryAdder) error { - if i.createObservable != nil { - o, err := i.createObservable(r.Meter()) - if err != nil { - return err - } - r.AddFloat64Observable(i.Name, o) - } - - if i.createCounter != nil { - c, err := i.createCounter(r.Meter()) - if err != nil { - return err - } - r.AddFloat64Counter(i.Name, c) - } - - if i.createHistogram != nil { - h, err := i.createHistogram(r.Meter()) - if err != nil { - return err - } - r.AddFloat64Histogram(i.Name, h) - } - return nil + counter func(meter otelmetric.Meter) (Float64Counter, error) + histogram func(meter otelmetric.Meter) (Float64Recorder, error) } func (i *float64Instrument) Add(ctx context.Context, incr float64, options ...otelmetric.AddOption) { - if c, ok := RegistryFromContext(ctx).Float64Counter(i.Name); ok { + if c, err := i.counter(otel.Meter(ctx)); err == nil { c.Add(ctx, incr, options...) } } func (i *float64Instrument) Record(ctx context.Context, incr float64, options ...otelmetric.RecordOption) { - if c, ok := RegistryFromContext(ctx).Float64Histogram(i.Name); ok { + if c, err := i.histogram(otel.Meter(ctx)); err == nil { c.Record(ctx, incr, options...) } } - -func (i *float64Instrument) AddObserve(ctx context.Context, callback otelmetric.Float64Callback) func() { - if c, ok := RegistryFromContext(ctx).Float64Observable(i.Name); ok { - return c.AddObserve(ctx, callback) - } - return func() {} -} - -type float64Observable struct { - lock sync.Mutex - callbacks []otelmetric.Float64Callback -} - -func (o *float64Observable) Callback(ctx context.Context, observer otelmetric.Float64Observer) error { - for i := range o.callbacks { - if c := o.callbacks[i]; c != nil { - if err := c(ctx, observer); err != nil { - return err - } - } - } - return nil -} - -func (o *float64Observable) AddObserve(ctx context.Context, callback otelmetric.Float64Callback) func() { - o.lock.Lock() - defer o.lock.Unlock() - - i := len(o.callbacks) - o.callbacks = append(o.callbacks, callback) - - return func() { - o.lock.Lock() - defer o.lock.Unlock() - - o.callbacks[i] = nil - } -} diff --git a/pkg/otel/metric/metric.go b/pkg/otel/metric/metric.go index 10d5ec6..b3d5254 100644 --- a/pkg/otel/metric/metric.go +++ b/pkg/otel/metric/metric.go @@ -1,17 +1,9 @@ package metric import ( - "sync" - - "github.com/pkg/errors" sdkmetric "go.opentelemetry.io/otel/sdk/metric" ) -type Factory interface { - register(r RegistryAdder) error - metric() Metric -} - type Metric struct { Name string Unit string @@ -23,35 +15,3 @@ type View struct { Instrument sdkmetric.Instrument Stream sdkmetric.Stream } - -var factories = sync.Map{} - -func register[T Factory](f T) T { - m := f.metric() - if _, ok := factories.Load(m.Name); ok { - panic(errors.Errorf("metric %s already register", m.Name)) - } - factories.Store(m.Name, f) - return f -} - -func RegisteredViews() (views []View) { - factories.Range(func(key, value any) bool { - f := value.(Factory) - views = append(views, f.metric().Views...) - return true - }) - return -} - -func AddToRegistry(r RegistryAdder) (err error) { - factories.Range(func(key, value any) bool { - f := value.(Factory) - if e := f.register(r); e != nil { - err = e - return false - } - return true - }) - return -} diff --git a/pkg/otel/metric/registry.go b/pkg/otel/metric/registry.go deleted file mode 100644 index b86f28e..0000000 --- a/pkg/otel/metric/registry.go +++ /dev/null @@ -1,170 +0,0 @@ -package metric - -import ( - "context" - - otelmetric "go.opentelemetry.io/otel/metric" -) - -type registryContext struct{} - -func ContextWithRegistry(ctx context.Context, r Registry) context.Context { - return context.WithValue(ctx, registryContext{}, r) -} - -func RegistryFromContext(ctx context.Context) Registry { - if r, ok := ctx.Value(registryContext{}).(Registry); ok { - return r - } - return &discord{} -} - -type discord struct { -} - -func (discord) Int64Counter(name string) (Int64Counter, bool) { - return nil, false -} - -func (discord) Int64Histogram(name string) (Int64Recorder, bool) { - return nil, false -} - -func (discord) Int64Observable(name string) (Int64Observable, bool) { - return nil, false -} - -func (discord) Float64Counter(name string) (Float64Counter, bool) { - return nil, false -} - -func (discord) Float64Histogram(name string) (Float64Recorder, bool) { - return nil, false -} - -func (discord) Float64Observable(name string) (Float64Observable, bool) { - return nil, false -} - -type Registry interface { - Int64Counter(name string) (Int64Counter, bool) - Int64Histogram(name string) (Int64Recorder, bool) - Int64Observable(name string) (Int64Observable, bool) - - Float64Counter(name string) (Float64Counter, bool) - Float64Histogram(name string) (Float64Recorder, bool) - Float64Observable(name string) (Float64Observable, bool) -} - -type RegistryAdder interface { - Meter() otelmetric.Meter - - AddInt64Counter(name string, c Int64Counter) - AddInt64Histogram(name string, h Int64Recorder) - AddInt64Observable(name string, o Int64Observable) - - AddFloat64Counter(name string, c Float64Counter) - AddFloat64Histogram(name string, h Float64Recorder) - AddFloat64Observable(name string, o Float64Observable) -} - -func NewRegistry(m otelmetric.Meter, init func(r RegistryAdder) error) (Registry, error) { - r := &metricRegister{ - m: m, - int64Counters: map[string]Int64Counter{}, - int64Histograms: map[string]Int64Recorder{}, - int64Observables: map[string]Int64Observable{}, - - float64Counters: map[string]Float64Counter{}, - float64Histograms: map[string]Float64Recorder{}, - float64Observables: map[string]Float64Observable{}, - } - - if err := init(r); err != nil { - return nil, err - } - - return r, nil -} - -type metricRegister struct { - m otelmetric.Meter - - int64Counters map[string]Int64Counter - int64Histograms map[string]Int64Recorder - int64Observables map[string]Int64Observable - - float64Counters map[string]Float64Counter - float64Histograms map[string]Float64Recorder - float64Observables map[string]Float64Observable -} - -func (r *metricRegister) AddInt64Observable(name string, o Int64Observable) { - r.int64Observables[name] = o -} - -func (r *metricRegister) AddFloat64Observable(name string, o Float64Observable) { - r.float64Observables[name] = o -} - -func (r *metricRegister) AddInt64Counter(name string, c Int64Counter) { - r.int64Counters[name] = c -} - -func (r *metricRegister) AddInt64Histogram(name string, h Int64Recorder) { - r.int64Histograms[name] = h -} - -func (r *metricRegister) AddFloat64Counter(name string, c Float64Counter) { - r.float64Counters[name] = c -} - -func (r *metricRegister) AddFloat64Histogram(name string, h Float64Recorder) { - r.float64Histograms[name] = h -} - -func (r *metricRegister) Meter() otelmetric.Meter { - return r.m -} - -func (r *metricRegister) Int64Counter(name string) (Int64Counter, bool) { - if c, ok := r.int64Counters[name]; ok { - return c, true - } - return nil, false -} - -func (r *metricRegister) Int64Histogram(name string) (Int64Recorder, bool) { - if h, ok := r.int64Histograms[name]; ok { - return h, true - } - return nil, false -} - -func (r *metricRegister) Float64Counter(name string) (Float64Counter, bool) { - if c, ok := r.float64Counters[name]; ok { - return c, true - } - return nil, false -} - -func (r *metricRegister) Float64Histogram(name string) (Float64Recorder, bool) { - if h, ok := r.float64Histograms[name]; ok { - return h, true - } - return nil, false -} - -func (r *metricRegister) Int64Observable(name string) (Int64Observable, bool) { - if o, ok := r.int64Observables[name]; ok { - return o, true - } - return nil, false -} - -func (r *metricRegister) Float64Observable(name string) (Float64Observable, bool) { - if o, ok := r.float64Observables[name]; ok { - return o, true - } - return nil, false -} diff --git a/pkg/otel/metric/zz_generated.runtimedoc.go b/pkg/otel/metric/zz_generated.runtimedoc.go new file mode 100644 index 0000000..c349e3c --- /dev/null +++ b/pkg/otel/metric/zz_generated.runtimedoc.go @@ -0,0 +1,49 @@ +/* +Package metric GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package metric + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (v Metric) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Name": + return []string{}, true + case "Unit": + return []string{}, true + case "Description": + return []string{}, true + case "Views": + return []string{}, true + + } + + return nil, false + } + return []string{}, true +} + +func (v View) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Instrument": + return []string{}, true + case "Stream": + return []string{}, true + + } + + return nil, false + } + return []string{}, true +} diff --git a/pkg/otel/otel.go b/pkg/otel/otel.go new file mode 100644 index 0000000..7373b4c --- /dev/null +++ b/pkg/otel/otel.go @@ -0,0 +1,190 @@ +package otel + +import ( + "context" + prometheusclient "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" + "go.opentelemetry.io/otel/exporters/prometheus" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "time" + + "github.com/go-courier/logr" + "github.com/innoai-tech/infra/internal/otel" + "github.com/innoai-tech/infra/pkg/cli" + "github.com/innoai-tech/infra/pkg/configuration" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/sdk/resource" + "golang.org/x/sync/errgroup" + + sdklog "go.opentelemetry.io/otel/sdk/log" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.24.0" +) + +type LogLevel = otel.LogLevel + +const ( + ErrorLevel = otel.ErrorLevel + WarnLevel = otel.WarnLevel + InfoLevel = otel.InfoLevel + DebugLevel = otel.DebugLevel +) + +type Otel struct { + // Log level + LogLevel LogLevel `flag:",omitempty"` + // When set, will collect traces + TraceCollectorEndpoint string `flag:",omitempty"` + + MetricCollectorEndpoint string `flag:",omitempty"` + MetricCollectIntervalSeconds int `flag:",omitempty"` + + tracerProvider *sdktrace.TracerProvider + loggerProvider *sdklog.LoggerProvider + meterProvider *sdkmetric.MeterProvider + promGatherer prometheusclient.Gatherer + + enabledLevel logr.Level +} + +func (o *Otel) SetDefaults() { + if o.LogLevel == "" { + o.LogLevel = InfoLevel + } + + if o.MetricCollectorEndpoint != "" { + if o.MetricCollectIntervalSeconds == 0 { + o.MetricCollectIntervalSeconds = 60 + } + } +} + +func (o *Otel) InjectContext(ctx context.Context) context.Context { + l := otel.NewLogger(otel.LoggerProviderContext.Inject(ctx, o.loggerProvider), o.enabledLevel) + + return configuration.InjectContext( + ctx, + configuration.InjectContextFunc(logr.WithLogger, l), + configuration.InjectContextFunc(otel.GathererContext.Inject, o.promGatherer), + configuration.InjectContextFunc(otel.TracerProviderContext.Inject, otel.TracerProvider(o.tracerProvider)), + configuration.InjectContextFunc(otel.LoggerProviderContext.Inject, otel.LoggerProvider(o.loggerProvider)), + configuration.InjectContextFunc(otel.MeterProviderContext.Inject, otel.MeterProvider(o.meterProvider)), + ) +} + +func (o *Otel) Init(ctx context.Context) error { + enabledLevel, err := logr.ParseLevel(string(o.LogLevel)) + if err != nil { + return err + } + o.enabledLevel = enabledLevel + + pr := prometheusclient.NewRegistry() + + if err := pr.Register(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})); err != nil { + return err + } + if err := pr.Register(collectors.NewGoCollector()); err != nil { + return err + } + + o.promGatherer = pr + + prometheusReader, err := prometheus.New( + prometheus.WithRegisterer(pr), + prometheus.WithoutScopeInfo(), + ) + + tracerOpts := []sdktrace.TracerProviderOption{ + sdktrace.WithSampler(sdktrace.AlwaysSample()), + } + + logOpts := []sdklog.LoggerProviderOption{ + sdklog.WithProcessor(sdklog.NewSimpleProcessor(otel.SlogExporter())), + } + + meterOpts := []sdkmetric.Option{ + sdkmetric.WithReader(prometheusReader), + } + + if info := cli.InfoFromContext(ctx); info != nil { + res := resource.NewSchemaless( + semconv.ServiceName(info.App.Name), + semconv.ServiceVersion(info.App.Version), + ) + + tracerOpts = append(tracerOpts, sdktrace.WithResource(res)) + logOpts = append(logOpts, sdklog.WithResource(res)) + meterOpts = append(meterOpts, sdkmetric.WithResource(res)) + } + + if o.TraceCollectorEndpoint != "" { + client := otlptracegrpc.NewClient( + otlptracegrpc.WithEndpoint(o.TraceCollectorEndpoint), + otlptracegrpc.WithInsecure(), + otlptracegrpc.WithTimeout(3*time.Second), + ) + + exporter, err := otlptrace.New(ctx, client) + if err != nil { + return err + } + + tracerOpts = append(tracerOpts, + sdktrace.WithBatcher( + otel.IgnoreErrSpanExporter(exporter), + ), + ) + } + + if o.MetricCollectorEndpoint != "" { + exporter, err := otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithEndpoint(o.MetricCollectorEndpoint)) + if err != nil { + return err + } + meterOpts = append(meterOpts, + sdkmetric.WithReader( + sdkmetric.NewPeriodicReader(exporter, + sdkmetric.WithInterval(time.Duration(o.MetricCollectIntervalSeconds)*time.Second)), + ), + ) + } + + o.loggerProvider = sdklog.NewLoggerProvider(logOpts...) + o.tracerProvider = sdktrace.NewTracerProvider(tracerOpts...) + o.meterProvider = sdkmetric.NewMeterProvider(meterOpts...) + + return nil +} + +func (o *Otel) Shutdown(c context.Context) error { + eg, ctx := errgroup.WithContext(c) + + if tp := o.tracerProvider; tp != nil { + eg.Go(func() error { + _ = tp.ForceFlush(ctx) + + return tp.Shutdown(ctx) + }) + } + + if lp := o.loggerProvider; lp != nil { + eg.Go(func() error { + _ = lp.ForceFlush(ctx) + + return lp.Shutdown(ctx) + }) + } + + if mp := o.meterProvider; mp != nil { + eg.Go(func() error { + _ = mp.ForceFlush(ctx) + + return mp.Shutdown(ctx) + }) + } + + return eg.Wait() +} diff --git a/pkg/otel/log_test.go b/pkg/otel/otel_test.go similarity index 56% rename from pkg/otel/log_test.go rename to pkg/otel/otel_test.go index 32b021c..5143d2f 100644 --- a/pkg/otel/log_test.go +++ b/pkg/otel/otel_test.go @@ -19,51 +19,20 @@ func Setup(t testing.TB, c any) context.Context { testingx.Expect(t, err, testingx.Be[error](nil)) t.Cleanup(func() { - _ = configuration.Shutdown(ctx, c) + if canShutdown, ok := c.(configuration.CanShutdown); ok { + _ = configuration.Shutdown(ctx, canShutdown) + } }) return configuration.InjectContext(ctx, c.(configuration.ContextInjector)) } func TestLog(t *testing.T) { - t.Run("FilterAlways", func(t *testing.T) { - t.Run("async", func(t *testing.T) { - ctx := Setup(t, &OtelWithBatchLog{ - Otel: Otel{ - LogLevel: DebugLevel, - LogFilter: OutputFilterAlways, - }, - }) - doLog(ctx) - }) - - t.Run("sync", func(t *testing.T) { - ctx := Setup(t, &Otel{ - LogLevel: DebugLevel, - LogFilter: OutputFilterAlways, - }) - doLog(ctx) - }) + ctx := Setup(t, &Otel{ + LogLevel: DebugLevel, }) - t.Run("OutputOnNever", func(t *testing.T) { - ctx := Setup(t, &Otel{ - LogFilter: OutputFilterNever, - LogLevel: DebugLevel, - }) - - ctx, log := logr.FromContext(ctx).Start(ctx, "op") - defer log.End() - doLog(ctx) - }) - - t.Run("OnFailure", func(t *testing.T) { - ctx := Setup(t, &Otel{ - LogFilter: OutputFilterOnFailure, - LogLevel: DebugLevel, - }) - doLog(ctx) - }) + doLog(ctx) } func doLog(ctx context.Context) { diff --git a/pkg/otel/types.go b/pkg/otel/types.go deleted file mode 100644 index 9ec6cdd..0000000 --- a/pkg/otel/types.go +++ /dev/null @@ -1,21 +0,0 @@ -package otel - -import "github.com/innoai-tech/infra/internal/otel" - -type OutputFilterType = otel.OutputFilterType - -const ( - OutputFilterAlways = otel.OutputFilterAlways - OutputFilterOnFailure = otel.OutputFilterOnFailure - OutputFilterNever = otel.OutputFilterNever -) - -// +gengo:enum -type LogLevel string - -const ( - ErrorLevel LogLevel = "error" - WarnLevel LogLevel = "warn" - InfoLevel LogLevel = "info" - DebugLevel LogLevel = "debug" -) diff --git a/pkg/otel/zz_generated.enum.go b/pkg/otel/zz_generated.enum.go deleted file mode 100644 index d879f20..0000000 --- a/pkg/otel/zz_generated.enum.go +++ /dev/null @@ -1,11 +0,0 @@ -/* -Package otel GENERATED BY gengo:enum -DON'T EDIT THIS FILE -*/ -package otel - -func (LogLevel) EnumValues() []any { - return []any{ - DebugLevel, ErrorLevel, InfoLevel, WarnLevel, - } -} diff --git a/pkg/otel/zz_generated.runtimedoc.go b/pkg/otel/zz_generated.runtimedoc.go index e28d902..c141f41 100644 --- a/pkg/otel/zz_generated.runtimedoc.go +++ b/pkg/otel/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package otel GENERATED BY gengo:runtimedoc +Package otel GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package otel @@ -14,9 +14,6 @@ func runtimeDoc(v any, names ...string) ([]string, bool) { return nil, false } -func (LogLevel) RuntimeDoc(names ...string) ([]string, bool) { - return []string{}, true -} func (v Otel) RuntimeDoc(names ...string) ([]string, bool) { if len(names) > 0 { switch names[0] { @@ -24,14 +21,14 @@ func (v Otel) RuntimeDoc(names ...string) ([]string, bool) { return []string{ "Log level", }, true - case "ExportFilter": - return []string{ - "Log filter", - }, true case "TraceCollectorEndpoint": return []string{ "When set, will collect traces", }, true + case "MetricCollectorEndpoint": + return []string{}, true + case "MetricCollectIntervalSeconds": + return []string{}, true }