From 26c22c80421065254eeee14686f17b7275824570 Mon Sep 17 00:00:00 2001 From: Thorsten Essig Date: Tue, 10 Sep 2024 11:45:25 +0200 Subject: [PATCH] ci: introduce flamingo code standards (#15) --- .github/workflows/golangci-lint.yml | 2 +- .golangci.yml | 114 ++++++++++++++++++++++++++++ go.mod | 6 +- go.sum | 9 --- module.go | 87 ++++++++++++++------- sampler.go | 1 + sampler_test.go | 32 ++++++-- 7 files changed, 200 insertions(+), 51 deletions(-) create mode 100644 .golangci.yml diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 8ca087e..ef5af6f 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -25,4 +25,4 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: - version: v1.59 \ No newline at end of file + version: v1.61 diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..934ff21 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,114 @@ +# Options for analysis running. +run: + concurrency: 4 + timeout: 5m + tests: true + modules-download-mode: readonly + allow-parallel-runners: false + +# output configuration options +output: + # Format: colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions + # + # Multiple can be specified by separating them by comma, output can be provided + # for each of them by separating format name and path by colon symbol. + # Output path can be either `stdout`, `stderr` or path to the file to write to. + # Example: "checkstyle:report.json,colored-line-number" + # + # Default: colored-line-number + formats: + - format: tab + print-issued-lines: true + print-linter-name: true + uniq-by-line: true + sort-results: true + +linters: + disable-all: true + enable: + - bidichk + - bodyclose + - containedctx + - contextcheck + - copyloopvar + - cyclop + - durationcheck + - err113 + - errcheck + - errname + - errorlint + - exhaustive + - forbidigo + - forcetypeassert + - gocognit + - goconst + - gocritic + - gofmt + - gomoddirectives + - gosec + - gosimple + - govet + - grouper + - ineffassign + - makezero + - misspell + - mnd + - nakedret + - nestif + - nilerr + - nilnil + - noctx + - nolintlint + - nosprintfhostport + - paralleltest + - prealloc + - predeclared + - revive + - staticcheck + - tenv + - testpackage + - thelper + - tparallel + - typecheck + - unconvert + - unparam + - unused + - usestdlibvars + - varnamelen + - wrapcheck + - wsl + +issues: + new: false + fix: false + exclude-rules: + - path: _test\.go + linters: + - containedctx + - err113 + - forcetypeassert + - goconst + - varnamelen + - wrapcheck + +linters-settings: + mnd: + ignored-functions: + - context.WithTimeout + nolintlint: + require-specific: true + require-explanation: true + revive: + rules: + - name: var-naming + disabled: true + varnamelen: + max-distance: 10 + ignore-type-assert-ok: true + ignore-map-index-ok: true + ignore-chan-recv-ok: true + ignore-names: + - err + - id + ignore-decls: + - i int diff --git a/go.mod b/go.mod index ccf023c..e7ffa7d 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module flamingo.me/opentelemetry -go 1.21 - -toolchain go1.22.6 +go 1.22 require ( flamingo.me/dingo v0.2.10 @@ -37,13 +35,11 @@ require ( github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/sessions v1.3.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lib/pq v1.10.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pkg/errors v0.9.1 // indirect diff --git a/go.sum b/go.sum index 8e05d4c..99be53d 100644 --- a/go.sum +++ b/go.sum @@ -59,8 +59,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -92,8 +90,6 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto= github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= @@ -104,15 +100,11 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= @@ -195,7 +187,6 @@ golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/module.go b/module.go index eb9f6d2..b3843f4 100644 --- a/module.go +++ b/module.go @@ -2,6 +2,7 @@ package opentelemetry import ( "context" + "fmt" "log" "net/http" "net/url" @@ -16,9 +17,7 @@ import ( runtimemetrics "go.opentelemetry.io/contrib/instrumentation/runtime" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/bridge/opencensus" - - //nolint:staticcheck - "go.opentelemetry.io/otel/exporters/jaeger" + "go.opentelemetry.io/otel/exporters/jaeger" //nolint:staticcheck // todo: migrate "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/exporters/prometheus" @@ -68,6 +67,7 @@ func (m *Module) Inject( m.otlpEnableGRPC = cfg.OTLPEnableGRPC m.otlpEndpointGRPC = cfg.OTLPEndpointGRPC } + return m } @@ -83,17 +83,54 @@ func (m *Module) Configure(injector *dingo.Injector) { } func (m *Module) initTraces() { - tracerProviderOptions := make([]tracesdk.TracerProviderOption, 0, 3) + const maxTracerProviderOptions = 5 + tracerProviderOptions := make([]tracesdk.TracerProviderOption, 0, maxTracerProviderOptions) + + tracerProviderOptions = m.initJaeger(tracerProviderOptions) + tracerProviderOptions = m.initOTLP(tracerProviderOptions) + tracerProviderOptions = m.initZipkin(tracerProviderOptions) + + res, err := resource.Merge(resource.Default(), + resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceName(m.serviceName), + semconv.ServiceVersion(flamingo.AppVersion()), + semconv.TelemetrySDKLanguageGo, + )) + if err != nil { + log.Fatalf("failed to initialize otel resource: %v", err) + } + + tracerProviderOptions = append(tracerProviderOptions, + tracesdk.WithResource(res), + tracesdk.WithSampler(tracesdk.AlwaysSample()), + ) + + tp := tracesdk.NewTracerProvider(tracerProviderOptions...) + otel.SetTracerProvider(tp) + tr := tp.Tracer(name, trace.WithInstrumentationVersion(SemVersion())) + octrace.DefaultTracer = opencensus.NewTracer(tr) + + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md#propagators-distribution + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) +} + +func (m *Module) initJaeger(tracerProviderOptions []tracesdk.TracerProviderOption) []tracesdk.TracerProviderOption { // Create the Jaeger exporter if m.jaegerEnable { exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(m.jaegerEndpoint))) if err != nil { log.Fatalf("failed to initialze Jeager exporter: %v", err) } + tracerProviderOptions = append(tracerProviderOptions, tracesdk.WithBatcher(exp)) } + return tracerProviderOptions +} + +func (m *Module) initOTLP(tracerProviderOptions []tracesdk.TracerProviderOption) []tracesdk.TracerProviderOption { // Create the OTLP HTTP exporter if m.otlpEnableHTTP { u, err := url.Parse(m.otlpEndpointHTTP) @@ -110,6 +147,7 @@ func (m *Module) initTraces() { if err != nil { log.Fatalf("failed to initialze OTLP HTTP exporter: %v", err) } + tracerProviderOptions = append(tracerProviderOptions, tracesdk.WithBatcher(exp)) } @@ -119,9 +157,14 @@ func (m *Module) initTraces() { if err != nil { log.Fatalf("failed to initialze OTLP gRPC exporter: %v", err) } + tracerProviderOptions = append(tracerProviderOptions, tracesdk.WithBatcher(exp)) } + return tracerProviderOptions +} + +func (m *Module) initZipkin(tracerProviderOptions []tracesdk.TracerProviderOption) []tracesdk.TracerProviderOption { // Create the Zipkin exporter if m.zipkinEnable { exp, err := zipkin.New( @@ -130,37 +173,16 @@ func (m *Module) initTraces() { if err != nil { log.Fatalf("failed to initialize Zipkin exporter: %v", err) } - tracerProviderOptions = append(tracerProviderOptions, tracesdk.WithBatcher(exp)) - } - res, err := resource.Merge(resource.Default(), - resource.NewWithAttributes( - semconv.SchemaURL, - semconv.ServiceName(m.serviceName), - semconv.ServiceVersion(flamingo.AppVersion()), - semconv.TelemetrySDKLanguageGo, - )) - if err != nil { - log.Fatalf("failed to initialize otel resource: %v", err) + tracerProviderOptions = append(tracerProviderOptions, tracesdk.WithBatcher(exp)) } - tracerProviderOptions = append(tracerProviderOptions, - tracesdk.WithResource(res), - tracesdk.WithSampler(tracesdk.AlwaysSample()), - ) - - tp := tracesdk.NewTracerProvider(tracerProviderOptions...) - otel.SetTracerProvider(tp) - - tr := tp.Tracer(name, trace.WithInstrumentationVersion(SemVersion())) - octrace.DefaultTracer = opencensus.NewTracer(tr) - - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md#propagators-distribution - otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) + return tracerProviderOptions } func (m *Module) initMetrics(injector *dingo.Injector) { bridge := opencensus.NewMetricProducer() + exp, err := prometheus.New(prometheus.WithProducer(bridge)) if err != nil { log.Fatalf("failed to initialize Prometheus exporter: %v", err) @@ -168,6 +190,7 @@ func (m *Module) initMetrics(injector *dingo.Injector) { meterProvider := sdkMetric.NewMeterProvider(sdkMetric.WithReader(exp)) otel.SetMeterProvider(meterProvider) + if err := runtimemetrics.Start(); err != nil { log.Fatal(err) } @@ -190,7 +213,13 @@ func (rt *correlationIDInjector) RoundTrip(req *http.Request) (*http.Response, e if span.SpanContext().IsSampled() { req.Header.Add("X-Correlation-ID", span.SpanContext().TraceID().String()) } - return rt.next.RoundTrip(req) + + resp, err := rt.next.RoundTrip(req) + if err != nil { + return nil, fmt.Errorf("correlationIDInjector next RoundTrip failed: %w", err) + } + + return resp, nil } func (m *Module) CueConfig() string { diff --git a/sampler.go b/sampler.go index 868d08b..ab41a17 100644 --- a/sampler.go +++ b/sampler.go @@ -28,6 +28,7 @@ func (c *ConfiguredURLPrefixSampler) Inject( c.Blocklist = cfg.Blocklist c.IgnoreParentDecision = cfg.IgnoreParentDecision } + return c } diff --git a/sampler_test.go b/sampler_test.go index caae08f..557c4d1 100644 --- a/sampler_test.go +++ b/sampler_test.go @@ -1,13 +1,18 @@ -package opentelemetry +package opentelemetry_test import ( + "context" "net/http" "testing" "github.com/stretchr/testify/assert" + + "flamingo.me/opentelemetry" ) func TestURLPrefixSampler_SampleAll(t *testing.T) { + t.Parallel() + tests := []struct { path string want bool @@ -30,10 +35,13 @@ func TestURLPrefixSampler_SampleAll(t *testing.T) { }, } - shouldSample := URLPrefixSampler(nil, nil, true) + shouldSample := opentelemetry.URLPrefixSampler(nil, nil, true) + for _, tt := range tests { t.Run("checking path "+tt.path, func(t *testing.T) { - request, err := http.NewRequest(http.MethodGet, tt.path, nil) + t.Parallel() + + request, err := http.NewRequestWithContext(context.Background(), http.MethodGet, tt.path, nil) assert.NoError(t, err) if got := shouldSample(request); got != tt.want { @@ -44,6 +52,8 @@ func TestURLPrefixSampler_SampleAll(t *testing.T) { } func TestURLPrefixSampler_SampleAllowed(t *testing.T) { + t.Parallel() + tests := []struct { path string want bool @@ -66,10 +76,13 @@ func TestURLPrefixSampler_SampleAllowed(t *testing.T) { }, } - shouldSample := URLPrefixSampler([]string{"/my-path", "/nested"}, nil, true) + shouldSample := opentelemetry.URLPrefixSampler([]string{"/my-path", "/nested"}, nil, true) + for _, tt := range tests { t.Run("checking path "+tt.path, func(t *testing.T) { - request, err := http.NewRequest(http.MethodGet, tt.path, nil) + t.Parallel() + + request, err := http.NewRequestWithContext(context.Background(), http.MethodGet, tt.path, nil) assert.NoError(t, err) if got := shouldSample(request); got != tt.want { @@ -80,6 +93,8 @@ func TestURLPrefixSampler_SampleAllowed(t *testing.T) { } func TestURLPrefixSampler_SampleBlocked(t *testing.T) { + t.Parallel() + tests := []struct { path string want bool @@ -102,10 +117,13 @@ func TestURLPrefixSampler_SampleBlocked(t *testing.T) { }, } - shouldSample := URLPrefixSampler(nil, []string{"/static"}, true) + shouldSample := opentelemetry.URLPrefixSampler(nil, []string{"/static"}, true) + for _, tt := range tests { t.Run("checking path "+tt.path, func(t *testing.T) { - request, err := http.NewRequest(http.MethodGet, tt.path, nil) + t.Parallel() + + request, err := http.NewRequestWithContext(context.Background(), http.MethodGet, tt.path, nil) assert.NoError(t, err) if got := shouldSample(request); got != tt.want {