From 94b21b286edff37496a2fe481963625ac01c30a1 Mon Sep 17 00:00:00 2001 From: Alan Pearce Date: Mon, 1 Jul 2024 22:15:06 +0200 Subject: feat: more structured logging --- internal/components/results.templ | 7 +------ internal/config/config.go | 6 +++--- internal/config/structs.go | 11 ++++++----- internal/fetcher/channel.go | 6 +++--- internal/fetcher/download.go | 8 ++++---- internal/fetcher/http.go | 7 ++++--- internal/fetcher/main.go | 4 ++-- internal/fetcher/nixpkgs-channel.go | 8 ++++---- internal/importer/importer.go | 4 ++-- internal/importer/main.go | 26 ++++++++++++++++++-------- internal/importer/main_test.go | 7 ++++--- internal/importer/options.go | 20 +++++++++++++------- internal/importer/package.go | 9 ++++++++- internal/index/index_meta.go | 15 +++++++++------ internal/index/indexer.go | 28 ++++++++++++++++------------ internal/index/search.go | 2 ++ internal/server/dev.go | 34 +++++++++++++++++++--------------- internal/server/error.go | 5 +++-- internal/server/logging.go | 15 +++++++++------ internal/server/mux.go | 28 ++++++++++------------------ internal/server/server.go | 23 +++++++++++++++-------- 21 files changed, 155 insertions(+), 118 deletions(-) (limited to 'internal') diff --git a/internal/components/results.templ b/internal/components/results.templ index 4897638..b051219 100644 --- a/internal/components/results.templ +++ b/internal/components/results.templ @@ -2,16 +2,11 @@ package components import ( "strconv" - "log/slog" "go.alanpearce.eu/searchix/internal/nix" ) func convertMatch[I nix.Importable](m nix.Importable) *I { - i, ok := m.(I) - if !ok { - slog.Warn("Converting match failed", "match", m) - return nil - } + i := m.(I) return &i } diff --git a/internal/config/config.go b/internal/config/config.go index 14375d6..f3c0b57 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,7 +1,6 @@ package config import ( - "log/slog" "maps" "net/url" "os" @@ -9,6 +8,7 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/pkg/errors" + "go.alanpearce.eu/x/log" ) var Version string @@ -102,10 +102,10 @@ func mustLocalTime(in string) (time LocalTime) { return } -func GetConfig(filename string) (*Config, error) { +func GetConfig(filename string, log *log.Logger) (*Config, error) { config := DefaultConfig if filename != "" { - slog.Debug("reading config", "filename", filename) + log.Debug("reading config", "filename", filename) f, err := os.Open(filename) if err != nil { return nil, errors.Wrap(err, "reading config failed") diff --git a/internal/config/structs.go b/internal/config/structs.go index e6cf20b..dd79303 100644 --- a/internal/config/structs.go +++ b/internal/config/structs.go @@ -5,14 +5,15 @@ package config import ( "fmt" - "log/slog" + + "go.uber.org/zap/zapcore" ) type Config struct { - DataPath string `comment:"Path to store index data."` - LogLevel slog.Level `comment:"How much information to log, one of 'debug', 'info', 'warn', 'error'."` - Web *Web `comment:"Settings for the web server"` - Importer *Importer `comment:"Settings for the import job"` + DataPath string `comment:"Path to store index data."` + LogLevel zapcore.Level `comment:"How much information to log, one of 'debug', 'info', 'warn', 'error', 'panic', 'fatal'."` + Web *Web `comment:"Settings for the web server"` + Importer *Importer `comment:"Settings for the import job"` } type Web struct { diff --git a/internal/fetcher/channel.go b/internal/fetcher/channel.go index 2bde631..8f0aa03 100644 --- a/internal/fetcher/channel.go +++ b/internal/fetcher/channel.go @@ -3,7 +3,6 @@ package fetcher import ( "context" "fmt" - "log/slog" "os" "os/exec" "path" @@ -13,6 +12,7 @@ import ( "go.alanpearce.eu/searchix/internal/config" "go.alanpearce.eu/searchix/internal/index" + "go.alanpearce.eu/x/log" "github.com/pkg/errors" ) @@ -20,12 +20,12 @@ import ( type ChannelFetcher struct { Source *config.Source SourceFile string - Logger *slog.Logger + Logger *log.Logger } func NewChannelFetcher( source *config.Source, - logger *slog.Logger, + logger *log.Logger, ) (*ChannelFetcher, error) { switch source.Importer { case config.Options: diff --git a/internal/fetcher/download.go b/internal/fetcher/download.go index 941a683..a34c838 100644 --- a/internal/fetcher/download.go +++ b/internal/fetcher/download.go @@ -3,24 +3,24 @@ package fetcher import ( "context" "fmt" - "log/slog" "net/url" "go.alanpearce.eu/searchix/internal/config" "go.alanpearce.eu/searchix/internal/index" "github.com/pkg/errors" + "go.alanpearce.eu/x/log" ) type DownloadFetcher struct { Source *config.Source SourceFile string - Logger *slog.Logger + Logger *log.Logger } func NewDownloadFetcher( source *config.Source, - logger *slog.Logger, + logger *log.Logger, ) (*DownloadFetcher, error) { switch source.Importer { case config.Options: @@ -59,7 +59,7 @@ func (i *DownloadFetcher) FetchIfNeeded( i.Logger.Debug("preparing to fetch URL", "url", fetchURL) - body, mtime, err := fetchFileIfNeeded(ctx, sourceUpdated, fetchURL) + body, mtime, err := fetchFileIfNeeded(ctx, i.Logger, sourceUpdated, fetchURL) if err != nil { i.Logger.Warn("failed to fetch file", "url", fetchURL, "error", err) diff --git a/internal/fetcher/http.go b/internal/fetcher/http.go index c848dc9..c5ec8fc 100644 --- a/internal/fetcher/http.go +++ b/internal/fetcher/http.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "log/slog" "net/http" "strings" "time" @@ -13,6 +12,7 @@ import ( "github.com/andybalholm/brotli" "github.com/pkg/errors" + "go.alanpearce.eu/x/log" ) type brotliReadCloser struct { @@ -33,6 +33,7 @@ func (r *brotliReadCloser) Close() error { func fetchFileIfNeeded( ctx context.Context, + log *log.Logger, mtime time.Time, url string, ) (body io.ReadCloser, newMtime time.Time, err error) { @@ -68,7 +69,7 @@ func fetchFileIfNeeded( case http.StatusOK: newMtime, err = time.Parse(time.RFC1123, res.Header.Get("Last-Modified")) if err != nil { - slog.Warn( + log.Warn( "could not parse Last-Modified header from response", "value", res.Header.Get("Last-Modified"), @@ -78,7 +79,7 @@ func fetchFileIfNeeded( switch ce := res.Header.Get("Content-Encoding"); ce { case "br": - slog.Debug("using brotli encoding") + log.Debug("using brotli encoding") body = newBrotliReader(res.Body) case "", "identity", "gzip": body = res.Body diff --git a/internal/fetcher/main.go b/internal/fetcher/main.go index fcc04a9..ac40ead 100644 --- a/internal/fetcher/main.go +++ b/internal/fetcher/main.go @@ -3,10 +3,10 @@ package fetcher import ( "context" "io" - "log/slog" "go.alanpearce.eu/searchix/internal/config" "go.alanpearce.eu/searchix/internal/index" + "go.alanpearce.eu/x/log" "github.com/pkg/errors" ) @@ -23,7 +23,7 @@ type Fetcher interface { func New( source *config.Source, - logger *slog.Logger, + logger *log.Logger, ) (fetcher Fetcher, err error) { switch source.Fetcher { case config.ChannelNixpkgs: diff --git a/internal/fetcher/nixpkgs-channel.go b/internal/fetcher/nixpkgs-channel.go index ca33ae6..6f8ca63 100644 --- a/internal/fetcher/nixpkgs-channel.go +++ b/internal/fetcher/nixpkgs-channel.go @@ -3,18 +3,18 @@ package fetcher import ( "context" "fmt" - "log/slog" "net/url" "go.alanpearce.eu/searchix/internal/config" "go.alanpearce.eu/searchix/internal/index" "github.com/pkg/errors" + "go.alanpearce.eu/x/log" ) type NixpkgsChannelFetcher struct { Source *config.Source - Logger *slog.Logger + Logger *log.Logger } func makeChannelURL(channel string, subPath string) (string, error) { @@ -25,7 +25,7 @@ func makeChannelURL(channel string, subPath string) (string, error) { func NewNixpkgsChannelFetcher( source *config.Source, - logger *slog.Logger, + logger *log.Logger, ) (*NixpkgsChannelFetcher, error) { switch source.Importer { case config.Options, config.Packages: @@ -66,7 +66,7 @@ func (i *NixpkgsChannelFetcher) FetchIfNeeded( } i.Logger.Debug("attempting to fetch file", "url", fetchURL) - body, mtime, err := fetchFileIfNeeded(ctx, sourceMeta.Updated, fetchURL) + body, mtime, err := fetchFileIfNeeded(ctx, i.Logger, sourceMeta.Updated, fetchURL) if err != nil { i.Logger.Warn("failed to fetch file", "url", fetchURL, "error", err) diff --git a/internal/importer/importer.go b/internal/importer/importer.go index 31d13c1..99f7e7a 100644 --- a/internal/importer/importer.go +++ b/internal/importer/importer.go @@ -2,11 +2,11 @@ package importer import ( "context" - "log/slog" "sync" "go.alanpearce.eu/searchix/internal/index" "go.alanpearce.eu/searchix/internal/nix" + "go.alanpearce.eu/x/log" ) type Importer interface { @@ -21,7 +21,7 @@ func process( ctx context.Context, indexer *index.WriteIndex, processor Processor, - logger *slog.Logger, + logger *log.Logger, ) (bool, error) { wg := sync.WaitGroup{} diff --git a/internal/importer/main.go b/internal/importer/main.go index bbd8b6c..4c66501 100644 --- a/internal/importer/main.go +++ b/internal/importer/main.go @@ -3,7 +3,6 @@ package importer import ( "context" "fmt" - "log/slog" "os/exec" "slices" "strings" @@ -12,18 +11,20 @@ import ( "go.alanpearce.eu/searchix/internal/config" "go.alanpearce.eu/searchix/internal/fetcher" "go.alanpearce.eu/searchix/internal/index" + "go.alanpearce.eu/x/log" "github.com/pkg/errors" ) func createSourceImporter( parent context.Context, + log *log.Logger, meta *index.Meta, indexer *index.WriteIndex, forceUpdate bool, ) func(*config.Source) error { return func(source *config.Source) error { - logger := slog.With( + logger := log.With( "name", source.Key, "fetcher", @@ -94,9 +95,17 @@ func createSourceImporter( switch source.Importer { case config.Options: logger.Debug("processor created", "file", fmt.Sprintf("%T", files.Options)) - processor, err = NewOptionProcessor(files.Options, source) + processor, err = NewOptionProcessor( + files.Options, + source, + logger.Named("processor"), + ) case config.Packages: - processor, err = NewPackageProcessor(files.Packages, source) + processor, err = NewPackageProcessor( + files.Packages, + source, + logger.Named("processor"), + ) } if err != nil { return errors.WithMessagef(err, "failed to create processor") @@ -123,17 +132,18 @@ func createSourceImporter( func Start( cfg *config.Config, + log *log.Logger, indexer *index.WriteIndex, forceUpdate bool, onlyUpdateSources *[]string, ) error { if len(cfg.Importer.Sources) == 0 { - slog.Info("No sources enabled") + log.Info("No sources enabled") return nil } - slog.Debug("starting importer", "timeout", cfg.Importer.Timeout.Duration) + log.Debug("starting importer", "timeout", cfg.Importer.Timeout.Duration) importCtx, cancelImport := context.WithTimeout( context.Background(), cfg.Importer.Timeout.Duration, @@ -144,7 +154,7 @@ func Start( meta := indexer.Meta - importSource := createSourceImporter(importCtx, meta, indexer, forceUpdate) + importSource := createSourceImporter(importCtx, log, meta, indexer, forceUpdate) for name, source := range cfg.Importer.Sources { if onlyUpdateSources != nil && len(*onlyUpdateSources) > 0 { if !slices.Contains(*onlyUpdateSources, name) { @@ -153,7 +163,7 @@ func Start( } err := importSource(source) if err != nil { - slog.Error("import failed", "source", name, "error", err) + log.Error("import failed", "source", name, "error", err) } } diff --git a/internal/importer/main_test.go b/internal/importer/main_test.go index cb4d1db..0bc438d 100644 --- a/internal/importer/main_test.go +++ b/internal/importer/main_test.go @@ -1,26 +1,27 @@ package importer import ( - "log/slog" "testing" "go.alanpearce.eu/searchix/internal/config" "go.alanpearce.eu/searchix/internal/index" + "go.alanpearce.eu/x/log" ) var cfg = config.DefaultConfig func BenchmarkImporterLowMemory(b *testing.B) { tmp := b.TempDir() - cfg.LogLevel = slog.LevelDebug + logger := log.Configure(false) _, write, _, err := index.OpenOrCreate(tmp, false, &index.Options{ LowMemory: true, + Logger: logger.Named("index"), }) if err != nil { b.Fatal(err) } - err = Start(&cfg, write, false, &[]string{"nixpkgs"}) + err = Start(&cfg, logger.Named("importer"), write, false, &[]string{"nixpkgs"}) if err != nil { b.Fatal(err) } diff --git a/internal/importer/options.go b/internal/importer/options.go index 290e2e3..763f57f 100644 --- a/internal/importer/options.go +++ b/internal/importer/options.go @@ -3,11 +3,11 @@ package importer import ( "context" "io" - "log/slog" "reflect" "go.alanpearce.eu/searchix/internal/config" "go.alanpearce.eu/searchix/internal/nix" + "go.alanpearce.eu/x/log" "github.com/bcicen/jstream" "github.com/mitchellh/mapstructure" @@ -35,7 +35,7 @@ type nixOptionJSON struct { Type string } -func convertValue(nj *nixValueJSON) *nix.Value { +func (i *OptionIngester) convertValue(nj *nixValueJSON) *nix.Value { if nj == nil { return nil } @@ -49,7 +49,7 @@ func convertValue(nj *nixValueJSON) *nix.Value { Markdown: nix.Markdown(nj.Text), } default: - slog.Warn("got unexpected Value type", "type", nj.Type, "text", nj.Text) + i.log.Warn("got unexpected Value type", "type", nj.Type, "text", nj.Text) return nil } @@ -58,14 +58,20 @@ func convertValue(nj *nixValueJSON) *nix.Value { type OptionIngester struct { dec *jstream.Decoder ms *mapstructure.Decoder + log *log.Logger optJSON nixOptionJSON infile io.ReadCloser source *config.Source } -func NewOptionProcessor(infile io.ReadCloser, source *config.Source) (*OptionIngester, error) { +func NewOptionProcessor( + infile io.ReadCloser, + source *config.Source, + log *log.Logger, +) (*OptionIngester, error) { i := OptionIngester{ dec: jstream.NewDecoder(infile, 1).EmitKV(), + log: log, optJSON: nixOptionJSON{}, infile: infile, source: source, @@ -163,14 +169,14 @@ func (i *OptionIngester) Process(ctx context.Context) (<-chan nix.Importable, <- decs[i] = nix.Link(d) } - // slog.Debug("sending option", "name", kv.Key) + // log.Debug("sending option", "name", kv.Key) results <- nix.Option{ Name: kv.Key, Source: i.source.Key, Declarations: decs, - Default: convertValue(i.optJSON.Default), + Default: i.convertValue(i.optJSON.Default), Description: nix.Markdown(i.optJSON.Description), - Example: convertValue(i.optJSON.Example), + Example: i.convertValue(i.optJSON.Example), RelatedPackages: nix.Markdown(i.optJSON.RelatedPackages), Loc: i.optJSON.Loc, Type: i.optJSON.Type, diff --git a/internal/importer/package.go b/internal/importer/package.go index 5a0ea00..80adc38 100644 --- a/internal/importer/package.go +++ b/internal/importer/package.go @@ -9,6 +9,7 @@ import ( "go.alanpearce.eu/searchix/internal/config" "go.alanpearce.eu/searchix/internal/nix" + "go.alanpearce.eu/x/log" "github.com/bcicen/jstream" "github.com/mitchellh/mapstructure" @@ -40,6 +41,7 @@ type maintainerJSON struct { type PackageIngester struct { dec *jstream.Decoder ms *mapstructure.Decoder + log *log.Logger pkg packageJSON infile io.ReadCloser source *config.Source @@ -60,9 +62,14 @@ func makeAdhocPlatform(v any) string { return string(s) } -func NewPackageProcessor(infile io.ReadCloser, source *config.Source) (*PackageIngester, error) { +func NewPackageProcessor( + infile io.ReadCloser, + source *config.Source, + log *log.Logger, +) (*PackageIngester, error) { i := &PackageIngester{ dec: jstream.NewDecoder(infile, 2).EmitKV(), + log: log, pkg: packageJSON{}, infile: infile, source: source, diff --git a/internal/index/index_meta.go b/internal/index/index_meta.go index fe1b26c..e67c6f2 100644 --- a/internal/index/index_meta.go +++ b/internal/index/index_meta.go @@ -2,11 +2,11 @@ package index import ( "encoding/json" - "log/slog" "os" "time" "go.alanpearce.eu/searchix/internal/file" + "go.alanpearce.eu/x/log" "github.com/pkg/errors" ) @@ -26,10 +26,11 @@ type data struct { type Meta struct { path string + log *log.Logger data } -func createMeta(path string) (*Meta, error) { +func createMeta(path string, log *log.Logger) (*Meta, error) { exists, err := file.Exists(path) if err != nil { return nil, errors.WithMessage(err, "could not check for existence of index metadata") @@ -40,19 +41,20 @@ func createMeta(path string) (*Meta, error) { return &Meta{ path: path, + log: log, data: data{ SchemaVersion: CurrentSchemaVersion, }, }, nil } -func openMeta(path string) (*Meta, error) { +func openMeta(path string, log *log.Logger) (*Meta, error) { exists, err := file.Exists(path) if err != nil { return nil, errors.WithMessage(err, "could not check for existence of index metadata") } if !exists { - return createMeta(path) + return createMeta(path, log) } j, err := os.ReadFile(path) @@ -61,6 +63,7 @@ func openMeta(path string) (*Meta, error) { } meta := Meta{ path: path, + log: log, } err = json.Unmarshal(j, &meta.data) if err != nil { @@ -74,7 +77,7 @@ func openMeta(path string) (*Meta, error) { func (i *Meta) checkSchemaVersion() { if i.SchemaVersion < CurrentSchemaVersion { - slog.Warn( + i.log.Warn( "Index schema version out of date, suggest re-indexing", "schema_version", i.SchemaVersion, @@ -90,7 +93,7 @@ func (i *Meta) Save() error { if err != nil { return errors.WithMessage(err, "could not prepare index metadata for saving") } - slog.Debug("saving index metadata", "path", i.path) + i.log.Debug("saving index metadata", "path", i.path) err = os.WriteFile(i.path, j, 0o600) if err != nil { return errors.WithMessage(err, "could not save index metadata") diff --git a/internal/index/indexer.go b/internal/index/indexer.go index 62edbc1..47701bd 100644 --- a/internal/index/indexer.go +++ b/internal/index/indexer.go @@ -5,8 +5,6 @@ import ( "context" "encoding/gob" "io/fs" - "log" - "log/slog" "math" "os" "path" @@ -14,6 +12,8 @@ import ( "go.alanpearce.eu/searchix/internal/file" "go.alanpearce.eu/searchix/internal/nix" + "go.alanpearce.eu/x/log" + "go.uber.org/zap" "github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2/analysis/analyzer/custom" @@ -30,6 +30,7 @@ import ( type WriteIndex struct { index bleve.Index + log *log.Logger Meta *Meta } @@ -190,6 +191,7 @@ func deleteIndex(dataRoot string) error { type Options struct { LowMemory bool + Logger *log.Logger } func OpenOrCreate( @@ -198,7 +200,7 @@ func OpenOrCreate( options *Options, ) (*ReadIndex, *WriteIndex, bool, error) { var err error - bleve.SetLog(log.Default()) + bleve.SetLog(zap.NewStdLog(options.Logger.Named("bleve").GetLogger())) indexPath := path.Join(dataRoot, indexBaseName) metaPath := path.Join(dataRoot, metaBaseName) @@ -226,7 +228,7 @@ func OpenOrCreate( return nil, nil, false, err } - meta, err = createMeta(metaPath) + meta, err = createMeta(metaPath, options.Logger) if err != nil { return nil, nil, false, err } @@ -237,7 +239,7 @@ func OpenOrCreate( return nil, nil, exists, errors.WithMessagef(err, "could not open index at path %s", indexPath) } - meta, err = openMeta(metaPath) + meta, err = openMeta(metaPath, options.Logger) if err != nil { return nil, nil, exists, err } @@ -248,12 +250,14 @@ func OpenOrCreate( } return &ReadIndex{ - idx, - meta, + index: idx, + log: options.Logger, + meta: meta, }, &WriteIndex{ - idx, - meta, + index: idx, + log: options.Logger, + Meta: meta, }, exists, nil @@ -280,7 +284,7 @@ func (i *WriteIndex) Import( for obj := range objects { select { case <-ctx.Done(): - slog.Warn("import aborted") + i.log.Warn("import aborted") break outer default: @@ -305,7 +309,7 @@ func (i *WriteIndex) Import( field := document.NewTextFieldWithIndexingOptions("_data", nil, data.Bytes(), indexAPI.StoreField) newDoc := doc.AddField(field) - // slog.Debug("adding object to index", "name", opt.Name) + // log.Debug("adding object to index", "name", opt.Name) err = batch.IndexAdvanced(newDoc) if err != nil { @@ -340,7 +344,7 @@ func (i *WriteIndex) Flush(batch *bleve.Batch) error { error: errors.New("no documents to flush"), } } - slog.Debug("flushing batch", "size", size) + i.log.Debug("flushing batch", "size", size) err := i.index.Batch(batch) if err != nil { diff --git a/internal/index/search.go b/internal/index/search.go index dc19db4..03a8e60 100644 --- a/internal/index/search.go +++ b/internal/index/search.go @@ -7,6 +7,7 @@ import ( "go.alanpearce.eu/searchix/internal/config" "go.alanpearce.eu/searchix/internal/nix" + "go.alanpearce.eu/x/log" "github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2/analysis/analyzer/standard" @@ -29,6 +30,7 @@ type Result struct { type ReadIndex struct { index bleve.Index + log *log.Logger meta *Meta } diff --git a/internal/server/dev.go b/internal/server/dev.go index 17489d3..f5fd4fd 100644 --- a/internal/server/dev.go +++ b/internal/server/dev.go @@ -3,37 +3,41 @@ package server import ( "fmt" "io/fs" - "log/slog" "os" "path/filepath" "time" "github.com/fsnotify/fsnotify" "github.com/pkg/errors" + "go.alanpearce.eu/x/log" ) type FileWatcher struct { - *fsnotify.Watcher + watcher *fsnotify.Watcher + log *log.Logger } -func NewFileWatcher() (*FileWatcher, error) { +func NewFileWatcher(log *log.Logger) (*FileWatcher, error) { watcher, err := fsnotify.NewWatcher() if err != nil { return nil, errors.WithMessage(err, "could not create watcher") } - return &FileWatcher{watcher}, nil + return &FileWatcher{ + watcher, + log, + }, nil } -func (watcher FileWatcher) AddRecursive(from string) error { - slog.Debug(fmt.Sprintf("watching files under %s", from)) +func (i FileWatcher) AddRecursive(from string) error { + i.log.Debug(fmt.Sprintf("watching files under %s", from)) err := filepath.WalkDir(from, func(path string, entry fs.DirEntry, err error) error { if err != nil { return errors.WithMessagef(err, "could not walk directory %s", path) } if entry.IsDir() { - slog.Debug(fmt.Sprintf("adding directory %s to watcher", path)) - if err = watcher.Add(path); err != nil { + i.log.Debug(fmt.Sprintf("adding directory %s to watcher", path)) + if err = i.watcher.Add(path); err != nil { return errors.WithMessagef(err, "could not add directory %s to watcher", path) } } @@ -44,18 +48,18 @@ func (watcher FileWatcher) AddRecursive(from string) error { return errors.WithMessage(err, "error walking directory tree") } -func (watcher FileWatcher) Start(callback func(string)) { +func (i FileWatcher) Start(callback func(string)) { for { select { - case event := <-watcher.Events: + case event := <-i.watcher.Events: if event.Has(fsnotify.Create) || event.Has(fsnotify.Rename) { f, err := os.Stat(event.Name) if err != nil { - slog.Error(fmt.Sprintf("error handling %s event: %v", event.Op.String(), err)) + i.log.Error(fmt.Sprintf("error handling %s event: %v", event.Op.String(), err)) } else if f.IsDir() { - err = watcher.Add(event.Name) + err = i.watcher.Add(event.Name) if err != nil { - slog.Error(fmt.Sprintf("error adding new folder to watcher: %v", err)) + i.log.Error(fmt.Sprintf("error adding new folder to watcher: %v", err)) } } } @@ -63,8 +67,8 @@ func (watcher FileWatcher) Start(callback func(string)) { callback(event.Name) time.Sleep(500 * time.Millisecond) } - case err := <-watcher.Errors: - slog.Error(fmt.Sprintf("error in watcher: %v", err)) + case err := <-i.watcher.Errors: + i.log.Error(fmt.Sprintf("error in watcher: %v", err)) } } } diff --git a/internal/server/error.go b/internal/server/error.go index d9d6778..1e04bbc 100644 --- a/internal/server/error.go +++ b/internal/server/error.go @@ -1,15 +1,16 @@ package server import ( - "log/slog" "net/http" "go.alanpearce.eu/searchix/internal/components" "go.alanpearce.eu/searchix/internal/config" + "go.alanpearce.eu/x/log" ) func createErrorHandler( config *config.Config, + log *log.Logger, ) func(http.ResponseWriter, *http.Request, string, int) { return func(w http.ResponseWriter, r *http.Request, message string, code int) { var err error @@ -31,7 +32,7 @@ func createErrorHandler( err = components.ErrorPage(indexData).Render(r.Context(), w) } if err != nil { - slog.Error( + log.Error( "error rendering error page template", "error", err, diff --git a/internal/server/logging.go b/internal/server/logging.go index 372972f..6e2f7c8 100644 --- a/internal/server/logging.go +++ b/internal/server/logging.go @@ -1,11 +1,10 @@ package server import ( - "fmt" - "io" "net/http" "github.com/pkg/errors" + "go.alanpearce.eu/x/log" ) type LoggingResponseWriter struct { @@ -42,7 +41,7 @@ func NewLoggingResponseWriter(w http.ResponseWriter) *LoggingResponseWriter { type wrappedHandlerOptions struct { defaultHostname string - logger io.Writer + logger *log.Logger } func wrapHandlerWithLogging(wrappedHandler http.Handler, opts wrappedHandlerOptions) http.Handler { @@ -54,13 +53,17 @@ func wrapHandlerWithLogging(wrappedHandler http.Handler, opts wrappedHandlerOpti lw := NewLoggingResponseWriter(w) wrappedHandler.ServeHTTP(lw, r) statusCode := lw.statusCode - fmt.Fprintf( - opts.logger, - "%s %s %d %s %s\n", + opts.logger.Info( + "http request", + "scheme", scheme, + "method", r.Method, + "code", statusCode, + "path", r.URL.Path, + "location", lw.Header().Get("Location"), ) }) diff --git a/internal/server/mux.go b/internal/server/mux.go index 16a0150..17445e5 100644 --- a/internal/server/mux.go +++ b/internal/server/mux.go @@ -4,12 +4,9 @@ import ( "context" "encoding/xml" "fmt" - "io" - "log/slog" "math" "net/http" "net/url" - "os" "path" "strconv" "time" @@ -19,11 +16,11 @@ import ( "go.alanpearce.eu/searchix/internal/config" search "go.alanpearce.eu/searchix/internal/index" "go.alanpearce.eu/searchix/internal/opensearch" + "go.alanpearce.eu/x/log" sentryhttp "github.com/getsentry/sentry-go/http" "github.com/osdevisnot/sorvor/pkg/livereload" "github.com/pkg/errors" - "github.com/shengyanli1982/law" ) type HTTPError struct { @@ -57,6 +54,7 @@ func sortSources(ss map[string]*config.Source) { func NewMux( cfg *config.Config, index *search.ReadIndex, + log *log.Logger, liveReload bool, ) (*http.ServeMux, error) { if cfg == nil { @@ -70,7 +68,7 @@ func NewMux( }) sortSources(cfg.Importer.Sources) - errorHandler := createErrorHandler(cfg) + errorHandler := createErrorHandler(cfg, log) top := http.NewServeMux() mux := http.NewServeMux() @@ -118,7 +116,7 @@ func NewMux( return } - slog.Error("search error", "error", err) + log.Error("search error", "error", err) errorHandler(w, r, err.Error(), http.StatusInternalServerError) return @@ -177,7 +175,7 @@ func NewMux( err = components.ResultsPage(tdata).Render(r.Context(), w) } if err != nil { - slog.Error("template error", "template", importerType, "error", err) + log.Error("template error", "template", importerType, "error", err) errorHandler(w, r, err.Error(), http.StatusInternalServerError) } } else { @@ -258,7 +256,7 @@ func NewMux( err = components.DetailPage(tdata.TemplateData, *doc).Render(r.Context(), w) } if err != nil { - slog.Error("template error", "template", importerSingular, "error", err) + log.Error("template error", "template", importerSingular, "error", err) errorHandler(w, r, err.Error(), http.StatusInternalServerError) } } @@ -332,7 +330,7 @@ func NewMux( liveReload := livereload.New() liveReload.Start() top.Handle("/livereload", liveReload) - fw, err := NewFileWatcher() + fw, err := NewFileWatcher(log.Named("watcher")) if err != nil { return nil, errors.WithMessage(err, "could not create file watcher") } @@ -341,29 +339,23 @@ func NewMux( return nil, errors.WithMessage(err, "could not add directory to file watcher") } go fw.Start(func(filename string) { - slog.Debug(fmt.Sprintf("got filename %s", filename)) + log.Debug(fmt.Sprintf("got filename %s", filename)) if match, _ := path.Match("frontend/static/*", filename); match { err := frontend.Rehash() if err != nil { - slog.Error("failed to re-hash frontend assets", "error", err) + log.Error("failed to re-hash frontend assets", "error", err) } } liveReload.Reload() }) } - var logWriter io.Writer - if cfg.Web.Environment == "production" { - logWriter = law.NewWriteAsyncer(os.Stdout, nil) - } else { - logWriter = os.Stdout - } top.Handle("/", AddHeadersMiddleware( sentryHandler.Handle( wrapHandlerWithLogging(mux, wrappedHandlerOptions{ defaultHostname: cfg.Web.BaseURL.Hostname(), - logger: logWriter, + logger: log, }), ), cfg, diff --git a/internal/server/server.go b/internal/server/server.go index c3c2a4d..aacef30 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -2,7 +2,6 @@ package server import ( "context" - "log/slog" "net" "net/http" "strconv" @@ -10,6 +9,7 @@ import ( "go.alanpearce.eu/searchix/internal/config" "go.alanpearce.eu/searchix/internal/index" + "go.alanpearce.eu/x/log" "github.com/pkg/errors" "golang.org/x/net/http2" @@ -18,18 +18,25 @@ import ( type Server struct { cfg *config.Config + log *log.Logger server *http.Server listener net.Listener } -func New(conf *config.Config, index *index.ReadIndex, liveReload bool) (*Server, error) { - mux, err := NewMux(conf, index, liveReload) +func New( + conf *config.Config, + index *index.ReadIndex, + log *log.Logger, + liveReload bool, +) (*Server, error) { + mux, err := NewMux(conf, index, log, liveReload) if err != nil { return nil, err } return &Server{ cfg: conf, + log: log, server: &http.Server{ Handler: http.MaxBytesHandler( h2c.NewHandler(mux, &http2.Server{ @@ -56,7 +63,7 @@ func (s *Server) Start() error { s.listener = l if s.cfg.Web.Environment == "development" { - slog.Info( + s.log.Info( "server listening on", "url", s.cfg.Web.BaseURL.String(), @@ -71,19 +78,19 @@ func (s *Server) Start() error { } func (s *Server) Stop() chan struct{} { - slog.Debug("stop called") + s.log.Debug("stop called") idleConnsClosed := make(chan struct{}) go func() { - slog.Debug("shutting down server") + s.log.Debug("shutting down server") ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() err := s.server.Shutdown(ctx) - slog.Debug("server shut down") + s.log.Debug("server shut down") if err != nil { // Error from closing listeners, or context timeout: - slog.Error("error shutting down server", "error", err) + s.log.Error("error shutting down server", "error", err) } s.listener.Close() close(idleConnsClosed) -- cgit 1.4.1