mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-05-20 17:28:40 +00:00
143 lines
4.3 KiB
Go
143 lines
4.3 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Based on https://github.com/DataDog/dd-trace-go/blob/8fb554ff7cf694267f9077ae35e27ce4689ed8b6/contrib/gin-gonic/gin/gintrace.go
|
|
|
|
package otelgin // import "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"go.opentelemetry.io/otel"
|
|
"go.opentelemetry.io/otel/codes"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/propagation"
|
|
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
|
"go.opentelemetry.io/otel/semconv/v1.17.0/httpconv"
|
|
oteltrace "go.opentelemetry.io/otel/trace"
|
|
)
|
|
|
|
const (
|
|
tracerKey = "otel-go-contrib-tracer"
|
|
tracerName = "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
|
|
)
|
|
|
|
// Middleware returns middleware that will trace incoming requests.
|
|
// The service parameter should describe the name of the (virtual)
|
|
// server handling the request.
|
|
func Middleware(service string, opts ...Option) gin.HandlerFunc {
|
|
cfg := config{}
|
|
for _, opt := range opts {
|
|
opt.apply(&cfg)
|
|
}
|
|
if cfg.TracerProvider == nil {
|
|
cfg.TracerProvider = otel.GetTracerProvider()
|
|
}
|
|
tracer := cfg.TracerProvider.Tracer(
|
|
tracerName,
|
|
oteltrace.WithInstrumentationVersion(SemVersion()),
|
|
)
|
|
if cfg.Propagators == nil {
|
|
cfg.Propagators = otel.GetTextMapPropagator()
|
|
}
|
|
return func(c *gin.Context) {
|
|
for _, f := range cfg.Filters {
|
|
if !f(c.Request) {
|
|
// Serve the request to the next middleware
|
|
// if a filter rejects the request.
|
|
c.Next()
|
|
return
|
|
}
|
|
}
|
|
c.Set(tracerKey, tracer)
|
|
savedCtx := c.Request.Context()
|
|
defer func() {
|
|
c.Request = c.Request.WithContext(savedCtx)
|
|
}()
|
|
ctx := cfg.Propagators.Extract(savedCtx, propagation.HeaderCarrier(c.Request.Header))
|
|
opts := []oteltrace.SpanStartOption{
|
|
oteltrace.WithAttributes(httpconv.ServerRequest(service, c.Request)...),
|
|
oteltrace.WithSpanKind(oteltrace.SpanKindServer),
|
|
}
|
|
var spanName string
|
|
if cfg.SpanNameFormatter == nil {
|
|
spanName = c.FullPath()
|
|
} else {
|
|
spanName = cfg.SpanNameFormatter(c.Request)
|
|
}
|
|
if spanName == "" {
|
|
spanName = fmt.Sprintf("HTTP %s route not found", c.Request.Method)
|
|
} else {
|
|
rAttr := semconv.HTTPRoute(spanName)
|
|
opts = append(opts, oteltrace.WithAttributes(rAttr))
|
|
}
|
|
ctx, span := tracer.Start(ctx, spanName, opts...)
|
|
defer span.End()
|
|
|
|
// pass the span through the request context
|
|
c.Request = c.Request.WithContext(ctx)
|
|
|
|
// serve the request to the next middleware
|
|
c.Next()
|
|
|
|
status := c.Writer.Status()
|
|
span.SetStatus(httpconv.ServerStatus(status))
|
|
if status > 0 {
|
|
span.SetAttributes(semconv.HTTPStatusCode(status))
|
|
}
|
|
if len(c.Errors) > 0 {
|
|
span.SetAttributes(attribute.String("gin.errors", c.Errors.String()))
|
|
}
|
|
}
|
|
}
|
|
|
|
// HTML will trace the rendering of the template as a child of the
|
|
// span in the given context. This is a replacement for
|
|
// gin.Context.HTML function - it invokes the original function after
|
|
// setting up the span.
|
|
func HTML(c *gin.Context, code int, name string, obj interface{}) {
|
|
var tracer oteltrace.Tracer
|
|
tracerInterface, ok := c.Get(tracerKey)
|
|
if ok {
|
|
tracer, ok = tracerInterface.(oteltrace.Tracer)
|
|
}
|
|
if !ok {
|
|
tracer = otel.GetTracerProvider().Tracer(
|
|
tracerName,
|
|
oteltrace.WithInstrumentationVersion(SemVersion()),
|
|
)
|
|
}
|
|
savedContext := c.Request.Context()
|
|
defer func() {
|
|
c.Request = c.Request.WithContext(savedContext)
|
|
}()
|
|
opt := oteltrace.WithAttributes(attribute.String("go.template", name))
|
|
_, span := tracer.Start(savedContext, "gin.renderer.html", opt)
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
err := fmt.Errorf("error rendering template:%s: %s", name, r)
|
|
span.RecordError(err)
|
|
span.SetStatus(codes.Error, "template failure")
|
|
span.End()
|
|
panic(r)
|
|
} else {
|
|
span.End()
|
|
}
|
|
}()
|
|
c.HTML(code, name, obj)
|
|
}
|