Metlo provides a native middleware for the Gin web framework in Go.
Installation
Install the Metlo Gin package:
go get github.com/metlo-labs/metlo/ingestors/golang/gin
go get github.com/metlo-labs/metlo/ingestors/golang/metlo
Quick Start
Add Metlo middleware to your Gin application:
package main
import (
" github.com/gin-gonic/gin "
metloGin " github.com/metlo-labs/metlo/ingestors/golang/gin "
" github.com/metlo-labs/metlo/ingestors/golang/metlo "
)
func main () {
// Initialize Metlo
metloApp := metlo . InitMetlo (
"<YOUR_METLO_COLLECTOR_URL>" ,
"<YOUR_METLO_API_KEY>" ,
)
// Create Gin router
r := gin . Default ()
// Add Metlo middleware
metloMiddleware := metloGin . Init ( metloApp )
r . Use ( metloMiddleware . Middleware )
// Define routes
r . GET ( "/" , func ( c * gin . Context ) {
c . JSON ( 200 , gin . H {
"message" : "Hello World" ,
})
})
r . Run ( ":8080" )
}
Configuration
Basic Initialization
The simplest setup uses default configuration:
metloApp := metlo . InitMetlo (
"https://collector.metlo.com" ,
"your_api_key_here" ,
)
metloMiddleware := metloGin . Init ( metloApp )
r . Use ( metloMiddleware . Middleware )
Custom Initialization
For advanced configuration, use CustomInit:
metloApp := metlo . InitMetlo (
"https://collector.metlo.com" ,
"your_api_key_here" ,
)
metloMiddleware := metloGin . CustomInit (
metloApp ,
"api.example.com" , // Server host
8080 , // Server port
getUserFunc , // Optional user extraction function
)
r . Use ( metloMiddleware . Middleware )
Initialized Metlo application instance
serverHost
string
default: "localhost"
The hostname of your server for trace metadata
The port your server is listening on
getUser
func(*gin.Context) *string
default: "nil"
Optional function to extract user identifier from context
User Tracking
Extract user information from requests for better tracing:
func getUserFromContext ( c * gin . Context ) * string {
// Extract from JWT token, session, etc.
userID , exists := c . Get ( "userID" )
if ! exists {
return nil
}
userIDStr := userID .( string )
return & userIDStr
}
metloMiddleware := metloGin . CustomInit (
metloApp ,
"api.example.com" ,
8080 ,
getUserFromContext ,
)
How It Works
The Gin middleware:
Wraps response writer - Captures response data as it’s written
Reads request body - Captures and restores request body
Rate limiting - Checks if request should be traced
Blocking - Can block requests based on security rules
Asynchronous transmission - Sends traces to Metlo without blocking
Request Blocking
Metlo can block malicious requests based on configured rules:
if m . app . ShouldBlock ( request , meta ) {
c . String ( 403 , "Forbidden" )
c . Abort ()
return
}
Captured Data
For each request, Metlo captures:
Request:
URL (host, path, query parameters)
HTTP method
Headers
Request body (up to 10KB)
User identifier (if configured)
Response:
Status code
Headers
Response body (up to 10KB)
Metadata:
Source IP and port
Destination host and port
Environment: production
Source identifier: go/gin
Full Example Application
package main
import (
" net/http "
" github.com/gin-gonic/gin "
metloGin " github.com/metlo-labs/metlo/ingestors/golang/gin "
" github.com/metlo-labs/metlo/ingestors/golang/metlo "
)
type User struct {
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required,email"`
}
func getUserFromContext ( c * gin . Context ) * string {
if userID , exists := c . Get ( "userID" ); exists {
userIDStr := userID .( string )
return & userIDStr
}
return nil
}
func main () {
// Initialize Metlo
metloApp := metlo . InitMetlo (
"https://collector.metlo.com" ,
"your_api_key_here" ,
)
// Create Gin router
r := gin . Default ()
// Add Metlo middleware
metloMiddleware := metloGin . CustomInit (
metloApp ,
"api.example.com" ,
8080 ,
getUserFromContext ,
)
r . Use ( metloMiddleware . Middleware )
// API routes
api := r . Group ( "/api" )
{
api . GET ( "/users" , func ( c * gin . Context ) {
c . JSON ( http . StatusOK , gin . H {
"users" : [] User {},
})
})
api . POST ( "/users" , func ( c * gin . Context ) {
var user User
if err := c . ShouldBindJSON ( & user ); err != nil {
c . JSON ( http . StatusBadRequest , gin . H { "error" : err . Error ()})
return
}
c . JSON ( http . StatusCreated , gin . H {
"success" : true ,
"user" : user ,
})
})
}
// Health check (you might want to exclude from Metlo)
r . GET ( "/health" , func ( c * gin . Context ) {
c . JSON ( http . StatusOK , gin . H { "status" : "healthy" })
})
r . Run ( ":8080" )
}
Environment Variables
Use environment variables for configuration:
import (
" os "
" strconv "
)
func main () {
metloApp := metlo . InitMetlo (
os . Getenv ( "METLO_COLLECTOR_URL" ),
os . Getenv ( "METLO_API_KEY" ),
)
serverPort , _ := strconv . Atoi ( os . Getenv ( "PORT" ))
metloMiddleware := metloGin . CustomInit (
metloApp ,
os . Getenv ( "SERVER_HOST" ),
serverPort ,
nil ,
)
r := gin . Default ()
r . Use ( metloMiddleware . Middleware )
// ... routes ...
r . Run ( ":" + os . Getenv ( "PORT" ))
}
Body Size Limits
Metlo captures up to 10KB of request and response bodies by default:
const MAX_BODY int = 10 * 1024 // 10KB
Larger bodies are truncated to prevent memory issues.
Troubleshooting
Middleware not capturing requests
Ensure:
Metlo middleware is added with r.Use(metloMiddleware.Middleware)
Middleware is added before route definitions
Metlo app is properly initialized
Collector URL is accessible from your application
The middleware reads and restores the request body. If you’re reading c.Request.Body before the middleware, it may be consumed. Ensure Metlo middleware runs first.
Large responses causing issues
Metlo limits body capture to 10KB. If you’re experiencing memory issues, this limit is already applied. Consider reducing logged response sizes in your application.
If you see “Metlo couldn’t find source port” in logs, it’s a warning and won’t prevent tracing. The source port extraction failed but tracing continues.
Rate Limiting
Metlo includes automatic rate limiting via m.app.Allow(). Configure rate limits when initializing the Metlo app:
metloApp := metlo . InitMetlo (
collectorURL ,
apiKey ,
// Additional rate limit options in metlo.InitMetlo if supported
)
Requirements
Next Steps
API Inventory Discover your Gin API endpoints
Gorilla Integration Use Metlo with Gorilla Mux