lens @ c3ea735c0f03a0827a8e753a5b5adf6e31f4c925

feat: Migrate from logrus to slog
  1diff --git a/cmd/server/main.go b/cmd/server/main.go
  2index 41b2b4a781170e387e42db4d187b3ff91fbfd7d5..c2160c4890eeba599b53517c8d89246262764350 100644
  3--- a/cmd/server/main.go
  4+++ b/cmd/server/main.go
  5@@ -4,6 +4,7 @@ import (
  6 	"context"
  7 	"encoding/base64"
  8 	"errors"
  9+	"log/slog"
 10 	"net/http"
 11 	"os"
 12 	"os/signal"
 13@@ -11,7 +12,6 @@ 	"time"
 14 
 15 	"github.com/glebarez/sqlite"
 16 	"github.com/gorilla/mux"
 17-	"github.com/sirupsen/logrus"
 18 	flag "github.com/spf13/pflag"
 19 	"gorm.io/driver/mysql"
 20 	"gorm.io/driver/postgres"
 21@@ -33,19 +33,20 @@ 	var (
 22 		key            = flag.String("aes-key", "", "AES key, either 16, 24, or 32 bytes string to select AES-128, AES-192, or AES-256")
 23 		dbType         = flag.String("db-type", "sqlite", "Database to be used. Choose either mysql, psql or sqlite")
 24 		dbCon          = flag.String("db-con", "main.db", "Database string connection for given database type. Ref: https://gorm.io/docs/connecting_to_the_database.html")
 25-		logLevel       = flag.String("log-level", "error", "Log level: Choose either trace, debug, info, warning, error, fatal or panic")
 26+		logLevel       = flag.String("log-level", "error", "Log level: Choose either debug, info, warning, error")
 27 		schedulerCount = flag.Uint("scheduler-count", 10, "How many workers are created to process media files")
 28 		cachePath      = flag.String("cache-path", "", "Folder to store thumbnail image")
 29 	)
 30 
 31 	flag.Parse()
 32 
 33-	l, err := logrus.ParseLevel(*logLevel)
 34-	if err != nil {
 35-		panic("failed to parse log level" + err.Error())
 36-	}
 37-	logger := logrus.New()
 38-	logger.SetLevel(l)
 39+	level := parseLogLevel(*logLevel)
 40+
 41+	handler := slog.NewTextHandler(
 42+		os.Stdout,
 43+		&slog.HandlerOptions{Level: level},
 44+	)
 45+	logger := slog.New(handler)
 46 
 47 	d, err := OpenDatabase(*dbType, *dbCon)
 48 	if err != nil {
 49@@ -53,7 +54,7 @@ 		panic("failed to parse database strings" + err.Error())
 50 	}
 51 
 52 	db, err := gorm.Open(d, &gorm.Config{
 53-		Logger: ext.Wraplog(logger.WithField("context", "sql")),
 54+		Logger: ext.Wraplog(logger.With("context", "sql")),
 55 	})
 56 	if err != nil {
 57 		panic("failed to connect database: " + err.Error())
 58@@ -85,8 +86,8 @@ 	)
 59 
 60 	// middleware
 61 	var (
 62-		authMiddleware    = ext.NewAuthMiddleware(baseKey, logger.WithField("context", "auth"), userRepository)
 63-		logMiddleware     = ext.NewLogMiddleare(logger.WithField("context", "http"))
 64+		authMiddleware    = ext.NewAuthMiddleware(baseKey, logger.With("context", "auth"), userRepository)
 65+		logMiddleware     = ext.NewLogMiddleare(logger.With("context", "http"))
 66 		initialMiddleware = ext.NewInitialSetupMiddleware(userRepository)
 67 	)
 68 
 69@@ -129,22 +130,22 @@ 		serverTask = worker.NewServerTask(&http.Server{Handler: r, Addr: "0.0.0.0:8080"})
 70 		fileTask   = worker.NewTaskFromChanProcessor[string](
 71 			fileScanner,
 72 			scheduler,
 73-			logrus.WithField("context", "file scanner"),
 74+			logger.With("context", "file scanner"),
 75 		)
 76 		exifTask = worker.NewTaskFromBatchProcessor[*repository.Media](
 77 			exifScanner,
 78 			scheduler,
 79-			logrus.WithField("context", "exif scanner"),
 80+			logger.With("context", "exif scanner"),
 81 		)
 82 		thumbnailTask = worker.NewTaskFromBatchProcessor[*repository.Media](
 83 			thumbnailScanner,
 84 			scheduler,
 85-			logrus.WithField("context", "thumbnail scanner"),
 86+			logger.With("context", "thumbnail scanner"),
 87 		)
 88 		albumTask = worker.NewTaskFromSerialProcessor[*repository.Media](
 89 			albumScanner,
 90 			scheduler,
 91-			logrus.WithField("context", "thumbnail scanner"),
 92+			logger.With("context", "thumbnail scanner"),
 93 		)
 94 	)
 95 
 96@@ -173,3 +174,18 @@ 	default:
 97 		return nil, errors.New("No valid db type given")
 98 	}
 99 }
100+
101+func parseLogLevel(input string) slog.Level {
102+	switch input {
103+	case "debug":
104+		return slog.LevelDebug
105+	case "info":
106+		return slog.LevelInfo
107+	case "warn":
108+		return slog.LevelWarn
109+	case "error":
110+		return slog.LevelError
111+	default:
112+		return slog.LevelWarn
113+	}
114+}
115diff --git a/go.mod b/go.mod
116index 6f8eb28073abaad1cb5b5eb126d07cdc6fe79c3d..b8e4bb7bc9e76aa474179b2fc77cc1842c457451 100644
117--- a/go.mod
118+++ b/go.mod
119@@ -1,6 +1,6 @@
120 module git.sr.ht/~gabrielgio/img
121 
122-go 1.19
123+go 1.21
124 
125 require (
126 	github.com/barasher/go-exiftool v1.10.0
127@@ -8,14 +8,12 @@ 	github.com/glebarez/sqlite v1.9.0
128 	github.com/google/go-cmp v0.5.9
129 	github.com/gorilla/mux v1.8.0
130 	github.com/h2non/bimg v1.1.9
131-	github.com/sirupsen/logrus v1.9.2
132 	github.com/spf13/pflag v1.0.5
133 	github.com/valyala/quicktemplate v1.7.0
134 	golang.org/x/crypto v0.8.0
135 	gorm.io/driver/mysql v1.5.1
136 	gorm.io/driver/postgres v1.5.2
137 	gorm.io/driver/sqlite v1.5.0
138-	gorm.io/gen v0.3.22
139 	gorm.io/gorm v1.25.2
140 )
141 
142@@ -33,13 +31,8 @@ 	github.com/mattn/go-isatty v0.0.17 // indirect
143 	github.com/mattn/go-sqlite3 v1.14.16 // indirect
144 	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
145 	github.com/valyala/bytebufferpool v1.0.0 // indirect
146-	golang.org/x/mod v0.10.0 // indirect
147 	golang.org/x/sys v0.8.0 // indirect
148 	golang.org/x/text v0.10.0 // indirect
149-	golang.org/x/tools v0.9.3 // indirect
150-	gorm.io/datatypes v1.2.0 // indirect
151-	gorm.io/hints v1.1.2 // indirect
152-	gorm.io/plugin/dbresolver v1.4.1 // indirect
153 	modernc.org/libc v1.22.5 // indirect
154 	modernc.org/mathutil v1.5.0 // indirect
155 	modernc.org/memory v1.5.0 // indirect
156diff --git a/go.sum b/go.sum
157index 6f654534983a5797bbba47b3fa8e4ce79145933d..03f8b3e75ff3a728a009724d1556164ca2a4caf2 100644
158--- a/go.sum
159+++ b/go.sum
160@@ -11,16 +11,14 @@ github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
161 github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
162 github.com/glebarez/sqlite v1.9.0 h1:Aj6bPA12ZEx5GbSF6XADmCkYXlljPNUY+Zf1EQxynXs=
163 github.com/glebarez/sqlite v1.9.0/go.mod h1:YBYCoyupOao60lzp1MVBLEjZfgkq0tdB1voAQ09K9zw=
164-github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
165 github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
166 github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
167 github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
168-github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
169-github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
170 github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
171 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
172 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
173 github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
174+github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
175 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
176 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
177 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
178@@ -35,7 +33,6 @@ github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
179 github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
180 github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
181 github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
182-github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
183 github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
184 github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
185 github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
186@@ -45,20 +42,18 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
187 github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
188 github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
189 github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
190-github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
191 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
192 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
193 github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
194 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
195 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
196-github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y=
197-github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
198 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
199 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
200 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
201 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
202 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
203 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
204+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
205 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
206 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
207 github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus=
208@@ -68,15 +63,11 @@ github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
209 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
210 golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
211 golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
212-golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
213-golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
214 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
215 golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
216-golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
217 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
218 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
219 golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
220-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
221 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
222 golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
223 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
224@@ -86,34 +77,20 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
225 golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
226 golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
227 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
228-golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
229-golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
230 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
231 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
232 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
233-gorm.io/datatypes v1.2.0 h1:5YT+eokWdIxhJgWHdrb2zYUimyk0+TaFth+7a0ybzco=
234-gorm.io/datatypes v1.2.0/go.mod h1:o1dh0ZvjIjhH/bngTpypG6lVRJ5chTBxE09FH/71k04=
235-gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
236+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
237 gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
238 gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
239 gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0=
240 gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8=
241 gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c=
242 gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I=
243-gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
244-gorm.io/gen v0.3.22 h1:K7u5tCyaZfe1cbQFD8N2xrTqUuqximNFSRl7zOFPq+M=
245-gorm.io/gen v0.3.22/go.mod h1:dQcELeF/7Kf82M6AQF+O/rKT5r1sjv49TlGz0cerPn4=
246-gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
247-gorm.io/gorm v1.24.3/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
248 gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
249-gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
250 gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
251 gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
252 gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
253-gorm.io/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o=
254-gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg=
255-gorm.io/plugin/dbresolver v1.4.1 h1:Ug4LcoPhrvqq71UhxtF346f+skTYoCa/nEsdjvHwEzk=
256-gorm.io/plugin/dbresolver v1.4.1/go.mod h1:CTbCtMWhsjXSiJqiW2R8POvJ2cq18RVOl4WGyT5nhNc=
257 modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
258 modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
259 modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
260diff --git a/pkg/ext/gorm_logger.go b/pkg/ext/gorm_logger.go
261index bfbbb1ebfc55238016b954ad72f80d07d985311b..f0ab59264e7ff3efdea2fe57e613c52c628af9cf 100644
262--- a/pkg/ext/gorm_logger.go
263+++ b/pkg/ext/gorm_logger.go
264@@ -3,15 +3,15 @@
265 import (
266 	"context"
267 	"fmt"
268+	"log/slog"
269 	"time"
270 
271-	"github.com/sirupsen/logrus"
272 	"gorm.io/gorm/logger"
273 	"gorm.io/gorm/utils"
274 )
275 
276 type Log struct {
277-	logrus *logrus.Entry
278+	logger *slog.Logger
279 }
280 
281 func getFullMsg(msg string, data ...interface{}) string {
282@@ -24,35 +24,29 @@ }
283 
284 func (self *Log) Info(ctx context.Context, msg string, data ...interface{}) {
285 	fullMsg := getFullMsg(msg, data)
286-	self.logrus.
287-		WithContext(ctx).
288-		Info(fullMsg)
289+	self.logger.InfoContext(ctx, fullMsg)
290 }
291 
292 func (self *Log) Warn(ctx context.Context, msg string, data ...interface{}) {
293 	fullMsg := getFullMsg(msg, data)
294-	self.logrus.
295-		WithContext(ctx).
296-		Warn(fullMsg)
297+	self.logger.
298+		WarnContext(ctx, fullMsg)
299 }
300 func (self *Log) Error(ctx context.Context, msg string, data ...interface{}) {
301 	fullMsg := getFullMsg(msg, data)
302-	self.logrus.
303-		WithContext(ctx).
304-		Error(fullMsg)
305+	self.logger.
306+		ErrorContext(ctx, fullMsg)
307 }
308 
309 func (self *Log) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), _ error) {
310 	elapsed := time.Since(begin)
311 	sql, _ := fc()
312-	self.logrus.
313-		WithContext(ctx).
314-		WithField("time", elapsed).
315-		Printf(sql)
316+	self.logger.
317+		InfoContext(ctx, sql, slog.Duration("elapsed", elapsed))
318 }
319 
320-func Wraplog(log *logrus.Entry) *Log {
321+func Wraplog(log *slog.Logger) *Log {
322 	return &Log{
323-		logrus: log,
324+		logger: log,
325 	}
326 }
327diff --git a/pkg/ext/middleware.go b/pkg/ext/middleware.go
328index 6a94c4f9442ed01ba1a98eb375b3c106aeda81e5..38bacca92dc571629e62545f05d3a89362f9d758 100644
329--- a/pkg/ext/middleware.go
330+++ b/pkg/ext/middleware.go
331@@ -4,10 +4,9 @@ import (
332 	"context"
333 	"encoding/base64"
334 	"errors"
335+	"log/slog"
336 	"net/http"
337 	"time"
338-
339-	"github.com/sirupsen/logrus"
340 
341 	"git.sr.ht/~gabrielgio/img/pkg/database/repository"
342 	"git.sr.ht/~gabrielgio/img/pkg/service"
343@@ -24,7 +23,7 @@ type (
344 	User string
345 
346 	LogMiddleware struct {
347-		entry *logrus.Entry
348+		logger *slog.Logger
349 	}
350 )
351 
352@@ -32,9 +31,9 @@ const (
353 	UserKey User = "user"
354 )
355 
356-func NewLogMiddleare(log *logrus.Entry) *LogMiddleware {
357+func NewLogMiddleare(log *slog.Logger) *LogMiddleware {
358 	return &LogMiddleware{
359-		entry: log,
360+		logger: log,
361 	}
362 }
363 
364@@ -43,27 +42,29 @@ 	return func(w http.ResponseWriter, r *http.Request) {
365 		start := time.Now()
366 		next(w, r)
367 		elapsed := time.Since(start)
368-		l.entry.
369-			WithField("time", elapsed).
370-			WithField("path", r.URL.Path).
371-			Info(r.Method)
372+		l.logger.Info(
373+			r.Method,
374+			slog.Duration("elapsed", elapsed),
375+			slog.String("path", r.URL.Path),
376+		)
377+
378 	}
379 }
380 
381 type AuthMiddleware struct {
382 	key            []byte
383-	entry          *logrus.Entry
384+	logger         *slog.Logger
385 	userRepository repository.UserRepository
386 }
387 
388 func NewAuthMiddleware(
389 	key []byte,
390-	log *logrus.Entry,
391+	logger *slog.Logger,
392 	userRepository repository.UserRepository,
393 ) *AuthMiddleware {
394 	return &AuthMiddleware{
395 		key:            key,
396-		entry:          log.WithField("context", "auth"),
397+		logger:         logger,
398 		userRepository: userRepository,
399 	}
400 }
401@@ -79,35 +80,37 @@
402 		redirectLogin := "/login?redirect=" + path
403 		authBase64, err := r.Cookie("auth")
404 		if errors.Is(err, http.ErrNoCookie) {
405-			a.entry.Info("No auth provided")
406+			a.logger.Info("No auth provided")
407 			http.Redirect(w, r, redirectLogin, http.StatusTemporaryRedirect)
408 			return
409 		}
410 
411 		auth, err := base64.StdEncoding.DecodeString(authBase64.Value)
412 		if err != nil {
413-			a.entry.Error(err)
414+			a.logger.Error(err.Error())
415 			return
416 		}
417 
418 		token, err := service.ReadToken(auth, a.key)
419 		if err != nil {
420-			a.entry.Error(err)
421+			a.logger.Error(err.Error())
422 			http.Redirect(w, r, redirectLogin, http.StatusTemporaryRedirect)
423 			return
424 		}
425 
426 		user, err := a.userRepository.Get(r.Context(), token.UserID)
427 		if err != nil {
428-			a.entry.Error(err)
429+			a.logger.Error(err.Error())
430 			return
431 		}
432 
433 		r = r.WithContext(context.WithValue(r.Context(), UserKey, user))
434-		a.entry.
435-			WithField("userID", token.UserID).
436-			WithField("username", token.Username).
437-			Info("user recognized")
438+		a.logger.
439+			Info(
440+				"user recognized",
441+				slog.Uint64("userid", uint64(token.UserID)),
442+				slog.String("username", token.Username),
443+			)
444 		next(w, r)
445 	}
446 }
447diff --git a/pkg/worker/list_processor.go b/pkg/worker/list_processor.go
448index ea6b45372e607368892aeb2d038c25a09d65e437..c4c37819c2c8542e7d39d30ba753bbc2457b7005 100644
449--- a/pkg/worker/list_processor.go
450+++ b/pkg/worker/list_processor.go
451@@ -3,9 +3,8 @@
452 import (
453 	"context"
454 	"errors"
455+	"log/slog"
456 	"sync"
457-
458-	"github.com/sirupsen/logrus"
459 )
460 
461 type (
462@@ -27,19 +26,19 @@ 	}
463 
464 	chanProcessorTask[T any] struct {
465 		chanProcessor ChanProcessor[T]
466-		logrus        *logrus.Entry
467+		logger        *slog.Logger
468 		scheduler     *Scheduler
469 	}
470 
471 	batchProcessorTask[T any] struct {
472 		batchProcessor ListProcessor[T]
473-		logrus         *logrus.Entry
474+		logger         *slog.Logger
475 		scheduler      *Scheduler
476 	}
477 
478 	serialProcessorTask[T any] struct {
479 		batchProcessor ListProcessor[T]
480-		logrus         *logrus.Entry
481+		logger         *slog.Logger
482 		scheduler      *Scheduler
483 	}
484 )
485@@ -47,36 +46,36 @@
486 func NewTaskFromBatchProcessor[T any](
487 	batchProcessor ListProcessor[T],
488 	scheduler *Scheduler,
489-	logrus *logrus.Entry,
490+	logger *slog.Logger,
491 ) Task {
492 	return &batchProcessorTask[T]{
493 		batchProcessor: batchProcessor,
494 		scheduler:      scheduler,
495-		logrus:         logrus,
496+		logger:         logger,
497 	}
498 }
499 
500 func NewTaskFromSerialProcessor[T any](
501 	batchProcessor ListProcessor[T],
502 	scheduler *Scheduler,
503-	logrus *logrus.Entry,
504+	logger *slog.Logger,
505 ) Task {
506 	return &serialProcessorTask[T]{
507 		batchProcessor: batchProcessor,
508 		scheduler:      scheduler,
509-		logrus:         logrus,
510+		logger:         logger,
511 	}
512 }
513 
514 func NewTaskFromChanProcessor[T any](
515 	chanProcessor ChanProcessor[T],
516 	scheduler *Scheduler,
517-	logrus *logrus.Entry,
518+	logger *slog.Logger,
519 ) Task {
520 	return &chanProcessorTask[T]{
521 		chanProcessor: chanProcessor,
522 		scheduler:     scheduler,
523-		logrus:        logrus,
524+		logger:        logger,
525 	}
526 }
527 
528@@ -111,7 +110,10 @@ 			go func(v T) {
529 				defer l.scheduler.Return()
530 				defer wg.Done()
531 				if err := l.batchProcessor.Process(ctx, v); err != nil && !errors.Is(err, context.Canceled) {
532-					l.logrus.WithError(err).Error("Error processing batch")
533+					l.logger.Error(
534+						"Error processing batch",
535+						slog.String("error", err.Error()),
536+					)
537 					if failure, ok := l.batchProcessor.(OnFail[T]); ok {
538 						failure.OnFail(ctx, v, err)
539 					}
540@@ -148,7 +150,10 @@ 			}
541 
542 			l.scheduler.Take()
543 			if err := l.batchProcessor.Process(ctx, v); err != nil && !errors.Is(err, context.Canceled) {
544-				l.logrus.WithError(err).Error("Error processing batch")
545+				l.logger.Error(
546+					"Error processing batch",
547+					slog.String("error", err.Error()),
548+				)
549 				if failure, ok := l.batchProcessor.(OnFail[T]); ok {
550 					failure.OnFail(ctx, v, err)
551 				}
552@@ -177,7 +182,13 @@ 			l.scheduler.Take()
553 			go func(v T) {
554 				defer l.scheduler.Return()
555 				if err := l.chanProcessor.Process(ctx, v); err != nil {
556-					l.logrus.WithError(err).Error("Error processing chan")
557+					l.logger.Error(
558+						"Error processing batch",
559+						slog.String("error", err.Error()),
560+					)
561+					if failure, ok := l.chanProcessor.(OnFail[T]); ok {
562+						failure.OnFail(ctx, v, err)
563+					}
564 				}
565 			}(v)
566 		}
567diff --git a/pkg/worker/list_processor_test.go b/pkg/worker/list_processor_test.go
568index abdb90792f9713c640180c778f8db69e9abfe388..21489e82685e62ed1e6f85d04f5209dbc59d7feb 100644
569--- a/pkg/worker/list_processor_test.go
570+++ b/pkg/worker/list_processor_test.go
571@@ -5,11 +5,10 @@
572 import (
573 	"context"
574 	"errors"
575+	"log/slog"
576 	"math/rand"
577 	"sync"
578 	"testing"
579-
580-	"github.com/sirupsen/logrus"
581 
582 	"git.sr.ht/~gabrielgio/img/pkg/testkit"
583 )
584@@ -27,12 +26,12 @@ )
585 
586 func TestListProcessorLimit(t *testing.T) {
587 	var (
588-		log       = logrus.New()
589+		log       = slog.Default()
590 		scheduler = NewScheduler(1)
591 		mock      = &mockCounterListProcessor{countTo: 10000}
592 	)
593 
594-	worker := NewTaskFromBatchProcessor[int](mock, scheduler, log.WithField("context", "testing"))
595+	worker := NewTaskFromBatchProcessor[int](mock, scheduler, log.With("context", "testing"))
596 
597 	err := worker.Start(context.Background())
598 	testkit.TestFatalError(t, "Start", err)
599@@ -42,12 +41,12 @@ }
600 
601 func TestListProcessorContextCancelQuery(t *testing.T) {
602 	var (
603-		log       = logrus.New()
604+		log       = slog.Default()
605 		scheduler = NewScheduler(1)
606 		mock      = &mockContextListProcessor{}
607 	)
608 
609-	worker := NewTaskFromBatchProcessor[int](mock, scheduler, log.WithField("context", "testing"))
610+	worker := NewTaskFromBatchProcessor[int](mock, scheduler, log.With("context", "testing"))
611 
612 	ctx, cancel := context.WithCancel(context.Background())
613 	var wg sync.WaitGroup