Gin-gonic Instrumentation
Gin-gonic is a full featured web framework for Go. Data reported from Gin-gonic is supported by AppSignal.
This page will help you get set up to instrument Gin-gonic applications and report request parameters and payloads.
Setup
To instrument your Go-gin application, you'll need to import the OpenTelemetry official instrumentation package:
import "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
The Go-gin instrumentation is packaged as a router middleware, as you can see in the example below, the tracer initialization is called before anything as described in more detail in the Go installation page. Adding otelgin.Middleware()
to your app router is all that you need to get all your requests instrumented.
func main() { cleanup := initTracer() defer cleanup(context.Background()) router := gin.New() // This middleware must be added before any middlewares or routes router.Use(otelgin.Middleware("your-app-name")) // Your routes, app's logic, and server start up // ... }
Reporting request parameters
By default, the Gin instrumentation does not report any incoming request parameters, like query strings and JSON body contents.
To report these values to AppSignal, add a middleware to your Gorilla stack as follows:
import ( "bytes" "encoding/json" "context" "io/ioutil" "net/url" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) func recordParameters(c *gin.Context) { span := trace.SpanFromContext(c.Request.Context()) // Query parameters requestQueryParameters := c.Request.URL.Query() attributeQueryParameters := make(map[string]any) for k, v := range requestQueryParameters { attributeQueryParameters[k] = v } if len(attributeQueryParameters) > 0 { serializedQueryParams, err := json.Marshal(attributeQueryParameters) if err == nil { span.SetAttributes(attribute.String("appsignal.request.query_parameters", string(serializedQueryParams))) } } // Request body payload var serializedBodyPayload string var payload map[string]interface{} requestBodyPayload, err := ioutil.ReadAll(c.Request.Body) if err != nil { c.Next() return } c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(requestBodyPayload)) contentType := c.GetHeader("Content-Type") if contentType == "application/json" { serializedBodyPayload = string(requestBodyPayload) } else if contentType == "application/x-www-form-urlencoded" { // Parse form-urlencoded body values, err := url.ParseQuery(string(requestBodyPayload)) if err != nil { c.Next() return } // Convert form values to a JSON-compatible map payload = make(map[string]interface{}) for key, val := range values { if len(val) == 1 { payload[key] = val[0] } else { payload[key] = val } } json, _ := json.Marshal(payload) serializedBodyPayload = string(json) } else { c.Next() return } if len(serializedBodyPayload) > 0 { span.SetAttributes(attribute.String("appsignal.request.payload", serializedBodyPayload)) } c.Next() } func main() { // Init OpenTelemetry cleanup := initTracer() defer cleanup(context.Background()) r := gin.New() r.Use(otelgin.Middleware("opentelemetry-go-gin")) // After the OpenTelemetry middleware, but before your other middlewares: r.Use(recordParameters) // Other middlewares and router handlers go here... }