implgen is a fork of uber-go/mock that extends mockgen with additional code generation modes beyond mocks: tracing decorators (OpenTelemetry) and metrics decorators (Prometheus).
In addition to the standard mock mode, mockgen in this fork can generate:
- Tracing wrappers (
-implementation_type=trace): generates a struct that wraps your interface and automatically starts/ends an OpenTelemetry span per method. Methods whose first argument iscontext.Contextget full span lifecycle management and error recording. - Metrics wrappers (
-implementation_type=metrics): generates a struct that wraps your interface and records an OpenTelemetryFloat64Histogramof call duration per method. Exports to Prometheus (or any OTel backend) via the standard OTel exporter — no go-kit or direct Prometheus dependency required. - Logging wrappers (
-implementation_type=log): generates a struct that wraps your interface and logs every call and its outcome using*slog.Logger. Context methods use context-aware log variants.
go install github.com/pableeee/implgen/mockgen@latestmockgen has two modes of operation: source and package.
mockgen -source=foo.go [other options]mockgen database/sql/driver Conn,Driver
# Convenient for go:generate
mockgen . Conn,DriverSelect the generated output type with -implementation_type:
Generates a gomock-based mock with an EXPECT() recorder for use in tests.
mockgen -source=foo.go -destination=mock_foo.go
# or explicitly:
mockgen -source=foo.go -destination=mock_foo.go -implementation_type=mockGenerates a struct that wraps the interface and instruments each method with an OpenTelemetry span. Methods taking context.Context as their first argument propagate the span through context and record errors automatically.
mockgen -source=foo.go -destination=trace_foo.go -implementation_type=traceGenerated output for an interface Foo:
// TracedFooImpl is a tracing decorator of Foo interface.
type TracedFooImpl struct {
delegate Foo
extraAttrs func(method string, args ...any) []attribute.KeyValue
}
func NewTracedFooImpl(delegate Foo, extraAttrs func(method string, args ...any) []attribute.KeyValue) *TracedFooImpl
func (t *TracedFooImpl) DoSomething(ctx context.Context, arg string) error {
tracer := otel.Tracer("Foo")
ctx, span := tracer.Start(ctx, "DoSomething")
defer span.End()
if t.extraAttrs != nil {
span.SetAttributes(t.extraAttrs("DoSomething", ctx, arg)...)
}
ret := t.delegate.DoSomething(ctx, arg)
if ret != nil {
span.RecordError(ret)
span.SetStatus(codes.Error, ret.Error())
}
return ret
}Pass nil as extraAttrs to disable custom attributes. When non-nil, use the method argument to dispatch per-method logic:
traced := NewTracedFooImpl(realFoo, func(method string, args ...any) []attribute.KeyValue {
switch method {
case "DoSomething":
return []attribute.KeyValue{attribute.String("arg", args[1].(string))}
}
return nil
})Generates a struct that wraps the interface and records an OpenTelemetry Float64Histogram of call duration per method, with a built-in error attribute (true/false). Because the backend is OTel, you can export to Prometheus via the OTel Prometheus exporter, or to any other OTel-compatible backend, without extra dependencies.
mockgen -source=foo.go -destination=metrics_foo.go -implementation_type=metricsGenerated output for an interface Foo:
// MetricsFooImpl is a metrics decorator of Foo interface.
type MetricsFooImpl struct {
doSomethingDuration metric.Float64Histogram
delegate Foo
extraAttrs func(method string, args ...any) []attribute.KeyValue
}
// Returns (*MetricsFooImpl, error) because OTel histogram registration can fail.
func NewMetricsFooImpl(delegate Foo, extraAttrs func(method string, args ...any) []attribute.KeyValue) (*MetricsFooImpl, error)The extraAttrs func has the same signature as in the tracing decorator, so the same function can be reused for both. Pass nil to disable custom attributes:
m, err := NewMetricsFooImpl(realFoo, func(method string, args ...any) []attribute.KeyValue {
switch method {
case "DoSomething":
return []attribute.KeyValue{attribute.String("region", args[1].(string))}
}
return nil
})To export to Prometheus, wire the OTel Prometheus exporter once at startup — no changes to the generated code needed:
exporter, _ := otelprom.New()
provider := metric.NewMeterProvider(metric.WithReader(exporter))
otel.SetMeterProvider(provider)
http.Handle("/metrics", promhttp.Handler())Generates a struct that wraps the interface and logs every method call and its outcome using *slog.Logger. Context-aware methods use InfoContext/ErrorContext; error returns are logged at Error level with the error value as a structured attribute.
mockgen -source=foo.go -destination=log_foo.go -implementation_type=log// Pass nil to fall back to slog.Default().
logged := NewLoggedFooImpl(realFoo, slog.New(slog.NewJSONHandler(os.Stdout, nil)))All upstream flags are supported. Fork-specific additions:
-implementation_type: Type of code to generate:mock(default),trace,metrics, orlog.
Standard flags:
-source: Input Go source file (source mode).-destination: Output file; defaults to stdout.-package: Package name for generated code; defaults to<type>_+ input package name.-interfaces: (source mode) Comma-separated list of interfaces to generate; defaults to all.-imports: (source mode) Comma-separatedname=pathpairs of explicit imports.-aux_files: (source mode) Comma-separatedpkg=file.gopairs of auxiliary source files.-build_flags: (package mode) Flags passed verbatim togo list.-mock_names: Comma-separatedInterface=MockNamepairs to override generated mock names.-self_package: Full import path for generated code, used to prevent import cycles.-copyright_file: File whose contents are prepended as a copyright header.-build_constraint: If non-empty, added as//go:build <constraint>to generated files.-typed: Generate type-safeReturn,Do,DoAndReturnfunctions (default false).-exclude_interfaces: (source mode) Comma-separated interface names to skip.-write_package_comment: Write package godoc comment (default true).-write_generate_directive: Add//go:generatedirective to regenerate (default false).-write_source_comment: Write source file or interface names as comment (default true).
type Foo interface {
Bar(x int) int
}
func TestFoo(t *testing.T) {
ctrl := gomock.NewController(t)
m := NewMockFoo(ctrl)
m.EXPECT().Bar(gomock.Eq(99)).Return(101)
SUT(m)
}For full gomock documentation see the upstream README.
This project is a fork of uber-go/mock, which itself originated from Google's golang/mock.