zenrpc-middleware
is a set of common middlewares for zenrpc implementing logging,
metrics and error tracking.
Sets bool flag to context for detecting development environment.
Sets User-Agent, Platform, Version, X-Country headers to context. User-Agent strips to 2048 chars, Platform and Version – to 64, X-Country - to 16.
Logs via Printf function (e.g. log.Printf) all requests. For example
ip= platform="" version="" method=nodevel.arith.divide duration=63.659µs params="{ \"a\": 1, \"b\": 24 }" err=<nil> userAgent="Go-http-client/1.1"
Sets additional parameters for current Sentry scope. Extras: params, duration, ip. Tags: platform, version, method.
Ignores Cancel func from context. This is useful for passing context to go-pg
.
Logs duration of RPC requests via Prometheus. Default AppName is zenrpc. It exposes two
metrics: appName_rpc_error_requests_count
and appName_rpc_responses_duration_seconds
. Labels: method, code,
platform, version.
Adds timings in JSON-RPC 2.0 Response via extensions
field (not in spec). Middleware is active
when isDevel=true
or AllowDebugFunc returns true
. Sample AllowDebugFunc (checks GET/POST parameters for "true"
value, like ?d=true
):
allowDebugFn := func (param string) middleware.AllowDebugFunc {
return func (req *http.Request) bool {
return req.FormValue(param) == "true"
}
}
DurationLocal
– total method execution time in ms.
If DurationRemote
or DurationDiff
are set then DurationLocal
excludes these values.
Adds SQL
or DurationSQL
fields in JSON-RPC 2.0 Response extensions
field (not in spec).
DurationSQL
field is set then isDevel=true
or AllowDebugFunc(allowDebugFunc) returns true
.
SQL
field is set then isDevel=true
or AllowDebugFunc(allowDebugFunc, allowSqlDebugFunc) returns true
.
Logs all errors (ErrorCode==500 or < 0) via Printf func and sends them to Sentry. It also removes sensitive error data from response. It is good to use pkg/errors for stack trace support in sentry.
ip= platform="Test1" version="v1.0.0 alpha1" method=errordevel.arith.checkzenrpcerror duration=30.786µs params=[ true ] err="test"
Handler for adding X-Request-ID
header to all requests and responses.
s := http.NewServer(middleware.XRequestID(http.HandlerFunc(rpc.ServeHTTP)))
Echo middleware for adding client IP info to context for zenrpc middleware
e.Use(middleware.EchoIPContext())
Echo middleware for adding sentry hub info to context for zenrpc middleware
e.Use(middleware.EchoSentryHubContext())
package main
import (
"log"
"net/http"
"os"
"github.com/go-pg/pg/v10"
"github.com/vmkteam/zenrpc-middleware"
"github.com/vmkteam/zenrpc/v2"
)
func main() {
dbс := pg.Connect(&pg.Options{User: "postgres"})
defer dbс.Close()
isDevel := true
elog := log.New(os.Stderr, "E", log.LstdFlags|log.Lshortfile)
dlog := log.New(os.Stdout, "D", log.LstdFlags|log.Lshortfile)
allowDebug := func(param string) middleware.AllowDebugFunc {
return func(req *http.Request) bool {
return req.FormValue(param) == "true"
}
}
rpc := zenrpc.NewServer(zenrpc.Options{
ExposeSMD: true,
AllowCORS: true,
})
rpc.Use(
middleware.WithDevel(isDevel),
middleware.WithHeaders(),
middleware.WithAPILogger(dlog.Printf, middleware.DefaultServerName),
middleware.WithSentry(middleware.DefaultServerName),
middleware.WithNoCancelContext(),
middleware.WithMetrics(middleware.DefaultServerName),
middleware.WithTiming(isDevel, allowDebug("d")),
middleware.WithSQLLogger(dbc, isDevel, allowDebug("d"), allowDebug("s")),
middleware.WithErrorLogger(elog.Printf, middleware.DefaultServerName),
)
}
// rpc.Register and server
// sentry init
sentry.Init(sentry.ClientOptions{
Dsn: cfg.Sentry.DSN,
Environment: cfg.Sentry.Environment,
Release: version,
}
// sentry middleware for Echo
e.Use(sentryecho.New(sentryecho.Options{
Repanic: true,
WaitForDelivery: true,
}))
// register handler
a.echo.Any("/v1/rpc/", middleware.EchoHandler(rpc))
--- OR ---
e.Use(middleware.EchoIPContext()), middleware.EchoSentryHubContext())
// register handler
e.Any("/int/rpc/", echo.WrapHandler(XRequestID(rpc)))