parent
							
								
									e0a25c274e
								
							
						
					
					
						commit
						a278d10e4c
					
				@ -0,0 +1,141 @@
 | 
			
		||||
## 基于 golangci-lint@v1.52.2
 | 
			
		||||
run:
 | 
			
		||||
  timeout: 1m
 | 
			
		||||
  build-tags: [ ]
 | 
			
		||||
  skip-dirs: [ ]
 | 
			
		||||
  skip-files: [ ]
 | 
			
		||||
linters:
 | 
			
		||||
  disable-all: true
 | 
			
		||||
  enable:
 | 
			
		||||
    - errcheck
 | 
			
		||||
    - gosimple
 | 
			
		||||
    - govet
 | 
			
		||||
    - ineffassign
 | 
			
		||||
    - staticcheck
 | 
			
		||||
    - typecheck
 | 
			
		||||
    - unused
 | 
			
		||||
    - asasalint
 | 
			
		||||
    - asciicheck
 | 
			
		||||
    - bidichk
 | 
			
		||||
    - bodyclose
 | 
			
		||||
    - containedctx
 | 
			
		||||
    - cyclop
 | 
			
		||||
    - dupl
 | 
			
		||||
    - durationcheck
 | 
			
		||||
    - errname
 | 
			
		||||
    - errorlint
 | 
			
		||||
    - exhaustive
 | 
			
		||||
    - exportloopref
 | 
			
		||||
    - funlen
 | 
			
		||||
    - gocheckcompilerdirectives
 | 
			
		||||
    - gochecknoinits
 | 
			
		||||
    - goconst
 | 
			
		||||
    - gocritic
 | 
			
		||||
    - gocyclo
 | 
			
		||||
    - goimports
 | 
			
		||||
    - gomnd
 | 
			
		||||
    - goprintffuncname
 | 
			
		||||
    - gosec
 | 
			
		||||
    - lll
 | 
			
		||||
    - loggercheck
 | 
			
		||||
    - makezero
 | 
			
		||||
    - nakedret
 | 
			
		||||
    - nestif
 | 
			
		||||
    - nilnil
 | 
			
		||||
    - noctx
 | 
			
		||||
    - nolintlint
 | 
			
		||||
    - prealloc
 | 
			
		||||
    - predeclared
 | 
			
		||||
    - promlinter
 | 
			
		||||
    - reassign
 | 
			
		||||
    - revive
 | 
			
		||||
    - rowserrcheck
 | 
			
		||||
    - stylecheck
 | 
			
		||||
    - tenv
 | 
			
		||||
    - testableexamples
 | 
			
		||||
    - testpackage
 | 
			
		||||
    - tparallel
 | 
			
		||||
    - unconvert
 | 
			
		||||
    - unparam
 | 
			
		||||
    - usestdlibvars
 | 
			
		||||
    - wastedassign
 | 
			
		||||
    - whitespace
 | 
			
		||||
linters-settings:
 | 
			
		||||
  errcheck:
 | 
			
		||||
    check-type-assertions: true
 | 
			
		||||
    exclude-functions: [ ]
 | 
			
		||||
  govet:
 | 
			
		||||
    enable-all: true
 | 
			
		||||
    disable: [ ]
 | 
			
		||||
  cyclop:
 | 
			
		||||
    max-complexity: 10
 | 
			
		||||
    package-average: 0.0
 | 
			
		||||
  dupl:
 | 
			
		||||
    threshold: 150
 | 
			
		||||
  exhaustive:
 | 
			
		||||
    check:
 | 
			
		||||
      - switch
 | 
			
		||||
      - map
 | 
			
		||||
  funlen:
 | 
			
		||||
    lines: 100
 | 
			
		||||
    statements: 60
 | 
			
		||||
  gocritic:
 | 
			
		||||
    disabled-checks:
 | 
			
		||||
      - commentFormatting
 | 
			
		||||
    settings:
 | 
			
		||||
      captLocal:
 | 
			
		||||
        paramsOnly: false
 | 
			
		||||
      underef:
 | 
			
		||||
        skipRecvDeref: false
 | 
			
		||||
  gocyclo:
 | 
			
		||||
    min-complexity: 20
 | 
			
		||||
  gomnd:
 | 
			
		||||
    ignored-functions:
 | 
			
		||||
      - os.Chmod
 | 
			
		||||
      - os.Mkdir
 | 
			
		||||
      - os.MkdirAll
 | 
			
		||||
      - os.OpenFile
 | 
			
		||||
      - os.WriteFile
 | 
			
		||||
      - prometheus.ExponentialBuckets
 | 
			
		||||
      - prometheus.ExponentialBucketsRange
 | 
			
		||||
      - prometheus.LinearBuckets
 | 
			
		||||
  lll:
 | 
			
		||||
    line-length: 240
 | 
			
		||||
  nakedret:
 | 
			
		||||
    max-func-lines: 10
 | 
			
		||||
  nestif:
 | 
			
		||||
    min-complexity: 5
 | 
			
		||||
  predeclared:
 | 
			
		||||
    ignore: ""
 | 
			
		||||
    q: false
 | 
			
		||||
  reassign:
 | 
			
		||||
    patterns:
 | 
			
		||||
      - ".*"
 | 
			
		||||
  rowserrcheck:
 | 
			
		||||
    packages:
 | 
			
		||||
      - github.com/jmoiron/sqlx
 | 
			
		||||
  tenv:
 | 
			
		||||
    all: true
 | 
			
		||||
  usestdlibvars:
 | 
			
		||||
    time-month: true
 | 
			
		||||
    time-layout: true
 | 
			
		||||
    crypto-hash: true
 | 
			
		||||
    default-rpc-path: true
 | 
			
		||||
    os-dev-null: true
 | 
			
		||||
    sql-isolation-level: true
 | 
			
		||||
    tls-signature-scheme: true
 | 
			
		||||
    constant-kind: true
 | 
			
		||||
    syslog-priority: true
 | 
			
		||||
issues:
 | 
			
		||||
  max-same-issues: 10
 | 
			
		||||
  exclude-rules:
 | 
			
		||||
    - source: "//noinspection"
 | 
			
		||||
      linters: [ gocritic ]
 | 
			
		||||
    - path: "_test\\.go"
 | 
			
		||||
      linters:
 | 
			
		||||
        - bodyclose
 | 
			
		||||
        - dupl
 | 
			
		||||
        - funlen
 | 
			
		||||
        - goconst
 | 
			
		||||
        - gosec
 | 
			
		||||
        - noctx
 | 
			
		||||
@ -0,0 +1,15 @@
 | 
			
		||||
module git.blauwelle.com/go/crate/bunrouterotel
 | 
			
		||||
 | 
			
		||||
go 1.21.1
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/uptrace/bunrouter v1.0.20
 | 
			
		||||
	go.opentelemetry.io/otel v1.19.0
 | 
			
		||||
	go.opentelemetry.io/otel/trace v1.19.0
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/go-logr/logr v1.2.4 // indirect
 | 
			
		||||
	github.com/go-logr/stdr v1.2.2 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/metric v1.19.0 // indirect
 | 
			
		||||
)
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 | 
			
		||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
 | 
			
		||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 | 
			
		||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 | 
			
		||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 | 
			
		||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 | 
			
		||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
			
		||||
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/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 | 
			
		||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 | 
			
		||||
github.com/uptrace/bunrouter v1.0.20 h1:jNvYNcJxF+lSYBQAaQjnE6I11Zs0m+3M5Ek7fq/Tp4c=
 | 
			
		||||
github.com/uptrace/bunrouter v1.0.20/go.mod h1:TwT7Bc0ztF2Z2q/ZzMuSVkcb/Ig/d3MQeP2cxn3e1hI=
 | 
			
		||||
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
 | 
			
		||||
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
 | 
			
		||||
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
 | 
			
		||||
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
 | 
			
		||||
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
 | 
			
		||||
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
@ -0,0 +1,79 @@
 | 
			
		||||
package bunrouterotel
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/uptrace/bunrouter"
 | 
			
		||||
	"go.opentelemetry.io/otel"
 | 
			
		||||
	"go.opentelemetry.io/otel/attribute"
 | 
			
		||||
	"go.opentelemetry.io/otel/propagation"
 | 
			
		||||
	semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
 | 
			
		||||
	"go.opentelemetry.io/otel/trace"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	tracerName = "git.blauwelle.com/go/crate/bunrouterotel"
 | 
			
		||||
	version    = "0.1.0"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type config struct {
 | 
			
		||||
	propagators propagation.TextMapPropagator
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Option interface {
 | 
			
		||||
	apply(cfg *config)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type optionFunc func(cfg *config)
 | 
			
		||||
 | 
			
		||||
func (o optionFunc) apply(cfg *config) {
 | 
			
		||||
	o(cfg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WithPropagators(propagators propagation.TextMapPropagator) Option {
 | 
			
		||||
	return optionFunc(func(cfg *config) {
 | 
			
		||||
		cfg.propagators = propagators
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Middleware create a span, which record the request,
 | 
			
		||||
// HTTP status code is NOT recorded.
 | 
			
		||||
func Middleware(serverName string, opts ...Option) bunrouter.MiddlewareFunc {
 | 
			
		||||
	cfg := config{}
 | 
			
		||||
	for _, opt := range opts {
 | 
			
		||||
		opt.apply(&cfg)
 | 
			
		||||
	}
 | 
			
		||||
	if cfg.propagators == nil {
 | 
			
		||||
		cfg.propagators = otel.GetTextMapPropagator()
 | 
			
		||||
	}
 | 
			
		||||
	tracer := otel.Tracer(tracerName, trace.WithInstrumentationVersion("semver:"+version))
 | 
			
		||||
	return func(next bunrouter.HandlerFunc) bunrouter.HandlerFunc {
 | 
			
		||||
		return func(w http.ResponseWriter, req bunrouter.Request) error {
 | 
			
		||||
			ctx := cfg.propagators.Extract(req.Context(), propagation.HeaderCarrier(req.Header))
 | 
			
		||||
			var spanName string
 | 
			
		||||
			params := req.Params()
 | 
			
		||||
			route := params.Route()
 | 
			
		||||
			if route != "" {
 | 
			
		||||
				spanName = req.Method + " " + route
 | 
			
		||||
			} else {
 | 
			
		||||
				spanName = "HTTP " + req.Method + " route not found"
 | 
			
		||||
			}
 | 
			
		||||
			tarcerStartOpts := []trace.SpanStartOption{
 | 
			
		||||
				trace.WithAttributes(semconv.NetAttributesFromHTTPRequest("tcp", req.Request)...),
 | 
			
		||||
				trace.WithAttributes(semconv.EndUserAttributesFromHTTPRequest(req.Request)...),
 | 
			
		||||
				trace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest(serverName, route, req.Request)...),
 | 
			
		||||
				trace.WithSpanKind(trace.SpanKindServer),
 | 
			
		||||
			}
 | 
			
		||||
			ctx, span := tracer.Start(ctx, spanName, tarcerStartOpts...)
 | 
			
		||||
			req.Request = req.Request.WithContext(ctx)
 | 
			
		||||
			paramSlice := params.Slice()
 | 
			
		||||
			attrs := make([]attribute.KeyValue, 0, len(paramSlice))
 | 
			
		||||
			for _, param := range paramSlice {
 | 
			
		||||
				attrs = append(attrs, attribute.String("http.route.param."+param.Key, param.Value))
 | 
			
		||||
			}
 | 
			
		||||
			span.SetAttributes(attrs...)
 | 
			
		||||
			defer span.End()
 | 
			
		||||
			return next(w, req)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue