Frequently Asked Questions (FAQ)
How to set 404 Not Found
handler?
Use WithNotFound
option:
h := myHandler{}
srv, err := api.NewServer(h, api.WithNotFound(func(w http.ResponseWriter, r *http.Request) {
_, _ = io.WriteString(w, `{"error": "not found"}`)
}))
if err != nil {
panic(err)
}
See Static router section for more details about router errors.
How to set error handler?
Use WithErrorHandler
option:
h := myHandler{}
srv, err := api.NewServer(h, api.WithErrorHandler(func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
var (
code = http.StatusInternalServerError
ogenErr ogenerrors.Error
)
switch {
case errors.Is(err, ht.ErrNotImplemented):
code = http.StatusNotImplemented
case errors.As(err, &ogenErr):
code = ogenErr.Code()
}
_, _ = io.WriteString(w, http.StatusText(code))
}))
if err != nil {
panic(err)
}
Also, check out the Convenient errors concept.
How to use net/http
middlewares?
The Server
type implements http.Handler
interface, so you can use any net/http
middleware with it.
type Middleware func(next http.Handler) http.Handler
func Wrap(h http.Handler, mw Middleware) http.Handler {
return mw(h)
}
// TraceMiddleware adds traces.
func TraceMiddleware(lg *zap.Logger, tp trace.TracerProvider) middleware.Middleware {
return func(next http.Handler) http.Handler {
t := tp.Tracer("http")
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
start := time.Now()
w := &writerProxy{ResponseWriter: rw}
ctx, span := t.Start(r.Context(), "HandleRequest")
defer span.End()
spanCtx := span.SpanContext()
ctx = With(ctx, lg.With(
zap.String("trace_id", spanCtx.TraceID().String()),
zap.String("span_id", spanCtx.SpanID().String()),
))
defer func() {
if r := recover(); r != nil {
lg.Error("Panic", zap.Stack("stack"))
if w.status == 0 {
w.WriteHeader(http.StatusInternalServerError)
}
span.AddEvent("Panic recovered",
trace.WithStackTrace(true),
)
span.SetStatus(codes.Error, "Panic recovered")
}
lg.Debug("Request",
zap.Duration("duration", time.Since(start)),
zap.Int("http.status", w.status),
zap.Int64("http.response.size", w.wrote),
zap.String("http.path", r.URL.String()),
zap.String("http.method", r.Method),
)
}()
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
func newServer() *http.Server {
h, err := oas.NewServer(h,
oas.WithMeterProvider(m.MeterProvider()),
oas.WithTracerProvider(m.TracerProvider()),
)
if err != nil {
return errors.Wrap(err, "oas server")
}
return &http.Server{
Handler: middleware.Wrap(h, TraceMiddleware(lg, m.TracerProvider())),
Addr: addr,
}
}
See Static router section for more details.
How to use Prometheus?
See OpenTelemetry Go SDK examples.
Also, see our autometer
helper.
Example setup:
package main
import (
"context"
"example/internal/oas"
"github.com/prometheus/client_golang/prometheus"
otelprometheus "go.opentelemetry.io/otel/exporters/prometheus"
"go.opentelemetry.io/otel/metric"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
const appName = "my-ogen-service"
// setupMeterProvider setups Prometheus instrumentation.
func setupMeterProvider(reg prometheus.Registerer) (metric.MeterProvider, ShutdownFunc, error) {
// Create an OpenTelemetry Resource description.
//
// See https://opentelemetry.io/docs/languages/go/resources/
appResource, err := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String(appName),
),
)
if err != nil {
return nil, nil, err
}
// Create Prometheus exporter and given registerer.
exp, err := otelprometheus.New(
otelprometheus.WithRegisterer(reg),
)
if err != nil {
return nil, nil, err
}
mp := sdkmetric.NewMeterProvider(
sdkmetric.WithResource(appResource),
sdkmetric.WithReader(exp),
)
return mp, func(ctx context.Context) error {
// Flush collected metrics.
_ = mp.ForceFlush(ctx)
return mp.Shutdown(ctx)
}, nil
}
type ShutdownFunc = func(ctx context.Context) error
func createAPIServer(h oas.Handler) (_ *oas.Server, _ ShutdownFunc, rerr error) {
meterProvider, shutdown, err := setupMeterProvider(prometheus.DefaultRegisterer)
if err != nil {
return nil, nil, err
}
defer func() {
// Shutdown MeterProvider and release resources in case of error.
if rerr != nil {
shutdown(context.TODO())
}
}()
server, err := oas.NewServer(h, oas.WithMeterProvider(meterProvider))
if err != nil {
return nil, nil, err
}
// Returned `shutdown` function would stop metric collection.
return server, shutdown, nil
}
How to add some tags to generated structures?
Use x-oapi-codegen-extra-tags
extension.
How to disable OpenTelemetry integration?
Disable ogen/otel
feature via config:
.ogen.yml
generator:
features:
disable:
- "ogen/otel"