internal logging library

This commit is contained in:
2026-01-12 23:21:52 -07:00
parent ee2808f8a1
commit 851234d9fa
18 changed files with 349 additions and 159 deletions
-7
View File
@@ -1,7 +0,0 @@
package consts
import "log/slog"
const (
LevelTrace slog.Level = slog.LevelDebug - 4
)
+3 -3
View File
@@ -7,8 +7,8 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"log/slog"
"ruben/inventory2/internal/consts" "ruben/inventory2/internal/consts"
"ruben/inventory2/internal/logging"
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/pgxpool"
@@ -16,7 +16,7 @@ import (
type ( type (
Store struct { Store struct {
log *slog.Logger log *logging.Logger
db *pgxpool.Pool db *pgxpool.Pool
} }
@@ -61,7 +61,7 @@ type (
} }
) )
func NewStore(logger *slog.Logger, db *pgxpool.Pool) *Store { func NewStore(logger *logging.Logger, db *pgxpool.Pool) *Store {
return &Store{ return &Store{
log: logger, log: logger,
db: db, db: db,
+3 -3
View File
@@ -5,7 +5,6 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"log/slog"
"net/url" "net/url"
"time" "time"
@@ -16,6 +15,7 @@ import (
"golang.org/x/oauth2" "golang.org/x/oauth2"
"ruben/inventory2/internal/consts" "ruben/inventory2/internal/consts"
"ruben/inventory2/internal/logging"
) )
// TODO: move these to a config? // TODO: move these to a config?
@@ -37,7 +37,7 @@ const (
type ( type (
// Authenticator is used to authenticate our users. // Authenticator is used to authenticate our users.
Authenticator struct { Authenticator struct {
log *slog.Logger log *logging.Logger
*oidc.Provider *oidc.Provider
oauth2.Config oauth2.Config
db *pgxpool.Pool db *pgxpool.Pool
@@ -64,7 +64,7 @@ type (
func New( func New(
ctx context.Context, ctx context.Context,
db *pgxpool.Pool, db *pgxpool.Pool,
logger *slog.Logger, logger *logging.Logger,
) (*Authenticator, error) { ) (*Authenticator, error) {
provider, err := oidc.NewProvider( provider, err := oidc.NewProvider(
ctx, ctx,
+3 -3
View File
@@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"log/slog"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
@@ -18,6 +17,7 @@ import (
"github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/pgxpool"
"ruben/inventory2/internal/domains/platforms/etsy/generated_client" "ruben/inventory2/internal/domains/platforms/etsy/generated_client"
"ruben/inventory2/internal/logging"
) )
//go:generate oapi-codegen -generate types,client -package generated_client -o generated_client/client.go openapi.3.0.2.json //go:generate oapi-codegen -generate types,client -package generated_client -o generated_client/client.go openapi.3.0.2.json
@@ -33,7 +33,7 @@ import (
type ( type (
Platform struct { Platform struct {
log *slog.Logger log *logging.Logger
oAuthRedirectURI func(acctID int64) string oAuthRedirectURI func(acctID int64) string
apiKeystring string apiKeystring string
apiSharedSecret string apiSharedSecret string
@@ -65,7 +65,7 @@ const (
scopeTransactionsWrite = "transactions_w" // Update a member's sales data. scopeTransactionsWrite = "transactions_w" // Update a member's sales data.
) )
func NewPlatform(logger *slog.Logger, oAuthRedirectURI func(acctID int64) string, apiKeystring, apiSharedSecret string, db *pgxpool.Pool) *Platform { func NewPlatform(logger *logging.Logger, oAuthRedirectURI func(acctID int64) string, apiKeystring, apiSharedSecret string, db *pgxpool.Pool) *Platform {
return &Platform{ return &Platform{
log: logger, log: logger,
oAuthRedirectURI: oAuthRedirectURI, oAuthRedirectURI: oAuthRedirectURI,
+3 -3
View File
@@ -4,7 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog" "ruben/inventory2/internal/logging"
"time" "time"
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
@@ -14,7 +14,7 @@ import (
type ( type (
Store struct { Store struct {
log *slog.Logger log *logging.Logger
db *pgxpool.Pool db *pgxpool.Pool
} }
@@ -27,7 +27,7 @@ type (
} }
) )
func NewStore(logger *slog.Logger, db *pgxpool.Pool) *Store { func NewStore(logger *logging.Logger, db *pgxpool.Pool) *Store {
return &Store{ return &Store{
log: logger, log: logger,
db: db, db: db,
+180
View File
@@ -0,0 +1,180 @@
package logging
import (
"context"
"fmt"
"log/slog"
"runtime"
"time"
)
// Logger creates a *slog.Logger wrap with a few more methods wrapped on top.
// It recreates a number of methods to allow replacing a *slog.Logger functionally.
// It also implements a number of methods to support formatted messages and
// tracing.
type Logger struct {
*slog.Logger
}
func New(h slog.Handler) *Logger {
return &Logger{
Logger: slog.New(h),
}
}
func From(l *slog.Logger) *Logger {
return &Logger{
Logger: l,
}
}
func (l *Logger) With(args ...any) *Logger {
return From(l.Logger.With(args...))
}
func (l *Logger) WithGroup(name string) *Logger {
return From(l.Logger.WithGroup(name))
}
func (l *Logger) Debugf(format string, args ...any) {
l.logContextf(context.Background(), slog.LevelDebug, format, args...)
}
func (l *Logger) DebugContextf(ctx context.Context, format string, args ...any) {
l.logContextf(ctx, slog.LevelDebug, format, args...)
}
func (l *Logger) DebugDeferf(format string, args ...any) func(func() (string, []any)) {
return l.logContextDeferf(context.Background(), slog.LevelDebug, format, args...)
}
func (l *Logger) DebugContextDeferf(ctx context.Context, format string, args ...any) func(func() (string, []any)) {
return l.logContextDeferf(ctx, slog.LevelDebug, format, args...)
}
func (l *Logger) DebugCallf(method string) func() {
return l.logContextCallf(context.Background(), slog.LevelDebug, method)
}
func (l *Logger) DebugContextCallf(ctx context.Context, method string) func() {
return l.logContextCallf(ctx, slog.LevelDebug, method)
}
func (l *Logger) Errorf(format string, args ...any) {
l.logContextf(context.Background(), slog.LevelError, format, args...)
}
func (l *Logger) ErrorContextf(ctx context.Context, format string, args ...any) {
l.logContextf(ctx, slog.LevelError, format, args...)
}
func (l *Logger) ErrorDeferf(format string, args ...any) func(func() (string, []any)) {
return l.logContextDeferf(context.Background(), slog.LevelError, format, args...)
}
func (l *Logger) ErrorContextDeferf(ctx context.Context, format string, args ...any) func(func() (string, []any)) {
return l.logContextDeferf(ctx, slog.LevelError, format, args...)
}
func (l *Logger) ErrorCallf(method string) func() {
return l.logContextCallf(context.Background(), slog.LevelError, method)
}
func (l *Logger) ErrorContextCallf(ctx context.Context, method string) func() {
return l.logContextCallf(ctx, slog.LevelError, method)
}
func (l *Logger) Infof(format string, args ...any) {
l.logContextf(context.Background(), slog.LevelInfo, format, args...)
}
func (l *Logger) InfoContextf(ctx context.Context, format string, args ...any) {
l.logContextf(ctx, slog.LevelInfo, format, args...)
}
func (l *Logger) InfoDeferf(format string, args ...any) func(func() (string, []any)) {
return l.logContextDeferf(context.Background(), slog.LevelInfo, format, args...)
}
func (l *Logger) InfoContextDeferf(ctx context.Context, format string, args ...any) func(func() (string, []any)) {
return l.logContextDeferf(ctx, slog.LevelInfo, format, args...)
}
func (l *Logger) InfoCallf(method string) func() {
return l.logContextCallf(context.Background(), slog.LevelInfo, method)
}
func (l *Logger) InfoContextCallf(ctx context.Context, method string) func() {
return l.logContextCallf(ctx, slog.LevelInfo, method)
}
func (l *Logger) Warnf(format string, args ...any) {
l.logContextf(context.Background(), slog.LevelWarn, format, args...)
}
func (l *Logger) WarnContextf(ctx context.Context, format string, args ...any) {
l.logContextf(ctx, slog.LevelWarn, format, args...)
}
func (l *Logger) WarnDeferf(format string, args ...any) func(func() (string, []any)) {
return l.logContextDeferf(context.Background(), slog.LevelWarn, format, args...)
}
func (l *Logger) WarnContextDeferf(ctx context.Context, format string, args ...any) func(func() (string, []any)) {
return l.logContextDeferf(ctx, slog.LevelWarn, format, args...)
}
func (l *Logger) WarnCallf(method string) func() {
return l.logContextCallf(context.Background(), slog.LevelWarn, method)
}
func (l *Logger) WarnContextCallf(ctx context.Context, method string) func() {
return l.logContextCallf(ctx, slog.LevelWarn, method)
}
func (l *Logger) logContextf(ctx context.Context, lvl slog.Level, format string, args ...any) {
if !l.Enabled(ctx, slog.LevelInfo) {
return
}
var pcs [1]uintptr
runtime.Callers(3, pcs[:]) // skip [Callers, Infof]
_ = l.Handler().Handle(
ctx,
slog.NewRecord(time.Now(), lvl, fmt.Sprintf(format, args...), pcs[0]),
)
}
func (l *Logger) logContextCallf(ctx context.Context, lvl slog.Level, method string) func() {
fn := l.With("method", method).logContextDeferf(ctx, lvl, "%s called", method)
return func() {
fn(func() (string, []any) {
return "%s returned", []any{method}
})
}
}
func (l *Logger) logContextDeferf(ctx context.Context, lvl slog.Level, format string, args ...any) func(func() (msg string, args []any)) {
if !l.Enabled(ctx, slog.LevelInfo) {
return func(func() (string, []any)) {
}
}
var pcs [1]uintptr
runtime.Callers(3, pcs[:]) // skip [Callers, Infof]
pc := pcs[0]
_ = l.Handler().Handle(ctx, slog.NewRecord(time.Now(), lvl, fmt.Sprintf(format, args...), pc))
return func(deferred func() (newFormat string, moreArgs []any)) {
if !l.Enabled(ctx, slog.LevelInfo) {
return
}
if deferred != nil {
format, args = deferred()
}
_ = l.Handler().Handle(ctx, slog.NewRecord(time.Now(), lvl, fmt.Sprintf(format, args...), pc))
}
}
+3 -3
View File
@@ -3,10 +3,10 @@ package accounts
import ( import (
"errors" "errors"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"ruben/inventory2/internal/consts" "ruben/inventory2/internal/consts"
"ruben/inventory2/internal/domains/accounts" "ruben/inventory2/internal/domains/accounts"
"ruben/inventory2/internal/logging"
"ruben/inventory2/internal/server/middleware" "ruben/inventory2/internal/server/middleware"
"ruben/inventory2/internal/server/response" "ruben/inventory2/internal/server/response"
"ruben/inventory2/internal/server/router" "ruben/inventory2/internal/server/router"
@@ -14,13 +14,13 @@ import (
) )
type accountSubrouter struct { type accountSubrouter struct {
log *slog.Logger log *logging.Logger
*router.SubMux *router.SubMux
accts *accounts.Store accts *accounts.Store
} }
func NewAccountSubrouter( func NewAccountSubrouter(
logger *slog.Logger, logger *logging.Logger,
accts *accounts.Store, accts *accounts.Store,
authMiddleware *middleware.Auth, authMiddleware *middleware.Auth,
) *accountSubrouter { ) *accountSubrouter {
+4 -4
View File
@@ -3,21 +3,21 @@ package auth
import ( import (
"context" "context"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"ruben/inventory2/internal/domains/authentication" "ruben/inventory2/internal/domains/authentication"
"ruben/inventory2/internal/logging"
"ruben/inventory2/internal/server/cookies" "ruben/inventory2/internal/server/cookies"
"ruben/inventory2/internal/server/response" "ruben/inventory2/internal/server/response"
"ruben/inventory2/internal/server/router" "ruben/inventory2/internal/server/router"
) )
type loginSubrouter struct { type loginSubrouter struct {
log *slog.Logger log *logging.Logger
auth *authentication.Authenticator auth *authentication.Authenticator
router.Subrouter router.Subrouter
} }
func NewLoginSubrouter(logger *slog.Logger, auth *authentication.Authenticator) *loginSubrouter { func NewLoginSubrouter(logger *logging.Logger, auth *authentication.Authenticator) *loginSubrouter {
mux := router.NewSubMux(logger) mux := router.NewSubMux(logger)
ls := &loginSubrouter{ ls := &loginSubrouter{
@@ -33,7 +33,7 @@ func NewLoginSubrouter(logger *slog.Logger, auth *authentication.Authenticator)
return ls return ls
} }
func (s *loginSubrouter) newLoginSubrouter(logger *slog.Logger) router.Subrouter { func (s *loginSubrouter) newLoginSubrouter(logger *logging.Logger) router.Subrouter {
mux := router.NewSubMux(logger) mux := router.NewSubMux(logger)
ls := &loginSubrouter{ ls := &loginSubrouter{
+3 -6
View File
@@ -3,7 +3,6 @@ package templates
import ( import (
"errors" "errors"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@@ -15,6 +14,7 @@ import (
"ruben/inventory2/internal/domains/accounts" "ruben/inventory2/internal/domains/accounts"
etsy_platform "ruben/inventory2/internal/domains/platforms/etsy" etsy_platform "ruben/inventory2/internal/domains/platforms/etsy"
"ruben/inventory2/internal/domains/raw_events" "ruben/inventory2/internal/domains/raw_events"
"ruben/inventory2/internal/logging"
"ruben/inventory2/internal/server/middleware" "ruben/inventory2/internal/server/middleware"
"ruben/inventory2/internal/server/response" "ruben/inventory2/internal/server/response"
"ruben/inventory2/internal/server/router" "ruben/inventory2/internal/server/router"
@@ -23,7 +23,7 @@ import (
) )
type webpageRouter struct { type webpageRouter struct {
log *slog.Logger log *logging.Logger
contentDir string contentDir string
templater *templater.Templater templater *templater.Templater
rawEvents *raw_events.Store rawEvents *raw_events.Store
@@ -33,7 +33,7 @@ type webpageRouter struct {
} }
func NewWebpageRouter( func NewWebpageRouter(
logger *slog.Logger, logger *logging.Logger,
contentDir string, contentDir string,
tmpl *templater.Templater, tmpl *templater.Templater,
rawEvents *raw_events.Store, rawEvents *raw_events.Store,
@@ -66,8 +66,6 @@ func NewWebpageRouter(
// compiles the page template or component template matching the url // compiles the page template or component template matching the url
// func (s *Server) serveTemplates(r *http.Request) (response.Response, error) { // func (s *Server) serveTemplates(r *http.Request) (response.Response, error) {
func (s *webpageRouter) serveTemplates(r *http.Request) (response.Response, error) { func (s *webpageRouter) serveTemplates(r *http.Request) (response.Response, error) {
fmt.Println("webpageRouter.serveTemplates:", r.URL)
name, args := s.getTemplateNameAndArgs(r, s.contentDir+"/templates/component_bodies") name, args := s.getTemplateNameAndArgs(r, s.contentDir+"/templates/component_bodies")
b, err := s.templater.ExecuteComponentBody(name, args...) b, err := s.templater.ExecuteComponentBody(name, args...)
@@ -90,7 +88,6 @@ func (s *webpageRouter) serveTemplates(r *http.Request) (response.Response, erro
// func (s *Server) getTemplateNameAndArgs(r *http.Request, templateDir string) (name string, args []any) { // func (s *Server) getTemplateNameAndArgs(r *http.Request, templateDir string) (name string, args []any) {
func (s *webpageRouter) getTemplateNameAndArgs(r *http.Request, templateDir string) (name string, args []any) { func (s *webpageRouter) getTemplateNameAndArgs(r *http.Request, templateDir string) (name string, args []any) {
fmt.Println("webpageRouter.getTemplateNameAndArgs:", r.URL)
ctx := r.Context() ctx := r.Context()
name, pathParams := getTemplateNameForURL(r.URL, templateDir) name, pathParams := getTemplateNameForURL(r.URL, templateDir)
@@ -3,18 +3,18 @@ package etsy
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
"ruben/inventory2/internal/domains/platforms/etsy" "ruben/inventory2/internal/domains/platforms/etsy"
"ruben/inventory2/internal/domains/raw_events" "ruben/inventory2/internal/domains/raw_events"
"ruben/inventory2/internal/logging"
) )
type ( type (
Webhooks struct { Webhooks struct {
log *slog.Logger log *logging.Logger
cfg Config cfg Config
db *raw_events.Store db *raw_events.Store
etsy *etsy.Platform etsy *etsy.Platform
@@ -26,7 +26,7 @@ type (
) )
func NewWebhookHandler( func NewWebhookHandler(
logger *slog.Logger, logger *logging.Logger,
db *raw_events.Store, db *raw_events.Store,
platform *etsy.Platform, platform *etsy.Platform,
cfg Config, cfg Config,
@@ -3,14 +3,14 @@ package tiktok
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"time" "time"
"ruben/inventory2/internal/domains/raw_events" "ruben/inventory2/internal/domains/raw_events"
"ruben/inventory2/internal/logging"
) )
func NewWebhookHandler(logger *slog.Logger, db *raw_events.Store) http.Handler { func NewWebhookHandler(logger *logging.Logger, db *raw_events.Store) http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("POST /test", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("POST /test", func(w http.ResponseWriter, r *http.Request) {
+2 -2
View File
@@ -1,11 +1,11 @@
package webhooks package webhooks
import ( import (
"log/slog"
"net/http" "net/http"
etsy_platform "ruben/inventory2/internal/domains/platforms/etsy" etsy_platform "ruben/inventory2/internal/domains/platforms/etsy"
"ruben/inventory2/internal/domains/raw_events" "ruben/inventory2/internal/domains/raw_events"
"ruben/inventory2/internal/logging"
"ruben/inventory2/internal/server/api/webhooks/etsy" "ruben/inventory2/internal/server/api/webhooks/etsy"
"ruben/inventory2/internal/server/api/webhooks/tiktok" "ruben/inventory2/internal/server/api/webhooks/tiktok"
"ruben/inventory2/internal/server/api/webhooks/wix" "ruben/inventory2/internal/server/api/webhooks/wix"
@@ -16,7 +16,7 @@ type Config struct {
} }
// TODO: just move over to the 'site' package, and then consider renaming the site package to something else? // TODO: just move over to the 'site' package, and then consider renaming the site package to something else?
func New(logger *slog.Logger, eventsDB *raw_events.Store, etsyPlatform *etsy_platform.Platform, cfg Config) http.Handler { func New(logger *logging.Logger, eventsDB *raw_events.Store, etsyPlatform *etsy_platform.Platform, cfg Config) http.Handler {
wh := http.NewServeMux() wh := http.NewServeMux()
wh.Handle( wh.Handle(
+2 -2
View File
@@ -3,14 +3,14 @@ package wix
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"time" "time"
"ruben/inventory2/internal/domains/raw_events" "ruben/inventory2/internal/domains/raw_events"
"ruben/inventory2/internal/logging"
) )
func NewWebhookHandler(logger *slog.Logger, db *raw_events.Store) http.Handler { func NewWebhookHandler(logger *logging.Logger, db *raw_events.Store) http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("POST /test", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("POST /test", func(w http.ResponseWriter, r *http.Request) {
+3 -3
View File
@@ -6,20 +6,20 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"log/slog"
"net/http" "net/http"
"time" "time"
"ruben/inventory2/internal/consts" "ruben/inventory2/internal/consts"
"ruben/inventory2/internal/domains/accounts" "ruben/inventory2/internal/domains/accounts"
"ruben/inventory2/internal/domains/authentication" "ruben/inventory2/internal/domains/authentication"
"ruben/inventory2/internal/logging"
"ruben/inventory2/internal/server/cookies" "ruben/inventory2/internal/server/cookies"
"ruben/inventory2/internal/server/response" "ruben/inventory2/internal/server/response"
) )
type ( type (
Auth struct { Auth struct {
log *slog.Logger log *logging.Logger
auth *authentication.Authenticator auth *authentication.Authenticator
newLoginURL LoginURLProviderFunc newLoginURL LoginURLProviderFunc
accts *accounts.Store accts *accounts.Store
@@ -38,7 +38,7 @@ type (
) )
func NewAuth( func NewAuth(
logger *slog.Logger, logger *logging.Logger,
auth *authentication.Authenticator, auth *authentication.Authenticator,
newLoginURL LoginURLProviderFunc, newLoginURL LoginURLProviderFunc,
accts *accounts.Store, accts *accounts.Store,
+2 -2
View File
@@ -2,14 +2,14 @@ package middleware
import ( import (
"context" "context"
"log/slog"
"net/http" "net/http"
"time" "time"
"ruben/inventory2/internal/logging"
"ruben/inventory2/internal/server/response" "ruben/inventory2/internal/server/response"
) )
func LogRequests(ctx context.Context, logger *slog.Logger) response.Middleware { func LogRequests(ctx context.Context, logger *logging.Logger) response.Middleware {
reqIDCh := newRequestIDProvider(ctx) reqIDCh := newRequestIDProvider(ctx)
return func(fn response.HandlerFunc) response.HandlerFunc { return func(fn response.HandlerFunc) response.HandlerFunc {
+109 -107
View File
@@ -2,13 +2,13 @@ package router
import ( import (
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"path" "path"
"slices" "slices"
"strings" "strings"
"ruben/inventory2/internal/consts" "ruben/inventory2/internal/consts"
"ruben/inventory2/internal/logging"
"ruben/inventory2/internal/server/response" "ruben/inventory2/internal/server/response"
) )
@@ -16,7 +16,7 @@ type (
Mux struct { Mux struct {
Mux *http.ServeMux Mux *http.ServeMux
middleware []response.Middleware middleware []response.Middleware
log *slog.Logger log *logging.Logger
} }
) )
@@ -24,7 +24,7 @@ var (
ErrHandlerNotFound = fmt.Errorf("%w: handler not found", consts.ErrNotFound) ErrHandlerNotFound = fmt.Errorf("%w: handler not found", consts.ErrNotFound)
) )
func NewMux(log *slog.Logger, ms ...response.Middleware) *Mux { func NewMux(log *logging.Logger, ms ...response.Middleware) *Mux {
return &Mux{ return &Mux{
Mux: http.NewServeMux(), Mux: http.NewServeMux(),
middleware: ms, middleware: ms,
@@ -37,12 +37,13 @@ func (m *Mux) AddMiddleware(ms ...response.Middleware) *Mux {
return m return m
} }
func (m *Mux) traced(method, msg string, args ...any) func(func() (finalArgs []any)) {
return logTraceAndDefer(m.log, "Mux", method, msg, args...)
}
func (m *Mux) Handle(pattern string, fn response.HandlerFunc) { func (m *Mux) Handle(pattern string, fn response.HandlerFunc) {
defer m.traced("Handle", "", "pattern", pattern)(nil) log := m.log.With(
"method", "Handle",
"patter", pattern,
"fn", fn,
)
defer log.DebugCallf("called")()
m.Mux.Handle(pattern, response.Handler(m.applyMiddleware(fn))) m.Mux.Handle(pattern, response.Handler(m.applyMiddleware(fn)))
} }
@@ -59,7 +60,10 @@ func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Route does not accept methods or ... wildcards // Route does not accept methods or ... wildcards
// func (m *Mux) Route(basePathPattern string, sr Subrouter) { // func (m *Mux) Route(basePathPattern string, sr Subrouter) {
func (m *Mux) Route(pattern string, sr Subrouter) { func (m *Mux) Route(pattern string, sr Subrouter) {
defer m.traced("Route", "pattern", pattern)(nil) log := m.log.With("method", "Route", "pattern", pattern, "sr", sr)
defer log.DebugDeferf("called")(func() (string, []any) {
return "returned", nil
})
// --- // ---
method, segments, _ := getHTTPMethodAndPathSegments(m.log, pattern) method, segments, _ := getHTTPMethodAndPathSegments(m.log, pattern)
@@ -79,7 +83,7 @@ func (m *Mux) Route(pattern string, sr Subrouter) {
if method != "" { if method != "" {
cleanPattern = method + " " + cleanPattern cleanPattern = method + " " + cleanPattern
} }
logTrace(m.log, "mux", "Route", "cleanPattern: "+cleanPattern) log.Debugf("cleanPattern: %s", cleanPattern)
// --- // ---
/* /*
@@ -123,11 +127,11 @@ type (
SubMux struct { SubMux struct {
tree *muxTree tree *muxTree
middleware []response.Middleware middleware []response.Middleware
log *slog.Logger log *logging.Logger
} }
) )
func NewSubMux(log *slog.Logger, ms ...response.Middleware) *SubMux { func NewSubMux(log *logging.Logger, ms ...response.Middleware) *SubMux {
return &SubMux{ return &SubMux{
tree: newMuxTree(log.WithGroup("muxTree")), tree: newMuxTree(log.WithGroup("muxTree")),
middleware: ms, middleware: ms,
@@ -135,13 +139,9 @@ func NewSubMux(log *slog.Logger, ms ...response.Middleware) *SubMux {
} }
} }
func (m *SubMux) traced(method, msg string, args ...any) func(func() (finalArgs []any)) {
return logTraceAndDefer(m.log, "SubMux", method, msg, args...)
}
// TODO: this is capturing all subroutes! // TODO: this is capturing all subroutes!
func (m *SubMux) Handle(pattern string, fn response.HandlerFunc) { func (m *SubMux) Handle(pattern string, fn response.HandlerFunc) {
defer m.traced("Handle", "", "pattern", pattern)(nil) defer m.log.With("pattern", pattern, "fn", fn).DebugCallf("Handle")()
m.tree.set(pattern, m.applyMiddleware(fn)) m.tree.set(pattern, m.applyMiddleware(fn))
} }
@@ -149,8 +149,8 @@ func (m *SubMux) Handle(pattern string, fn response.HandlerFunc) {
// Route does not accept methods or ... wildcards // Route does not accept methods or ... wildcards
// func (m *SubMux) Route(basePathPattern string, sr Subrouter) { // func (m *SubMux) Route(basePathPattern string, sr Subrouter) {
func (m *SubMux) Route(pattern string, sr Subrouter) { func (m *SubMux) Route(pattern string, sr Subrouter) {
//defer m.traced("Route", "", "basePathPattern", basePathPattern)(nil) log := m.log.With("method", "Route", "pattern", pattern, "sr", sr)
defer m.traced("Route", "", "pattern", pattern)(nil) defer log.DebugCallf("Route")()
method, segments, _ := getHTTPMethodAndPathSegments(m.log, pattern) method, segments, _ := getHTTPMethodAndPathSegments(m.log, pattern)
if pattern == "" || (len(segments) == 0 && pattern[len(pattern)-1] != '/') { if pattern == "" || (len(segments) == 0 && pattern[len(pattern)-1] != '/') {
@@ -169,7 +169,7 @@ func (m *SubMux) Route(pattern string, sr Subrouter) {
if method != "" { if method != "" {
cleanPattern = method + " " + cleanPattern cleanPattern = method + " " + cleanPattern
} }
logTrace(m.log, "SubMux", "Route", "Handle about to be called", "cleanPattern", cleanPattern) log.Debugf("Handle about to be called: cleanPattern: %s", cleanPattern)
// --- // ---
/* /*
@@ -238,11 +238,16 @@ func (m *SubMux) Route(pattern string, sr Subrouter) {
// */ // */
// } // }
func buildSubrouterHandlerFunc(logger *slog.Logger, numPatternSegments int, sr Subrouter) response.HandlerFunc { func buildSubrouterHandlerFunc(logger *logging.Logger, numPatternSegments int, sr Subrouter) response.HandlerFunc {
defer logTraceAndDefer(logger, "", "buildSubrouterHandlerFunc", "", "numPatternSegments", numPatternSegments)(nil) log := logger.With(
"function", "buildSubrouterHandlerFunc",
"numPatternSegments", numPatternSegments,
)
return func(r *http.Request) (res response.Response, err error) { return func(r *http.Request) (res response.Response, err error) {
defer logTraceAndDefer(logger, "", "buildSubrouterHandlerFunc", "", "numPatternSegments", numPatternSegments, "r.URL", r.URL)(func() []any { log := log.With("r.URL", r.URL)
return []any{ defer log.DebugDeferf("called")(func() (string, []any) {
return "returned", []any{
"res", res, "res", res,
"err", err, "err", err,
} }
@@ -275,9 +280,9 @@ func buildSubrouterHandlerFunc(logger *slog.Logger, numPatternSegments int, sr S
// do the request // do the request
logTrace(logger, "", "buildSubrouterHandlerFunc", "handler called", "subpath", r.URL.Path) log.Debugf("handler called: %s", r.URL)
fn, params, ok := sr.Handler(r) fn, params, ok := sr.Handler(r)
logTrace(logger, "", "buildSubrouterHandlerFunc", "handler returned", "subpath", r.URL.Path, "fn", fn, "params", params, "ok", ok) log.Debugf("handler returned: url = %s, fn = %v, params = %v, ok = %v", r.URL, fn, params, ok)
if !ok { if !ok {
return nil, response.NotFound().Wrap(ErrHandlerNotFound) return nil, response.NotFound().Wrap(ErrHandlerNotFound)
} }
@@ -293,8 +298,13 @@ func buildSubrouterHandlerFunc(logger *slog.Logger, numPatternSegments int, sr S
} }
func (m *SubMux) Handler(r *http.Request) (fn response.HandlerFunc, params map[string]string, found bool) { func (m *SubMux) Handler(r *http.Request) (fn response.HandlerFunc, params map[string]string, found bool) {
defer m.traced("Handler", "", "r.URL", r.URL)(func() []any { log := m.log.With(
return []any{ "method", "Handler",
"r.URL", r.URL,
)
defer log.DebugDeferf("called")(func() (string, []any) {
return "returned", []any{
"fn", fn,
"params", params, "params", params,
"found", found, "found", found,
} }
@@ -310,7 +320,7 @@ func (m *SubMux) applyMiddleware(fn response.HandlerFunc) response.HandlerFunc {
type ( type (
muxTree struct { muxTree struct {
log *slog.Logger log *logging.Logger
branches map[string]*muxTree branches map[string]*muxTree
wildcardKey string wildcardKey string
@@ -322,17 +332,13 @@ type (
} }
) )
func newMuxTree(log *slog.Logger) *muxTree { func newMuxTree(log *logging.Logger) *muxTree {
return &muxTree{ return &muxTree{
log: log, log: log,
branches: make(map[string]*muxTree), branches: make(map[string]*muxTree),
} }
} }
func (m *muxTree) traced(method, msg string, args ...any) func(func() (finalArgs []any)) {
return logTraceAndDefer(m.log, "muxTree", method, msg, args...)
}
func (m *muxTree) String() string { func (m *muxTree) String() string {
if m == nil { if m == nil {
return "<nil>" return "<nil>"
@@ -370,11 +376,12 @@ func (m *muxTree) String() string {
} }
func (m *muxTree) set(pattern string, fn response.HandlerFunc) { func (m *muxTree) set(pattern string, fn response.HandlerFunc) {
defer m.traced("set", "", "pattern", pattern)(func() []any { log := m.log.With(
return []any{ "method", "set",
"muxTree", m, "patter", pattern,
} "fn", fn,
}) )
defer log.DebugCallf("called")()
method, segments, trailingSlash, endOfURLWildcard := getHTTPMethodAndPathSegmentsDroppingEndOfURLWildcard(m.log, pattern) method, segments, trailingSlash, endOfURLWildcard := getHTTPMethodAndPathSegmentsDroppingEndOfURLWildcard(m.log, pattern)
m.setBySegments( m.setBySegments(
method, method,
@@ -387,18 +394,15 @@ func (m *muxTree) set(pattern string, fn response.HandlerFunc) {
// TODO: handle /{$} properly // TODO: handle /{$} properly
func (m *muxTree) setBySegments(method string, segments []string, trailingSlash, endOfURLWildcard bool, fn response.HandlerFunc) { func (m *muxTree) setBySegments(method string, segments []string, trailingSlash, endOfURLWildcard bool, fn response.HandlerFunc) {
defer m.traced( log := m.log.With(
"setBySegments", "method", "setBySegments",
"", "arg.method", method,
"method", method,
"segments", segments, "segments", segments,
"trailingSlash", trailingSlash, "trailingSlash", trailingSlash,
"endOfURLWildcard", endOfURLWildcard, "endOfURLWildcard", endOfURLWildcard,
)(func() []any { "fn", fn,
return []any{ )
"muxTree", m, defer log.DebugCallf("called")()
}
})
if len(segments) == 0 { if len(segments) == 0 {
m.method = method m.method = method
if !trailingSlash || endOfURLWildcard { if !trailingSlash || endOfURLWildcard {
@@ -454,8 +458,13 @@ func (m *muxTree) setBySegments(method string, segments []string, trailingSlash,
} }
func (m *muxTree) get(method, pattern string) (fn response.HandlerFunc, params map[string]string, found bool) { func (m *muxTree) get(method, pattern string) (fn response.HandlerFunc, params map[string]string, found bool) {
defer m.traced("get", "", "method", method, "pattern", pattern)(func() []any { log := m.log.With(
return []any{ "method", "get",
"arg.method", method,
"pattern", pattern,
)
defer log.DebugDeferf("called")(func() (string, []any) {
return "returned", []any{
"fn", fn, "fn", fn,
"params", params, "params", params,
"found", found, "found", found,
@@ -466,15 +475,22 @@ func (m *muxTree) get(method, pattern string) (fn response.HandlerFunc, params m
} }
func (m *muxTree) getByHTTPMethodAndSegments(method string, segments []string, trailingSlash bool) (fn response.HandlerFunc, params map[string]string, found bool) { func (m *muxTree) getByHTTPMethodAndSegments(method string, segments []string, trailingSlash bool) (fn response.HandlerFunc, params map[string]string, found bool) {
defer m.traced("getByHTTPMethodAndSegments", "", "method", method, "segments", segments, "trailingSlash", trailingSlash)(func() []any { log := m.log.With(
return []any{ "method", "getByHTTPMethodAndSegments",
"arg.method", method,
"segments", segments,
"trailingSlash", trailingSlash,
"muxTree", m,
)
defer log.DebugDeferf("called")(func() (string, []any) {
return "returned", []any{
"fn", fn, "fn", fn,
"params", params, "params", params,
"found", found, "found", found,
} }
}) })
if len(segments) == 0 { if len(segments) == 0 {
logTrace(m.log, "muxTree", "getByHTTPMethodAndSegments", "no segments", "tree", m) log.Debugf("no segments")
if methodMatches := m.method == "" || m.method == method; methodMatches { if methodMatches := m.method == "" || m.method == method; methodMatches {
if fn = m.handler; fn == nil { if fn = m.handler; fn == nil {
fn = m.subrouteHandler fn = m.subrouteHandler
@@ -497,42 +513,50 @@ func (m *muxTree) getByHTTPMethodAndSegments(method string, segments []string, t
} }
if sm, ok := m.branches[head]; ok { if sm, ok := m.branches[head]; ok {
logTrace(m.log, "muxTree", "getByHTTPMethodAndSegments", "matching branch found", "head", head, "sm", sm) log.Debugf("matching branch found")
//return sm.getByHTTPMethodAndSegments(method, tail, trailingSlash) //return sm.getByHTTPMethodAndSegments(method, tail, trailingSlash)
if fn, params, found = sm.getByHTTPMethodAndSegments(method, tail, trailingSlash); found { if fn, params, found = sm.getByHTTPMethodAndSegments(method, tail, trailingSlash); found {
return fn, params, found return fn, params, found
} }
logTrace(m.log, "muxTree", "getByHTTPMethodAndSegments", "matching branch mismatched at subpath") log.Debugf("matching branch mismatched at subpath")
} else { } else {
logTrace(m.log, "muxTree", "getByHTTPMethodAndSegments", "no matching branch found") log.Debugf("no matching branch found")
} }
if sm := m.wildcardBranch; sm != nil { if sm := m.wildcardBranch; sm != nil {
logTrace(m.log, "muxTree", "getByHTTPMethodAndSegments", "wildcard branch found", "m.wildcardKey", m.wildcardKey, "sm", sm) wlog := log.With(
"wildcardKey", m.wildcardKey,
"sub.muxTree", sm,
)
wlog.Debugf("wildcard branch found")
//fn, params, found = sm.getByHTTPMethodAndSegments(method, tail, trailingSlash) //fn, params, found = sm.getByHTTPMethodAndSegments(method, tail, trailingSlash)
if fn, params, found = sm.getByHTTPMethodAndSegments(method, tail, trailingSlash); found { if fn, params, found = sm.getByHTTPMethodAndSegments(method, tail, trailingSlash); found {
params[m.wildcardKey] = head params[m.wildcardKey] = head
return fn, params, found return fn, params, found
} }
logTrace(m.log, "muxTree", "getByHTTPMethodAndSegments", "wildcard branch mismatched at subpath") wlog.Debugf("wildcard branch mismatched at subpath")
} else { } else {
logTrace(m.log, "muxTree", "getByHTTPMethodAndSegments", "no wildcard branch found") log.Debugf("no wildcard branch found")
} }
// --- TODO: test --- // --- TODO: test ---
if m.subrouteHandler != nil && (m.method == method || m.method == "") { if m.subrouteHandler != nil && (m.method == method || m.method == "") {
logTrace(m.log, "muxTree", "getByHTTPMethodAndSegments", "subroutes captured", "m", m) log.Debugf("subroutes captured")
return m.subrouteHandler, nil, true return m.subrouteHandler, nil, true
} }
logTrace(m.log, "muxTree", "getByHTTPMethodAndSegments", "subroutes not captured", "m", m) log.Debugf("subroutes not captured")
// --- // ---
return nil, map[string]string{}, false return nil, map[string]string{}, false
} }
func getHTTPMethodAndPathSegmentsDroppingEndOfURLWildcard(logger *slog.Logger, pattern string) (method string, segments []string, trailingSlash, endOfURLWildcard bool) { func getHTTPMethodAndPathSegmentsDroppingEndOfURLWildcard(logger *logging.Logger, pattern string) (method string, segments []string, trailingSlash, endOfURLWildcard bool) {
defer logTraceAndDefer(logger, "", "getHTTPMethodAndPathSegmentsDroppingEndOfURLWildcard", "", "pattern", pattern)(func() []any { log := logger.With(
return []any{ "function", "getHTTPMethodAndPathSegmentsDroppingEndOfURLWildcard",
"pattern", pattern,
)
defer log.DebugDeferf("called")(func() (string, []any) {
return "returned", []any{
"method", method, "method", method,
"segments", segments, "segments", segments,
"trailingSlash", trailingSlash, "trailingSlash", trailingSlash,
@@ -548,9 +572,13 @@ func getHTTPMethodAndPathSegmentsDroppingEndOfURLWildcard(logger *slog.Logger, p
return method, segments, trailingSlash, endOfURLWildcard return method, segments, trailingSlash, endOfURLWildcard
} }
func getHTTPMethodAndPathSegments(logger *slog.Logger, pattern string) (method string, segments []string, trailingSlash bool) { func getHTTPMethodAndPathSegments(logger *logging.Logger, pattern string) (method string, segments []string, trailingSlash bool) {
defer logTraceAndDefer(logger, "", "getHTTPMethodAndPathSegments", "", "pattern", pattern)(func() []any { log := logger.With(
return []any{ "function", "getHTTPMethodAndPathSegments",
"pattern", pattern,
)
defer log.DebugDeferf("called")(func() (string, []any) {
return "returned", []any{
"method", method, "method", method,
"segments", segments, "segments", segments,
"trailingSlash", trailingSlash, "trailingSlash", trailingSlash,
@@ -565,13 +593,12 @@ func getHTTPMethodAndPathSegments(logger *slog.Logger, pattern string) (method s
return method, segments, trailingSlash return method, segments, trailingSlash
} }
func getPathSegments(logger *slog.Logger, pathPattern string) (segments []string, trailingSlash bool) { func getPathSegments(logger *logging.Logger, pathPattern string) (segments []string, trailingSlash bool) {
defer logTraceAndDefer(logger, "", "getPathSegments", "", "pathPattern", pathPattern)(func() []any { log := logger.With(
return []any{ "function", "getPathSegments",
"segments", segments, "pathPattern", pathPattern,
"trailingSlash", trailingSlash, )
} defer log.DebugCallf("getPathSegments")()
})
trailingSlash = strings.HasSuffix(pathPattern, "/") trailingSlash = strings.HasSuffix(pathPattern, "/")
p := strings.Trim(path.Clean(pathPattern), "/") p := strings.Trim(path.Clean(pathPattern), "/")
@@ -583,9 +610,13 @@ func getPathSegments(logger *slog.Logger, pathPattern string) (segments []string
} }
// NOTE: if endOfURLWildcard, then trailingSlash // NOTE: if endOfURLWildcard, then trailingSlash
func getPathSegmentsWithoutEndOfURLWildcard(logger *slog.Logger, pathPattern string) (segments []string, trailingSlash, endOfURLWildcard bool) { func getPathSegmentsWithoutEndOfURLWildcard(logger *logging.Logger, pathPattern string) (segments []string, trailingSlash, endOfURLWildcard bool) {
defer logTraceAndDefer(logger, "", "getPathSegmentsWithoutEndOfURLWildcard", "", "pathPattern", pathPattern)(func() []any { log := logger.With(
return []any{ "function", "getPathSegmentsWithoutEndOfURLWildcard",
"pathPattern", pathPattern,
)
defer log.DebugDeferf("called")(func() (string, []any) {
return "returned", []any{
"segments", segments, "segments", segments,
"trailingSlash", trailingSlash, "trailingSlash", trailingSlash,
"endOfURLWildcard", endOfURLWildcard, "endOfURLWildcard", endOfURLWildcard,
@@ -625,33 +656,4 @@ func applyMiddleware(fn response.HandlerFunc, ms ...response.Middleware) respons
return fn return fn
} }
// logging utilities // TODO: remove the consts. trace level
func logTraceAndDefer(logger *slog.Logger, typeName, method, msg string, args ...any) func(func() (finalArgs []any)) {
fmsg := fmt.Sprintf("%s.%s called", typeName, method)
if msg != "" {
fmsg = fmt.Sprintf("%s: %s", fmsg, msg)
}
logger.Log(nil, consts.LevelTrace, fmsg, args...)
return func(cb func() (finalArgs []any)) {
if cb != nil {
args = append(args, cb()...)
}
fmsg := fmt.Sprintf("%s.%s returned", typeName, method)
if msg != "" {
fmsg = fmt.Sprintf("%s: %s", fmsg, msg)
}
logger.Log(nil, consts.LevelTrace, fmsg, args...)
}
}
func logTrace(logger *slog.Logger, typeName, method, msg string, args ...any) {
fmsg := fmt.Sprintf("%s.%s", typeName, method)
if msg != "" {
fmsg = fmt.Sprintf("%s: %s", fmsg, msg)
}
logger.Log(nil, consts.LevelTrace, fmsg, args...)
}
+2 -2
View File
@@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"
"log/slog"
"net/http" "net/http"
"path" "path"
"strconv" "strconv"
@@ -17,6 +16,7 @@ import (
"ruben/inventory2/internal/domains/authentication" "ruben/inventory2/internal/domains/authentication"
etsy_platform "ruben/inventory2/internal/domains/platforms/etsy" etsy_platform "ruben/inventory2/internal/domains/platforms/etsy"
"ruben/inventory2/internal/domains/raw_events" "ruben/inventory2/internal/domains/raw_events"
"ruben/inventory2/internal/logging"
accounts_api "ruben/inventory2/internal/server/api/accounts" accounts_api "ruben/inventory2/internal/server/api/accounts"
auth_api "ruben/inventory2/internal/server/api/auth" auth_api "ruben/inventory2/internal/server/api/auth"
templates_api "ruben/inventory2/internal/server/api/templates" templates_api "ruben/inventory2/internal/server/api/templates"
@@ -29,7 +29,7 @@ import (
func NewServer( func NewServer(
ctx context.Context, ctx context.Context,
logger *slog.Logger, logger *logging.Logger,
contentDir string, contentDir string,
rawEvents *raw_events.Store, rawEvents *raw_events.Store,
accts *accounts.Store, accts *accounts.Store,
+22 -4
View File
@@ -24,6 +24,7 @@ import (
"ruben/inventory2/internal/domains/authentication" "ruben/inventory2/internal/domains/authentication"
etsy_platform "ruben/inventory2/internal/domains/platforms/etsy" etsy_platform "ruben/inventory2/internal/domains/platforms/etsy"
"ruben/inventory2/internal/domains/raw_events" "ruben/inventory2/internal/domains/raw_events"
"ruben/inventory2/internal/logging"
"ruben/inventory2/internal/server" "ruben/inventory2/internal/server"
) )
@@ -33,10 +34,9 @@ const (
) )
func main() { func main() {
logger := slog.New(tint.NewHandler(os.Stderr, &tint.Options{ logger := logging.New(tint.NewHandler(os.Stderr, &tint.Options{
AddSource: true, AddSource: true,
Level: slog.LevelDebug, Level: slog.LevelDebug,
//Level: consts.LevelTrace,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
// this can perform general key=value log cleanup // this can perform general key=value log cleanup
@@ -50,6 +50,24 @@ func main() {
// Time format (Default: time.StampMilli) // Time format (Default: time.StampMilli)
//TimeFormat: "", //TimeFormat: "",
})) }))
/*
logger := slog.New(tint.NewHandler(os.Stderr, &tint.Options{
AddSource: true,
Level: slog.LevelDebug,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
// this can perform general key=value log cleanup
if a.Key == slog.SourceKey && len(groups) == 0 {
source := a.Value.Any().(*slog.Source)
source.File = strings.TrimPrefix(source.File, "/home/angel/go/src/ruben/inventory2/internal")
}
return a
},
// Time format (Default: time.StampMilli)
//TimeFormat: "",
}))
*/
logger.Info("application starting") logger.Info("application starting")
@@ -60,7 +78,7 @@ func main() {
logger.Info("application shutdown") logger.Info("application shutdown")
} }
func runApp(ctx context.Context, logger *slog.Logger) error { func runApp(ctx context.Context, logger *logging.Logger) error {
ctx, shutdown := context.WithCancel(ctx) ctx, shutdown := context.WithCancel(ctx)
defer shutdown() defer shutdown()
@@ -149,7 +167,7 @@ func runAuthProcesses(ctx context.Context, auth *authentication.Authenticator) <
return errCh return errCh
} }
func runServer(ctx context.Context, logger *slog.Logger, connPool *pgxpool.Pool, auth *authentication.Authenticator) <-chan error { func runServer(ctx context.Context, logger *logging.Logger, connPool *pgxpool.Pool, auth *authentication.Authenticator) <-chan error {
srv := &http.Server{ srv := &http.Server{
Addr: ":8082", // local Addr: ":8082", // local
Handler: server.NewServer( Handler: server.NewServer(