lens @ c3ea735c0f03a0827a8e753a5b5adf6e31f4c925

feat: Migrate from logrus to slog
diff --git a/cmd/server/main.go b/cmd/server/main.go
index 41b2b4a781170e387e42db4d187b3ff91fbfd7d5..c2160c4890eeba599b53517c8d89246262764350 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"encoding/base64"
 	"errors"
+	"log/slog"
 	"net/http"
 	"os"
 	"os/signal"
@@ -11,7 +12,6 @@ 	"time"
 
 	"github.com/glebarez/sqlite"
 	"github.com/gorilla/mux"
-	"github.com/sirupsen/logrus"
 	flag "github.com/spf13/pflag"
 	"gorm.io/driver/mysql"
 	"gorm.io/driver/postgres"
@@ -33,19 +33,20 @@ 	var (
 		key            = flag.String("aes-key", "", "AES key, either 16, 24, or 32 bytes string to select AES-128, AES-192, or AES-256")
 		dbType         = flag.String("db-type", "sqlite", "Database to be used. Choose either mysql, psql or sqlite")
 		dbCon          = flag.String("db-con", "main.db", "Database string connection for given database type. Ref: https://gorm.io/docs/connecting_to_the_database.html")
-		logLevel       = flag.String("log-level", "error", "Log level: Choose either trace, debug, info, warning, error, fatal or panic")
+		logLevel       = flag.String("log-level", "error", "Log level: Choose either debug, info, warning, error")
 		schedulerCount = flag.Uint("scheduler-count", 10, "How many workers are created to process media files")
 		cachePath      = flag.String("cache-path", "", "Folder to store thumbnail image")
 	)
 
 	flag.Parse()
 
-	l, err := logrus.ParseLevel(*logLevel)
-	if err != nil {
-		panic("failed to parse log level" + err.Error())
-	}
-	logger := logrus.New()
-	logger.SetLevel(l)
+	level := parseLogLevel(*logLevel)
+
+	handler := slog.NewTextHandler(
+		os.Stdout,
+		&slog.HandlerOptions{Level: level},
+	)
+	logger := slog.New(handler)
 
 	d, err := OpenDatabase(*dbType, *dbCon)
 	if err != nil {
@@ -53,7 +54,7 @@ 		panic("failed to parse database strings" + err.Error())
 	}
 
 	db, err := gorm.Open(d, &gorm.Config{
-		Logger: ext.Wraplog(logger.WithField("context", "sql")),
+		Logger: ext.Wraplog(logger.With("context", "sql")),
 	})
 	if err != nil {
 		panic("failed to connect database: " + err.Error())
@@ -85,8 +86,8 @@ 	)
 
 	// middleware
 	var (
-		authMiddleware    = ext.NewAuthMiddleware(baseKey, logger.WithField("context", "auth"), userRepository)
-		logMiddleware     = ext.NewLogMiddleare(logger.WithField("context", "http"))
+		authMiddleware    = ext.NewAuthMiddleware(baseKey, logger.With("context", "auth"), userRepository)
+		logMiddleware     = ext.NewLogMiddleare(logger.With("context", "http"))
 		initialMiddleware = ext.NewInitialSetupMiddleware(userRepository)
 	)
 
@@ -129,22 +130,22 @@ 		serverTask = worker.NewServerTask(&http.Server{Handler: r, Addr: "0.0.0.0:8080"})
 		fileTask   = worker.NewTaskFromChanProcessor[string](
 			fileScanner,
 			scheduler,
-			logrus.WithField("context", "file scanner"),
+			logger.With("context", "file scanner"),
 		)
 		exifTask = worker.NewTaskFromBatchProcessor[*repository.Media](
 			exifScanner,
 			scheduler,
-			logrus.WithField("context", "exif scanner"),
+			logger.With("context", "exif scanner"),
 		)
 		thumbnailTask = worker.NewTaskFromBatchProcessor[*repository.Media](
 			thumbnailScanner,
 			scheduler,
-			logrus.WithField("context", "thumbnail scanner"),
+			logger.With("context", "thumbnail scanner"),
 		)
 		albumTask = worker.NewTaskFromSerialProcessor[*repository.Media](
 			albumScanner,
 			scheduler,
-			logrus.WithField("context", "thumbnail scanner"),
+			logger.With("context", "thumbnail scanner"),
 		)
 	)
 
@@ -173,3 +174,18 @@ 	default:
 		return nil, errors.New("No valid db type given")
 	}
 }
+
+func parseLogLevel(input string) slog.Level {
+	switch input {
+	case "debug":
+		return slog.LevelDebug
+	case "info":
+		return slog.LevelInfo
+	case "warn":
+		return slog.LevelWarn
+	case "error":
+		return slog.LevelError
+	default:
+		return slog.LevelWarn
+	}
+}
diff --git a/go.mod b/go.mod
index 6f8eb28073abaad1cb5b5eb126d07cdc6fe79c3d..b8e4bb7bc9e76aa474179b2fc77cc1842c457451 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module git.sr.ht/~gabrielgio/img
 
-go 1.19
+go 1.21
 
 require (
 	github.com/barasher/go-exiftool v1.10.0
@@ -8,14 +8,12 @@ 	github.com/glebarez/sqlite v1.9.0
 	github.com/google/go-cmp v0.5.9
 	github.com/gorilla/mux v1.8.0
 	github.com/h2non/bimg v1.1.9
-	github.com/sirupsen/logrus v1.9.2
 	github.com/spf13/pflag v1.0.5
 	github.com/valyala/quicktemplate v1.7.0
 	golang.org/x/crypto v0.8.0
 	gorm.io/driver/mysql v1.5.1
 	gorm.io/driver/postgres v1.5.2
 	gorm.io/driver/sqlite v1.5.0
-	gorm.io/gen v0.3.22
 	gorm.io/gorm v1.25.2
 )
 
@@ -33,13 +31,8 @@ 	github.com/mattn/go-isatty v0.0.17 // indirect
 	github.com/mattn/go-sqlite3 v1.14.16 // indirect
 	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
-	golang.org/x/mod v0.10.0 // indirect
 	golang.org/x/sys v0.8.0 // indirect
 	golang.org/x/text v0.10.0 // indirect
-	golang.org/x/tools v0.9.3 // indirect
-	gorm.io/datatypes v1.2.0 // indirect
-	gorm.io/hints v1.1.2 // indirect
-	gorm.io/plugin/dbresolver v1.4.1 // indirect
 	modernc.org/libc v1.22.5 // indirect
 	modernc.org/mathutil v1.5.0 // indirect
 	modernc.org/memory v1.5.0 // indirect
diff --git a/go.sum b/go.sum
index 6f654534983a5797bbba47b3fa8e4ce79145933d..03f8b3e75ff3a728a009724d1556164ca2a4caf2 100644
--- a/go.sum
+++ b/go.sum
@@ -11,16 +11,14 @@ github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
 github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
 github.com/glebarez/sqlite v1.9.0 h1:Aj6bPA12ZEx5GbSF6XADmCkYXlljPNUY+Zf1EQxynXs=
 github.com/glebarez/sqlite v1.9.0/go.mod h1:YBYCoyupOao60lzp1MVBLEjZfgkq0tdB1voAQ09K9zw=
-github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
 github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
-github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
-github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
 github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
+github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
@@ -35,7 +33,6 @@ github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
 github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
 github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
 github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
-github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
 github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
@@ -45,20 +42,18 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
 github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
 github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
-github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
-github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y=
-github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus=
@@ -68,15 +63,11 @@ github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
 golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
-golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
-golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -86,34 +77,20 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
 golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
-golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gorm.io/datatypes v1.2.0 h1:5YT+eokWdIxhJgWHdrb2zYUimyk0+TaFth+7a0ybzco=
-gorm.io/datatypes v1.2.0/go.mod h1:o1dh0ZvjIjhH/bngTpypG6lVRJ5chTBxE09FH/71k04=
-gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
 gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
 gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0=
 gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8=
 gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c=
 gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I=
-gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
-gorm.io/gen v0.3.22 h1:K7u5tCyaZfe1cbQFD8N2xrTqUuqximNFSRl7zOFPq+M=
-gorm.io/gen v0.3.22/go.mod h1:dQcELeF/7Kf82M6AQF+O/rKT5r1sjv49TlGz0cerPn4=
-gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
-gorm.io/gorm v1.24.3/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
 gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
-gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
 gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
 gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
 gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
-gorm.io/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o=
-gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg=
-gorm.io/plugin/dbresolver v1.4.1 h1:Ug4LcoPhrvqq71UhxtF346f+skTYoCa/nEsdjvHwEzk=
-gorm.io/plugin/dbresolver v1.4.1/go.mod h1:CTbCtMWhsjXSiJqiW2R8POvJ2cq18RVOl4WGyT5nhNc=
 modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
 modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
 modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
diff --git a/pkg/ext/gorm_logger.go b/pkg/ext/gorm_logger.go
index bfbbb1ebfc55238016b954ad72f80d07d985311b..f0ab59264e7ff3efdea2fe57e613c52c628af9cf 100644
--- a/pkg/ext/gorm_logger.go
+++ b/pkg/ext/gorm_logger.go
@@ -3,15 +3,15 @@
 import (
 	"context"
 	"fmt"
+	"log/slog"
 	"time"
 
-	"github.com/sirupsen/logrus"
 	"gorm.io/gorm/logger"
 	"gorm.io/gorm/utils"
 )
 
 type Log struct {
-	logrus *logrus.Entry
+	logger *slog.Logger
 }
 
 func getFullMsg(msg string, data ...interface{}) string {
@@ -24,35 +24,29 @@ }
 
 func (self *Log) Info(ctx context.Context, msg string, data ...interface{}) {
 	fullMsg := getFullMsg(msg, data)
-	self.logrus.
-		WithContext(ctx).
-		Info(fullMsg)
+	self.logger.InfoContext(ctx, fullMsg)
 }
 
 func (self *Log) Warn(ctx context.Context, msg string, data ...interface{}) {
 	fullMsg := getFullMsg(msg, data)
-	self.logrus.
-		WithContext(ctx).
-		Warn(fullMsg)
+	self.logger.
+		WarnContext(ctx, fullMsg)
 }
 func (self *Log) Error(ctx context.Context, msg string, data ...interface{}) {
 	fullMsg := getFullMsg(msg, data)
-	self.logrus.
-		WithContext(ctx).
-		Error(fullMsg)
+	self.logger.
+		ErrorContext(ctx, fullMsg)
 }
 
 func (self *Log) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), _ error) {
 	elapsed := time.Since(begin)
 	sql, _ := fc()
-	self.logrus.
-		WithContext(ctx).
-		WithField("time", elapsed).
-		Printf(sql)
+	self.logger.
+		InfoContext(ctx, sql, slog.Duration("elapsed", elapsed))
 }
 
-func Wraplog(log *logrus.Entry) *Log {
+func Wraplog(log *slog.Logger) *Log {
 	return &Log{
-		logrus: log,
+		logger: log,
 	}
 }
diff --git a/pkg/ext/middleware.go b/pkg/ext/middleware.go
index 6a94c4f9442ed01ba1a98eb375b3c106aeda81e5..38bacca92dc571629e62545f05d3a89362f9d758 100644
--- a/pkg/ext/middleware.go
+++ b/pkg/ext/middleware.go
@@ -4,10 +4,9 @@ import (
 	"context"
 	"encoding/base64"
 	"errors"
+	"log/slog"
 	"net/http"
 	"time"
-
-	"github.com/sirupsen/logrus"
 
 	"git.sr.ht/~gabrielgio/img/pkg/database/repository"
 	"git.sr.ht/~gabrielgio/img/pkg/service"
@@ -24,7 +23,7 @@ type (
 	User string
 
 	LogMiddleware struct {
-		entry *logrus.Entry
+		logger *slog.Logger
 	}
 )
 
@@ -32,9 +31,9 @@ const (
 	UserKey User = "user"
 )
 
-func NewLogMiddleare(log *logrus.Entry) *LogMiddleware {
+func NewLogMiddleare(log *slog.Logger) *LogMiddleware {
 	return &LogMiddleware{
-		entry: log,
+		logger: log,
 	}
 }
 
@@ -43,27 +42,29 @@ 	return func(w http.ResponseWriter, r *http.Request) {
 		start := time.Now()
 		next(w, r)
 		elapsed := time.Since(start)
-		l.entry.
-			WithField("time", elapsed).
-			WithField("path", r.URL.Path).
-			Info(r.Method)
+		l.logger.Info(
+			r.Method,
+			slog.Duration("elapsed", elapsed),
+			slog.String("path", r.URL.Path),
+		)
+
 	}
 }
 
 type AuthMiddleware struct {
 	key            []byte
-	entry          *logrus.Entry
+	logger         *slog.Logger
 	userRepository repository.UserRepository
 }
 
 func NewAuthMiddleware(
 	key []byte,
-	log *logrus.Entry,
+	logger *slog.Logger,
 	userRepository repository.UserRepository,
 ) *AuthMiddleware {
 	return &AuthMiddleware{
 		key:            key,
-		entry:          log.WithField("context", "auth"),
+		logger:         logger,
 		userRepository: userRepository,
 	}
 }
@@ -79,35 +80,37 @@
 		redirectLogin := "/login?redirect=" + path
 		authBase64, err := r.Cookie("auth")
 		if errors.Is(err, http.ErrNoCookie) {
-			a.entry.Info("No auth provided")
+			a.logger.Info("No auth provided")
 			http.Redirect(w, r, redirectLogin, http.StatusTemporaryRedirect)
 			return
 		}
 
 		auth, err := base64.StdEncoding.DecodeString(authBase64.Value)
 		if err != nil {
-			a.entry.Error(err)
+			a.logger.Error(err.Error())
 			return
 		}
 
 		token, err := service.ReadToken(auth, a.key)
 		if err != nil {
-			a.entry.Error(err)
+			a.logger.Error(err.Error())
 			http.Redirect(w, r, redirectLogin, http.StatusTemporaryRedirect)
 			return
 		}
 
 		user, err := a.userRepository.Get(r.Context(), token.UserID)
 		if err != nil {
-			a.entry.Error(err)
+			a.logger.Error(err.Error())
 			return
 		}
 
 		r = r.WithContext(context.WithValue(r.Context(), UserKey, user))
-		a.entry.
-			WithField("userID", token.UserID).
-			WithField("username", token.Username).
-			Info("user recognized")
+		a.logger.
+			Info(
+				"user recognized",
+				slog.Uint64("userid", uint64(token.UserID)),
+				slog.String("username", token.Username),
+			)
 		next(w, r)
 	}
 }
diff --git a/pkg/worker/list_processor.go b/pkg/worker/list_processor.go
index ea6b45372e607368892aeb2d038c25a09d65e437..c4c37819c2c8542e7d39d30ba753bbc2457b7005 100644
--- a/pkg/worker/list_processor.go
+++ b/pkg/worker/list_processor.go
@@ -3,9 +3,8 @@
 import (
 	"context"
 	"errors"
+	"log/slog"
 	"sync"
-
-	"github.com/sirupsen/logrus"
 )
 
 type (
@@ -27,19 +26,19 @@ 	}
 
 	chanProcessorTask[T any] struct {
 		chanProcessor ChanProcessor[T]
-		logrus        *logrus.Entry
+		logger        *slog.Logger
 		scheduler     *Scheduler
 	}
 
 	batchProcessorTask[T any] struct {
 		batchProcessor ListProcessor[T]
-		logrus         *logrus.Entry
+		logger         *slog.Logger
 		scheduler      *Scheduler
 	}
 
 	serialProcessorTask[T any] struct {
 		batchProcessor ListProcessor[T]
-		logrus         *logrus.Entry
+		logger         *slog.Logger
 		scheduler      *Scheduler
 	}
 )
@@ -47,36 +46,36 @@
 func NewTaskFromBatchProcessor[T any](
 	batchProcessor ListProcessor[T],
 	scheduler *Scheduler,
-	logrus *logrus.Entry,
+	logger *slog.Logger,
 ) Task {
 	return &batchProcessorTask[T]{
 		batchProcessor: batchProcessor,
 		scheduler:      scheduler,
-		logrus:         logrus,
+		logger:         logger,
 	}
 }
 
 func NewTaskFromSerialProcessor[T any](
 	batchProcessor ListProcessor[T],
 	scheduler *Scheduler,
-	logrus *logrus.Entry,
+	logger *slog.Logger,
 ) Task {
 	return &serialProcessorTask[T]{
 		batchProcessor: batchProcessor,
 		scheduler:      scheduler,
-		logrus:         logrus,
+		logger:         logger,
 	}
 }
 
 func NewTaskFromChanProcessor[T any](
 	chanProcessor ChanProcessor[T],
 	scheduler *Scheduler,
-	logrus *logrus.Entry,
+	logger *slog.Logger,
 ) Task {
 	return &chanProcessorTask[T]{
 		chanProcessor: chanProcessor,
 		scheduler:     scheduler,
-		logrus:        logrus,
+		logger:        logger,
 	}
 }
 
@@ -111,7 +110,10 @@ 			go func(v T) {
 				defer l.scheduler.Return()
 				defer wg.Done()
 				if err := l.batchProcessor.Process(ctx, v); err != nil && !errors.Is(err, context.Canceled) {
-					l.logrus.WithError(err).Error("Error processing batch")
+					l.logger.Error(
+						"Error processing batch",
+						slog.String("error", err.Error()),
+					)
 					if failure, ok := l.batchProcessor.(OnFail[T]); ok {
 						failure.OnFail(ctx, v, err)
 					}
@@ -148,7 +150,10 @@ 			}
 
 			l.scheduler.Take()
 			if err := l.batchProcessor.Process(ctx, v); err != nil && !errors.Is(err, context.Canceled) {
-				l.logrus.WithError(err).Error("Error processing batch")
+				l.logger.Error(
+					"Error processing batch",
+					slog.String("error", err.Error()),
+				)
 				if failure, ok := l.batchProcessor.(OnFail[T]); ok {
 					failure.OnFail(ctx, v, err)
 				}
@@ -177,7 +182,13 @@ 			l.scheduler.Take()
 			go func(v T) {
 				defer l.scheduler.Return()
 				if err := l.chanProcessor.Process(ctx, v); err != nil {
-					l.logrus.WithError(err).Error("Error processing chan")
+					l.logger.Error(
+						"Error processing batch",
+						slog.String("error", err.Error()),
+					)
+					if failure, ok := l.chanProcessor.(OnFail[T]); ok {
+						failure.OnFail(ctx, v, err)
+					}
 				}
 			}(v)
 		}
diff --git a/pkg/worker/list_processor_test.go b/pkg/worker/list_processor_test.go
index abdb90792f9713c640180c778f8db69e9abfe388..21489e82685e62ed1e6f85d04f5209dbc59d7feb 100644
--- a/pkg/worker/list_processor_test.go
+++ b/pkg/worker/list_processor_test.go
@@ -5,11 +5,10 @@
 import (
 	"context"
 	"errors"
+	"log/slog"
 	"math/rand"
 	"sync"
 	"testing"
-
-	"github.com/sirupsen/logrus"
 
 	"git.sr.ht/~gabrielgio/img/pkg/testkit"
 )
@@ -27,12 +26,12 @@ )
 
 func TestListProcessorLimit(t *testing.T) {
 	var (
-		log       = logrus.New()
+		log       = slog.Default()
 		scheduler = NewScheduler(1)
 		mock      = &mockCounterListProcessor{countTo: 10000}
 	)
 
-	worker := NewTaskFromBatchProcessor[int](mock, scheduler, log.WithField("context", "testing"))
+	worker := NewTaskFromBatchProcessor[int](mock, scheduler, log.With("context", "testing"))
 
 	err := worker.Start(context.Background())
 	testkit.TestFatalError(t, "Start", err)
@@ -42,12 +41,12 @@ }
 
 func TestListProcessorContextCancelQuery(t *testing.T) {
 	var (
-		log       = logrus.New()
+		log       = slog.Default()
 		scheduler = NewScheduler(1)
 		mock      = &mockContextListProcessor{}
 	)
 
-	worker := NewTaskFromBatchProcessor[int](mock, scheduler, log.WithField("context", "testing"))
+	worker := NewTaskFromBatchProcessor[int](mock, scheduler, log.With("context", "testing"))
 
 	ctx, cancel := context.WithCancel(context.Background())
 	var wg sync.WaitGroup