grpc-ecosystem/go-grpc-middleware
> A collection of gRPC-Go interceptors — auth, logging, retries, recovery, rate limiting, validation — plus the plumbing to chain them.
GitHub repo · License: Apache-2.0
Overview
gRPC-Go exposes a single extension point on both client and server: the interceptor, a function that wraps a call the way HTTP middleware wraps a handler. This repository is the community's standard bag of interceptors for the common cross-cutting concerns — authentication, structured logging, metrics, retries, timeouts, panic recovery, rate limiting, and message validation — along with the selector helper that turns any interceptor on or off per service/method. It sits under the grpc-ecosystem org alongside grpc-gateway, and is one of the most-depended-on Go modules in the gRPC world.
The project is on its second major line. v1 dates from ~2015 and is effectively frozen; v2 is the active branch (main) and is a deliberate rewrite that removed a lot of the surface area v1 accumulated1. If you are starting today you want v2, and you should know before adopting that v2 is intentionally minimal: several things v1 shipped (logger adapters, OpenTracing, ctxtags, the chaining helper) were deleted rather than ported, on the reasoning that gRPC-Go or OpenTelemetry now own them.
The defining tension is scope. The maintainers are explicit that this is not a batteries-included framework — the README tells you it is "ok to copy some simpler interceptors" and that "this repo can't support all the edge cases you might have"2. That honesty is the product: you get small, composable, low-dependency pieces, and you are expected to write the glue (logging adapters especially) yourself.
Getting Started
The main module is v2, so the import path ends in /v2:
go get github.com/grpc-ecosystem/go-grpc-middleware/v2
import (
"google.golang.org/grpc"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/selector"
)
srv := grpc.NewServer(
grpc.ChainUnaryInterceptor(
logging.UnaryServerInterceptor(myLogger),
selector.UnaryServerInterceptor(
auth.UnaryServerInterceptor(authFn),
selector.MatchFunc(allButHealthZ), // skip auth on health checks
),
recovery.UnaryServerInterceptor(), // keep panic recovery last
),
)
myLogger must satisfy logging.Logger, a one-method interface you implement over your logging library. The repo ships copy-paste example adapters for slog, zap, zerolog, logrus, logr, go-kit and the stdlib log, but they are examples to copy, not packages to import1.
Architecture / How It Works
A gRPC interceptor is just a function. Server-side unary interceptors have the signature func(ctx, req, info, handler) (resp, error); you do work, call handler(ctx, req), and do more work on the way out. Streaming, and both client sides, have analogous signatures. Everything here is built on those four shapes plus grpc.ChainUnaryInterceptor / grpc.ChainStreamInterceptor, which gRPC-Go added natively — so v2 removed its own chaining helper1.
Two structural decisions shape the codebase:
- Multi-module repository. The core
interceptors/*packages live in the v2 module and are kept "ultra slim" on dependencies. Anything that would pull in a heavy dependency lives underproviders/as a separate Go module with independent (v1-line) versioning — most notablyproviders/prometheus, absorbed from the now-deprecatedgo-grpc-prometheus3. The upside is that importing the retry interceptor doesn't drag Prometheus into yourgo.mod; the cost is thatproviders/*versions move independently of the mainv2.x.yline and are easy to miss when upgrading. - The
Reporterinterface. Generic observability interceptors are built on a smallReporterabstraction (interceptors/reporter.go), so metrics/logging-style middleware share one code path for post-call reporting rather than each re-implementing timing and status extraction.
The most important non-obvious piece is selector. In v1, most interceptors took a "decider" function (WithDecider, keyed on the full method name) to conditionally apply. v2 deleted all deciders and replaced them with one interceptor, selector, that wraps another interceptor and a MatchFunc1. This is cleaner but it means the conditional-application pattern is now external to each interceptor — a real migration gotcha for anyone porting v1 config.
Production Notes
- Interceptor order is load-bearing. Chains run in the order given.
recoverymust be last (innermost) or a panic in a later interceptor bypasses it — the README calls this out explicitly2. Auth generally goes before logging so failed-auth requests are still logged; context-injecting interceptors (e.g.logging.InjectFields, trace-ID injection) must be chained before the code that reads those fields. - Logging is BYO adapter. There is no importable logger package by design. You implement the
logging.Loggerinterface once. Teams migrating from v1 are often surprised thatgrpc_zap/grpc_logrusno longer exist as packages — you copy the example and own it. - Provider module versioning.
providers/prometheusis versioned asproviders/prometheus/vX.Y.Z, not in lockstep withv2.x.y. Pin it explicitly ingo.modand check its tags separately; ago get -uof the main module will not bump it. - Retries have a native competitor. The
retryinterceptor predates and overlaps gRPC-Go's built-in retry policy (configured via service config /MethodConfig). For simple response-code retries the native mechanism may be enough and avoids a dependency; the interceptor is useful when you need per-call programmatic control. The README itself points at grpc-go's native retries2. - Validation split. The old proto-option
validator(codegen from.proto) coexists with a newerprotovalidateinterceptor built on Buf's protovalidate-go, which is where the ecosystem is heading; new services should generally preferprotovalidate. - Tracing lives elsewhere now. v2 removed the OpenTracing interceptor. Use the official OpenTelemetry
otelgrpcStatsHandler instead — it is aStatsHandler, not an interceptor, so it wires in viagrpc.StatsHandler(...), not the chain1.
When to Use / When Not
Use when:
- You run gRPC-Go services and want vetted, low-dependency interceptors for auth, logging, recovery, retries, timeouts, rate limiting or validation.
- You want fine-grained per-method control via
selectorrather than one global policy. - You value slim dependency graphs and are willing to write your own logging adapter.
Avoid / reconsider when:
- You only need metrics and tracing — the official OpenTelemetry
otelgrpcStatsHandler covers those without this repo. - You want a turnkey, batteries-included framework: the maintainers explicitly don't aim for that and suggest copying code for edge cases2.
- You're on gRPC-Go's native retry policy and don't need programmatic retry control — you may not need the
retryinterceptor at all.
Alternatives
- grpc/grpc-go — the base library already provides native interceptor chaining, a retry policy, and an authz interceptor; reach here first before adding a dependency.
- open-telemetry/opentelemetry-go-contrib — use
otelgrpcinstead of this repo's (removed) tracing and, arguably, its metrics, when you're standardized on OpenTelemetry. - bufbuild/protovalidate-go — the validation direction this repo's
protovalidateinterceptor wraps; use directly when you want validation outside the interceptor. - grpc-ecosystem/go-grpc-prometheus — the original Prometheus interceptor, now deprecated and folded into
providers/prometheushere; use the provider module, not the old repo. - grpc-ecosystem/grpc-gateway — sibling project; different problem (REST↔gRPC transcoding), not a substitute, but the org you're already in.
History
| Version | Date | Notes |
|---|---|---|
| v1.x | ~2015–2016 | Original interceptor collection; logger adapters, ctxtags, OpenTracing, deciders, own chaining helper. Now frozen. |
| v2 (dev) | 2020 onward | Rewrite: multi-module layout, Reporter interface, deciders replaced by selector, loggers/ctxtags/opentracing removed, new proto API1. |
| v2.x.y | ongoing | Active main line; providers/* submodules versioned independently3. |
Repository created 2016-05-14; still actively maintained (last push 2026-03).4
References
- ^ "Changes compared to v1", project README — enumerates the v2 removals (loggers, opentracing, ctxtags, deciders, chaining) and the multi-module rationale. https://github.com/grpc-ecosystem/go-grpc-middleware#changes-compared-to-v1
- ^ Project README, "Middleware" section — maintainers' explicit scope note ("ok to copy… can't support all edge cases") and recovery-last guidance. https://github.com/grpc-ecosystem/go-grpc-middleware
- ^ README, "Structure of this repository" —
providers/as separate Go modules;providers/prometheusabsorbed from the deprecated go-grpc-prometheus. https://github.com/grpc-ecosystem/go-grpc-middleware#structure-of-this-repository - ^ GitHub repository metadata (stars, forks, license, last-push), retrieved 2026-07. https://github.com/grpc-ecosystem/go-grpc-middleware
Tags
go, grpc, middleware, interceptor, authentication, logging, observability, retries, rate-limiting, validation, microservices, apache-2.0