tslog is a structured logging module for the Tinh Tinh framework, built on top of Go's standard log/slog package. It integrates slog as a first-class provider within the Tinh Tinh dependency injection system and exposes a flexible middleware for HTTP request logging.
- π DI-friendly β Register any
slog.Handleras a module-level provider usingForRoot - π Easy injection β Retrieve the logger anywhere via
Inject(module) - π£οΈ HTTP middleware β Log every incoming request with a customisable function
- βοΈ Skip paths β Exclude health-check or any other routes from logging
- π Context-aware β Use
slog.Logger.InfoContexttogether with request context for trace/request-ID propagation
| Dependency | Version |
|---|---|
| Go | β₯ 1.24 |
github.com/tinh-tinh/tinhtinh |
v2 |
go get github.com/tinh-tinh/tslogPass any standard slog.Handler to ForRoot. The logger is registered as a named provider (TSLOG) and made available across all imported modules.
package main
import (
"log/slog"
"os"
"github.com/tinh-tinh/tinhtinh/v2/core"
"github.com/tinh-tinh/tslog"
)
func main() {
appModule := func() core.Module {
return core.NewModule(core.NewModuleOptions{
Imports: []core.Modules{
tslog.ForRoot(slog.NewJSONHandler(os.Stdout, nil)),
},
Controllers: []core.Controllers{userController},
})
}
app := core.CreateFactory(appModule)
app.Listen(3000)
}Inside any controller or service, call tslog.Inject(module) to obtain the *slog.Logger.
func userController(module core.Module) core.Controller {
ctrl := module.NewController("users")
logger := tslog.Inject(module)
ctrl.Get("", func(ctx core.Ctx) error {
logger.Info("Request processed",
"http", slog.Group("request",
"method", "GET",
"path", "/api/users",
"status", 200,
),
"duration_ms", 42,
)
return ctx.JSON(map[string]any{"ok": true})
})
return ctrl
}tslog.Middleware wraps a user-supplied function (Fnc) that receives the request context. Use it to log method, path, latency, or any attribute you need.
loggerMiddleware := tslog.Middleware(tslog.MiddlewareOptions{
Fnc: func(ctx core.Ctx) {
logger := ctx.Ref(tslog.TSLOG).(*slog.Logger)
logger.Info("Incoming request",
slog.Group("http",
slog.Group("request",
"method", ctx.Req().Method,
"path", ctx.Req().URL.Path,
),
),
)
},
})
appModule := func() core.Module {
return core.NewModule(core.NewModuleOptions{
Imports: []core.Modules{tslog.ForRoot(slog.NewJSONHandler(os.Stdout, nil))},
Controllers: []core.Controllers{userController},
Middlewares: []core.Middleware{loggerMiddleware},
})
}Supply SkipPaths to prevent certain routes (e.g., health-check endpoints) from being logged.
loggerMiddleware := tslog.Middleware(tslog.MiddlewareOptions{
SkipPaths: []string{"/health", "/readyz"},
Fnc: func(ctx core.Ctx) {
logger := ctx.Ref(tslog.TSLOG).(*slog.Logger)
logger.Info("Incoming request",
"method", ctx.Req().Method,
"path", ctx.Req().URL.Path,
)
},
})Combine tslog with a custom slog.Handler that reads values from the request context to automatically attach trace or request IDs to every log line.
const reqIDKey = "req_id"
// ContextHandler enriches each log record with the request ID stored in ctx.
type ContextHandler struct{ slog.Handler }
func (h ContextHandler) Handle(ctx context.Context, r slog.Record) error {
if reqID, ok := ctx.Value(reqIDKey).(string); ok {
r.AddAttrs(slog.String("req_id", reqID))
}
return h.Handler.Handle(ctx, r)
}
// traceMiddleware assigns a unique request ID and stores it in context.
traceMiddleware := func(ctx core.Ctx) error {
ctx.Set(reqIDKey, rand.Text())
return ctx.Next()
}
// loggerMiddleware logs using the enriched context.
loggerMiddleware := tslog.Middleware(tslog.MiddlewareOptions{
Fnc: func(ctx core.Ctx) {
logger := ctx.Ref(tslog.TSLOG).(*slog.Logger)
logger.InfoContext(ctx.Req().Context(), "API Request")
},
})
appModule := func() core.Module {
base := slog.NewJSONHandler(os.Stdout, nil)
return core.NewModule(core.NewModuleOptions{
Imports: []core.Modules{tslog.ForRoot(ContextHandler{Handler: base})},
Controllers: []core.Controllers{userController},
Middlewares: []core.Middleware{traceMiddleware, loggerMiddleware},
})
}Every log line produced inside a request will now contain a req_id field automatically.
Registers a new *slog.Logger (backed by h) as the TSLOG provider in the module. Call this once in your root module's Imports.
Retrieves the *slog.Logger registered by ForRoot. Returns nil if ForRoot was not imported.
Returns a Tinh Tinh middleware that:
- Skips execution for any path listed in
SkipPaths. - Calls
Fnc(ctx)for all other requests.
| Field | Type | Description |
|---|---|---|
SkipPaths |
[]string |
URL paths that bypass the middleware |
Fnc |
func(ctx core.Ctx) |
Logging logic executed per request |
A core.Provide constant ("TS_LOG") used as the DI token. Use ctx.Ref(tslog.TSLOG) inside middleware to retrieve the logger.
go test -cover ./...The CI pipeline runs tests against Go 1.24, 1.25, and 1.26, with coverage reports uploaded to Codecov.
Distributed under the MIT License. See LICENSE for details.