From ca83ad1f984fe5177e76f1822caed4789a7ed66b Mon Sep 17 00:00:00 2001 From: kolaente Date: Sat, 19 Jul 2025 16:21:35 +0200 Subject: [PATCH] feat: move to slog for logging --- config-raw.json | 5 + go.mod | 3 - go.sum | 43 ---- pkg/config/config.go | 6 +- pkg/db/db.go | 2 +- pkg/db/test.go | 2 +- pkg/events/events.go | 2 +- pkg/files/files.go | 2 +- pkg/files/main_test.go | 5 + pkg/log/echo_logger.go | 186 ++++++++++++++++++ pkg/log/logging.go | 139 +++++++------ pkg/log/mail_logger.go | 66 ++----- pkg/log/noop.go | 6 +- pkg/log/watermill_logger.go | 42 +--- pkg/log/xorm_logger.go | 71 ++----- pkg/mail/mail.go | 2 +- pkg/mail/send_mail.go | 2 +- pkg/migration/migration.go | 2 +- pkg/models/listeners.go | 2 +- pkg/models/main_test.go | 4 + pkg/modules/auth/ldap/main_test.go | 4 + pkg/modules/auth/openid/main_test.go | 4 + pkg/modules/migration/main_test.go | 4 + .../migration/microsoft-todo/main_test.go | 32 +++ pkg/modules/migration/todoist/main_test.go | 32 +++ pkg/modules/migration/todoist/todoist.go | 2 +- pkg/modules/migration/trello/main_test.go | 32 +++ .../migration/vikunja-file/main_test.go | 4 + pkg/notifications/main_test.go | 3 + pkg/routes/routes.go | 33 +++- pkg/user/main_test.go | 5 + pkg/web/readme.md | 2 +- pkg/webtests/integrations.go | 5 + 33 files changed, 490 insertions(+), 264 deletions(-) create mode 100644 pkg/log/echo_logger.go create mode 100644 pkg/modules/migration/microsoft-todo/main_test.go create mode 100644 pkg/modules/migration/todoist/main_test.go create mode 100644 pkg/modules/migration/trello/main_test.go diff --git a/config-raw.json b/config-raw.json index ed451a8ba..bcfc8389e 100644 --- a/config-raw.json +++ b/config-raw.json @@ -395,6 +395,11 @@ "default_value": "INFO", "comment": "Change the log level. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG." }, + { + "key": "format", + "default_value": "text", + "comment": "Logging format. Can be either `text` or `structured` to output JSON." + }, { "key": "database", "default_value": "off", diff --git a/go.mod b/go.mod index a8447c0ab..5013c2a41 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,6 @@ require ( github.com/mattn/go-sqlite3 v1.14.28 github.com/microcosm-cc/bluemonday v1.0.27 github.com/olekukonko/tablewriter v1.0.8 - github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/pquerna/otp v1.5.0 github.com/prometheus/client_golang v1.22.0 github.com/redis/go-redis/v9 v9.11.0 @@ -115,8 +114,6 @@ require ( github.com/goccy/go-yaml v1.18.0 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gorilla/css v1.0.1 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/huandu/go-clone v1.7.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect diff --git a/go.sum b/go.sum index 57e0ddb96..c884bff29 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,6 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -github.com/ThreeDotsLabs/watermill v1.4.6 h1:rWoXlxdBgUyg/bZ3OO0pON+nESVd9r6tnLTgkZ6CYrU= -github.com/ThreeDotsLabs/watermill v1.4.6/go.mod h1:lBnrLbxOjeMRgcJbv+UiZr8Ylz8RkJ4m6i/VN/Nk+to= github.com/ThreeDotsLabs/watermill v1.4.7 h1:LiF4wMP400/psRTdHL/IcV1YIv9htHYFggbe2d6cLeI= github.com/ThreeDotsLabs/watermill v1.4.7/go.mod h1:Ks20MyglVnqjpha1qq0kjaQ+J9ay7bdnjszQ4cW9FMU= github.com/adlio/trello v1.12.0 h1:JqOE2GFHQ9YtEviRRRSnicSxPbt4WFOxhqXzjMOw8lw= @@ -98,12 +96,8 @@ github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBv github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= github.com/ganigeorgiev/fexpr v0.5.0 h1:XA9JxtTE/Xm+g/JFI6RfZEHSiQlk+1glLvRK1Lpv/Tk= github.com/ganigeorgiev/fexpr v0.5.0/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE= -github.com/getsentry/sentry-go v0.34.0 h1:1FCHBVp8TfSc8L10zqSwXUZNiOSF+10qw4czjarTiY4= -github.com/getsentry/sentry-go v0.34.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= github.com/getsentry/sentry-go v0.34.1 h1:HSjc1C/OsnZttohEPrrqKH42Iud0HuLCXpv8cU1pWcw= github.com/getsentry/sentry-go v0.34.1/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= -github.com/getsentry/sentry-go/echo v0.34.0 h1:qjEOup0MJ4qyNKEc/H1XUooIQndka0HOGDMFbLR3x8E= -github.com/getsentry/sentry-go/echo v0.34.0/go.mod h1:kCjZ3/HnI340yMESlyRRYoL/7stfCkuxakhFkrR2nxk= github.com/getsentry/sentry-go/echo v0.34.1 h1:QmRs8A6SK7YYbc6Dtuyh2RTFd4Fe9v9VH8Ty4h8wC8s= github.com/getsentry/sentry-go/echo v0.34.1/go.mod h1:4kdQH/69jXiWE7Ve5nwkWa9U4A38FK/Eu/zSQ4tcaHc= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= @@ -136,8 +130,6 @@ github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1 github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-testfixtures/testfixtures/v3 v3.16.0 h1:bSjH1206tTSXSQZLtUjLvy6sbSrSXO8lGtcB454QP8Q= -github.com/go-testfixtures/testfixtures/v3 v3.16.0/go.mod h1:fH6IT/2lM5yV5cuTkPkuQv3f6JW+AE9tMUovxUhWUhE= github.com/go-testfixtures/testfixtures/v3 v3.17.0 h1:oaWVDAOl13JszPM1jQZ2iS6bIBhy43WY3gpTeQEq/IU= github.com/go-testfixtures/testfixtures/v3 v3.17.0/go.mod h1:HCIVT6p9uKXaCv898IT1iS0My5TF8kF785H4n+6049U= github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk= @@ -147,13 +139,9 @@ github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a/go.mod h1:5YoVOkjYA github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY= -github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= -github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0= github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= @@ -194,11 +182,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= @@ -256,8 +239,6 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jaswdr/faker/v2 v2.5.0 h1:KUYfnleIZMSHNp/q+rDk7XEuqUUL5FhfT19iTTFqF5o= -github.com/jaswdr/faker/v2 v2.5.0/go.mod h1:ROK8xwQV0hYOLDUtxCQgHGcl10jbVzIvqHxcIDdwY2Q= github.com/jaswdr/faker/v2 v2.6.0 h1:19MkwsI6I2vrJhYZqsJpspzRb/fDlax2q1iBhPMxGy0= github.com/jaswdr/faker/v2 v2.6.0/go.mod h1:jZq+qzNQr8/P+5fHd9t3txe2GNPnthrTfohtnJ7B+68= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= @@ -367,8 +348,6 @@ github.com/olekukonko/errors v0.0.0-20250405072817-4e6d85265da6 h1:r3FaAI0NZK3hS github.com/olekukonko/errors v0.0.0-20250405072817-4e6d85265da6/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= github.com/olekukonko/ll v0.0.8 h1:sbGZ1Fx4QxJXEqL/6IG8GEFnYojUSQ45dJVwN2FH2fc= github.com/olekukonko/ll v0.0.8/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g= -github.com/olekukonko/tablewriter v1.0.7 h1:HCC2e3MM+2g72M81ZcJU11uciw6z/p82aEnm4/ySDGw= -github.com/olekukonko/tablewriter v1.0.7/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs= github.com/olekukonko/tablewriter v1.0.8 h1:f6wJzHg4QUtJdvrVPKco4QTrAylgaU0+b9br/lJxEiQ= github.com/olekukonko/tablewriter v1.0.8/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -381,8 +360,6 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= @@ -465,8 +442,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= -github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= github.com/swaggo/swag v1.16.5 h1:nMf2fEV1TetMTJb4XzD0Lz7jFfKJmJKGTygEey8NSxM= github.com/swaggo/swag v1.16.5/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= @@ -529,13 +504,9 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE= -golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY= golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas= golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -567,8 +538,6 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= @@ -582,8 +551,6 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -618,8 +585,6 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= @@ -632,8 +597,6 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= -golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -648,13 +611,9 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -673,8 +632,6 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/config/config.go b/pkg/config/config.go index 7b3856047..f1590b4a5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -137,6 +137,7 @@ const ( LogEnabled Key = `log.enabled` LogStandard Key = `log.standard` LogLevel Key = `log.level` + LogFormat Key = `log.format` LogDatabase Key = `log.database` LogDatabaseLevel Key = `log.databaselevel` LogHTTP Key = `log.http` @@ -400,6 +401,7 @@ func InitDefaultConfig() { LogEnabled.setDefault(true) LogStandard.setDefault("stdout") LogLevel.setDefault("INFO") + LogFormat.setDefault("text") LogDatabase.setDefault("off") LogDatabaseLevel.setDefault("WARNING") LogHTTP.setDefault("stdout") @@ -537,7 +539,7 @@ func InitConfig() { viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) viper.AutomaticEnv() - log.ConfigureLogger(LogEnabled.GetBool(), LogStandard.GetString(), LogPath.GetString(), LogLevel.GetString()) + log.ConfigureStandardLogger(LogEnabled.GetBool(), LogStandard.GetString(), LogPath.GetString(), LogLevel.GetString(), LogFormat.GetString()) // Load the config file viper.AddConfigPath(ServiceRootpath.GetString()) @@ -562,7 +564,7 @@ func InitConfig() { log.Warning(err.Error()) log.Warning("Using default config.") } else { - log.ConfigureLogger(LogEnabled.GetBool(), LogStandard.GetString(), LogPath.GetString(), LogLevel.GetString()) + log.ConfigureStandardLogger(LogEnabled.GetBool(), LogStandard.GetString(), LogPath.GetString(), LogLevel.GetString(), LogFormat.GetString()) } } else { log.Info("No config file found, using default or config from environment variables.") diff --git a/pkg/db/db.go b/pkg/db/db.go index 75717aa83..0aefac4bf 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -87,7 +87,7 @@ func CreateDBEngine() (engine *xorm.Engine, err error) { } engine.SetTZDatabase(loc) engine.SetMapper(names.GonicMapper{}) - logger := log.NewXormLogger(config.LogEnabled.GetBool(), config.LogDatabase.GetString(), config.LogDatabaseLevel.GetString()) + logger := log.NewXormLogger(config.LogEnabled.GetBool(), config.LogDatabase.GetString(), config.LogDatabaseLevel.GetString(), config.LogFormat.GetString()) engine.SetLogger(logger) x = engine diff --git a/pkg/db/test.go b/pkg/db/test.go index f9b4e4096..538189c15 100644 --- a/pkg/db/test.go +++ b/pkg/db/test.go @@ -53,7 +53,7 @@ func CreateTestEngine() (engine *xorm.Engine, err error) { } engine.SetMapper(names.GonicMapper{}) - logger := log.NewXormLogger(config.LogEnabled.GetBool(), config.LogDatabase.GetString(), "DEBUG") + logger := log.NewXormLogger(config.LogEnabled.GetBool(), config.LogDatabase.GetString(), "DEBUG", config.LogFormat.GetString()) logger.ShowSQL(os.Getenv("TESTS_VERBOSE") == "1") engine.SetLogger(logger) engine.SetTZLocation(config.GetTimeZone()) diff --git a/pkg/events/events.go b/pkg/events/events.go index a3af3b20d..a892207da 100644 --- a/pkg/events/events.go +++ b/pkg/events/events.go @@ -51,7 +51,7 @@ func (m *messageHandleFailedError) Error() string { // InitEvents sets up everything needed to work with events func InitEvents() (err error) { - logger := log.NewWatermillLogger(config.LogEnabled.GetBool(), config.LogEvents.GetString(), config.LogEventsLevel.GetString()) + logger := log.NewWatermillLogger(config.LogEnabled.GetBool(), config.LogEvents.GetString(), config.LogEventsLevel.GetString(), config.LogFormat.GetString()) router, err := message.NewRouter( message.RouterConfig{}, diff --git a/pkg/files/files.go b/pkg/files/files.go index 241c423d1..e56d78393 100644 --- a/pkg/files/files.go +++ b/pkg/files/files.go @@ -143,7 +143,7 @@ func (f *File) Delete(s *xorm.Session) (err error) { var perr *os.PathError if errors.As(err, &perr) { // Don't fail when removing the file failed - log.Errorf("Error deleting file %d: %w", f.ID, err) + log.Errorf("Error deleting file %d: %s", f.ID, err) return s.Commit() } diff --git a/pkg/files/main_test.go b/pkg/files/main_test.go index 4f9d30d96..bf19b2ea7 100644 --- a/pkg/files/main_test.go +++ b/pkg/files/main_test.go @@ -19,10 +19,15 @@ package files import ( "os" "testing" + + "code.vikunja.io/api/pkg/log" ) // TestMain is the main test function used to bootstrap the test env func TestMain(m *testing.M) { + // Initialize logger for tests + log.InitLogger() + InitTests() os.Exit(m.Run()) } diff --git a/pkg/log/echo_logger.go b/pkg/log/echo_logger.go new file mode 100644 index 000000000..fe298fe51 --- /dev/null +++ b/pkg/log/echo_logger.go @@ -0,0 +1,186 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package log + +import ( + "encoding/json" + "fmt" + "io" + "log/slog" + "os" + + "github.com/labstack/echo/v4" + "github.com/labstack/gommon/log" +) + +type EchoLogger struct { + logger *slog.Logger + writer io.Writer +} + +// NewEchoLogger creates and initializes a new echo logger +func NewEchoLogger(configLogEnabled bool, configLogEcho string, configLogFormat string) echo.Logger { + handler, writer := makeLogHandler(configLogEnabled, configLogEcho, "DEBUG", configLogFormat) + + echoLogger := &EchoLogger{ + logger: slog.New(handler).With("component", "http"), + writer: writer, + } + + return echoLogger +} + +func (e *EchoLogger) Output() io.Writer { + return e.writer +} + +func (e *EchoLogger) SetOutput(_ io.Writer) { +} + +func (e *EchoLogger) Prefix() string { + return "http" +} + +func (e *EchoLogger) SetPrefix(_ string) { +} + +func (e *EchoLogger) Level() log.Lvl { + return log.DEBUG +} + +func (e *EchoLogger) SetLevel(_ log.Lvl) { +} + +func (e *EchoLogger) SetHeader(_ string) { +} + +func (e *EchoLogger) Print(i ...interface{}) { + e.logger.Info(fmt.Sprint(i...)) +} + +func (e *EchoLogger) Printf(format string, args ...interface{}) { + e.logger.Info(fmt.Sprintf(format, args...)) +} + +func (e *EchoLogger) Printj(j log.JSON) { + if b, err := json.Marshal(j); err == nil { + e.logger.Info(string(b)) + } +} + +func (e *EchoLogger) Debug(i ...interface{}) { + e.logger.Debug(fmt.Sprint(i...)) +} + +func (e *EchoLogger) Debugf(format string, args ...interface{}) { + e.logger.Debug(fmt.Sprintf(format, args...)) +} + +func (e *EchoLogger) Debugj(j log.JSON) { + if b, err := json.Marshal(j); err == nil { + e.logger.Debug(string(b)) + } +} + +func (e *EchoLogger) Info(i ...interface{}) { + e.logger.Info(fmt.Sprint(i...)) +} + +func (e *EchoLogger) Infof(format string, args ...interface{}) { + e.logger.Info(fmt.Sprintf(format, args...)) +} + +func (e *EchoLogger) Infoj(j log.JSON) { + if b, err := json.Marshal(j); err == nil { + e.logger.Info(string(b)) + } +} + +func (e *EchoLogger) Warn(i ...interface{}) { + e.logger.Warn(fmt.Sprint(i...)) +} + +func (e *EchoLogger) Warnf(format string, args ...interface{}) { + e.logger.Warn(fmt.Sprintf(format, args...)) +} + +func (e *EchoLogger) Warnj(j log.JSON) { + if b, err := json.Marshal(j); err == nil { + e.logger.Warn(string(b)) + } +} + +func (e *EchoLogger) Error(i ...interface{}) { + e.logger.Error(fmt.Sprint(i...)) +} + +func (e *EchoLogger) Errorf(format string, args ...interface{}) { + e.logger.Error(fmt.Sprintf(format, args...)) +} + +func (e *EchoLogger) Errorj(j log.JSON) { + if b, err := json.Marshal(j); err == nil { + e.logger.Error(string(b)) + } +} + +func (e *EchoLogger) Fatal(i ...interface{}) { + e.logger.Error(fmt.Sprint(i...)) + os.Exit(1) +} + +func (e *EchoLogger) Fatalj(j log.JSON) { + if b, err := json.Marshal(j); err == nil { + e.logger.Error(string(b)) + } + os.Exit(1) +} + +func (e *EchoLogger) Fatalf(format string, args ...interface{}) { + e.logger.Error(fmt.Sprintf(format, args...)) + os.Exit(1) +} + +func (e *EchoLogger) Panic(i ...interface{}) { + msg := fmt.Sprint(i...) + e.logger.Error(msg) + panic(msg) +} + +func (e *EchoLogger) Panicj(j log.JSON) { + if b, err := json.Marshal(j); err == nil { + msg := string(b) + e.logger.Error(msg) + panic(msg) + } +} + +func (e *EchoLogger) Panicf(format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + e.logger.Error(msg) + panic(msg) +} + +// EnableColor enables color output +func (e *EchoLogger) EnableColor() { + // This is a no-op for our slog implementation +} + +// DisableColor disables color output +func (e *EchoLogger) DisableColor() { + // This is a no-op for our slog implementation +} diff --git a/pkg/log/logging.go b/pkg/log/logging.go index 83449dad6..521ba27d2 100644 --- a/pkg/log/logging.go +++ b/pkg/log/logging.go @@ -17,27 +17,15 @@ package log import ( + "fmt" "io" + "log/slog" "os" "strings" - "time" - - "github.com/op/go-logging" ) -// ErrFmt holds the format for all the console logging -const ErrFmt = `${time_rfc3339}: ${level} ` + "\t" + `▶ ${prefix} ${short_file}:${line}` - -// WebFmt holds the format for all logging related to web requests -const WebFmt = `${time_rfc3339}: WEB ` + "\t" + `▶ ${remote_ip} ${id} ${method} ${status} ${uri} ${latency_human} - ${user_agent}` - -// Fmt is the general log format -const Fmt = `%{color}%{time:` + time.RFC3339 + `}: %{level}` + "\t" + `▶ %{id:03x}%{color:reset} %{message}` - -const logModule = `vikunja` - -// loginstance is the instance of the logger which is used under the hood to log -var logInstance = logging.MustGetLogger(logModule) +// logInstance is the instance of the logger which is used under the hood to log +var logInstance *slog.Logger // logpath is the path in which log files will be written. // This value is a mere fallback for other modules that could but shouldn't be used before calling ConfigureLogger @@ -45,45 +33,74 @@ var logPath = "." // InitLogger initializes the global log handler func InitLogger() { - // This show correct caller functions - logInstance.ExtraCalldepth = 1 - - // Init with stdout and INFO as default format and level - logBackend := logging.NewLogBackend(os.Stdout, "", 0) - backend := logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(Fmt+"\n")) - - backendLeveled := logging.AddModuleLevel(backend) - backendLeveled.SetLevel(logging.INFO, logModule) - - logInstance.SetBackend(backendLeveled) + handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}) + logInstance = slog.New(handler) } -// ConfigureLogger configures the global log handler -func ConfigureLogger(configLogEnabled bool, configLogStandard string, configLogPath string, configLogLevel string) { - lvl := strings.ToUpper(configLogLevel) - level, err := logging.LogLevel(lvl) - if err != nil { - Fatalf("Error setting standard log level %s: %s", lvl, err.Error()) +func makeLogHandler(enabled bool, output string, level string, format string) (slog.Handler, io.Writer) { + var slogLevel slog.Level + switch strings.ToUpper(level) { + case "CRITICAL", "ERROR": + slogLevel = slog.LevelError + case "WARNING": + slogLevel = slog.LevelWarn + case "NOTICE", "INFO": + slogLevel = slog.LevelInfo + case "DEBUG": + slogLevel = slog.LevelDebug + default: + slogLevel = slog.LevelInfo } - logPath = configLogPath - - // The backend is the part which actually handles logging the log entries somewhere. - var backend logging.Backend - backend = &NoopBackend{} - if configLogEnabled && configLogStandard != "off" { - logBackend := logging.NewLogBackend(GetLogWriter(configLogStandard, "standard"), "", 0) - backend = logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(Fmt+"\n")) + format = strings.ToLower(format) + if format == "" { + format = "text" + } + if format != "text" && format != "structured" { + Fatalf("invalid log format %s", format) } - backendLeveled := logging.AddModuleLevel(backend) - backendLeveled.SetLevel(level, logModule) + writer := io.Discard + if enabled && output != "off" { + writer = getLogWriter(output, "standard") + } - logInstance.SetBackend(backendLeveled) + return createHandler(writer, slogLevel, format), writer +} + +// createHandler creates a consistent slog handler for all loggers +func createHandler(writer io.Writer, level slog.Level, format string) slog.Handler { + handlerOpts := &slog.HandlerOptions{Level: level} + if strings.ToLower(format) == "structured" { + return slog.NewJSONHandler(writer, handlerOpts) + } + + return slog.NewTextHandler(writer, handlerOpts) +} + +// NewHTTPLogger creates and initializes a new HTTP logger +func NewHTTPLogger(enabled bool, output string, format string) *slog.Logger { + handler, _ := makeLogHandler(enabled, output, "DEBUG", format) + + return slog.New(handler).With("component", "http") +} + +// ConfigureStandardLogger configures the global log handler +func ConfigureStandardLogger(enabled bool, output string, path string, level string, format string) { + handler, _ := makeLogHandler(enabled, output, level, format) + logInstance = slog.New(handler) + logPath = path +} + +// wrapLogger is used for libraries requiring a Debugf method. +type wrapLogger struct{} + +func (wrapLogger) Debugf(format string, args ...interface{}) { + logInstance.Debug(fmt.Sprintf(format, args...)) } // GetLogWriter returns the writer to where the normal log goes, depending on the config -func GetLogWriter(logfmt string, logfile string) (writer io.Writer) { +func getLogWriter(logfmt string, logfile string) (writer io.Writer) { writer = os.Stdout // Set the default case to prevent nil pointer panics switch logfmt { case "file": @@ -106,68 +123,72 @@ func GetLogWriter(logfmt string, logfile string) (writer io.Writer) { } // GetLogger returns the logging instance. DO NOT USE THIS TO LOG STUFF. -func GetLogger() *logging.Logger { - return logInstance +// GetLogger returns a logger which can be used by external libraries expecting a Debugf method. +// It only implements Debugf and forwards to the global logger. +func GetLogger() interface{ Debugf(string, ...interface{}) } { + return wrapLogger{} } // The following functions are to be used as an "eye-candy", so one can just write log.Error() instead of log.Log.Error() // Debug is for debug messages func Debug(args ...interface{}) { - logInstance.Debug(args...) + logInstance.Debug(fmt.Sprint(args...)) } // Debugf is for debug messages func Debugf(format string, args ...interface{}) { - logInstance.Debugf(format, args...) + logInstance.Debug(fmt.Sprintf(format, args...)) } // Info is for info messages func Info(args ...interface{}) { - logInstance.Info(args...) + logInstance.Info(fmt.Sprint(args...)) } // Infof is for info messages func Infof(format string, args ...interface{}) { - logInstance.Infof(format, args...) + logInstance.Info(fmt.Sprintf(format, args...)) } // Error is for error messages func Error(args ...interface{}) { - logInstance.Error(args...) + logInstance.Error(fmt.Sprint(args...)) } // Errorf is for error messages func Errorf(format string, args ...interface{}) { - logInstance.Errorf(format, args...) + logInstance.Error(fmt.Sprintf(format, args...)) } // Warning is for warning messages func Warning(args ...interface{}) { - logInstance.Warning(args...) + logInstance.Warn(fmt.Sprint(args...)) } // Warningf is for warning messages func Warningf(format string, args ...interface{}) { - logInstance.Warningf(format, args...) + logInstance.Warn(fmt.Sprintf(format, args...)) } // Critical is for critical messages func Critical(args ...interface{}) { - logInstance.Critical(args...) + logInstance.Error(fmt.Sprint(args...)) } // Criticalf is for critical messages func Criticalf(format string, args ...interface{}) { - logInstance.Criticalf(format, args...) + logInstance.Error(fmt.Sprintf(format, args...)) } // Fatal is for fatal messages func Fatal(args ...interface{}) { - logInstance.Fatal(args...) + logInstance.Error(fmt.Sprint(args...)) + os.Exit(1) } // Fatalf is for fatal messages func Fatalf(format string, args ...interface{}) { - logInstance.Fatalf(format, args...) + logInstance.Error(fmt.Sprintf(format, args...)) + os.Exit(1) } diff --git a/pkg/log/mail_logger.go b/pkg/log/mail_logger.go index 854874fa6..2abd7c4cb 100644 --- a/pkg/log/mail_logger.go +++ b/pkg/log/mail_logger.go @@ -17,75 +17,39 @@ package log import ( - "strings" - "time" + "fmt" + "log/slog" - "github.com/op/go-logging" - "github.com/wneessen/go-mail/log" + maillog "github.com/wneessen/go-mail/log" ) type MailLogger struct { - logger *logging.Logger - level log.Level + logger *slog.Logger } -const mailFormat = `%{color}%{time:` + time.RFC3339Nano + `}: %{level}` + "\t" + `▶ [MAIL] %{id:03x}%{color:reset} %{message}` -const mailLogModule = `vikunja_mail` - // NewMailLogger creates and initializes a new mail logger -func NewMailLogger(configLogEnabled bool, configLogMail string, configLogMailLevel string) log.Logger { - lvl := strings.ToUpper(configLogMailLevel) - level, err := logging.LogLevel(lvl) - if err != nil { - Criticalf("Error setting mail log level %s: %s", lvl, err.Error()) - } +func NewMailLogger(configLogEnabled bool, configLogMail string, configLogMailLevel string, configLogFormat string) maillog.Logger { + handler, _ := makeLogHandler(configLogEnabled, configLogMail, configLogMailLevel, configLogFormat) mailLogger := &MailLogger{ - logger: logging.MustGetLogger(mailLogModule), - } - - var backend logging.Backend - backend = &NoopBackend{} - if configLogEnabled && configLogMail != "off" { - logBackend := logging.NewLogBackend(GetLogWriter(configLogMail, "mail"), "", 0) - backend = logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(mailFormat+"\n")) - } - - backendLeveled := logging.AddModuleLevel(backend) - backendLeveled.SetLevel(level, mailLogModule) - - mailLogger.logger.SetBackend(backendLeveled) - - switch level { - case logging.CRITICAL: - case logging.ERROR: - mailLogger.level = log.LevelError - case logging.WARNING: - mailLogger.level = log.LevelWarn - case logging.NOTICE: - case logging.INFO: - mailLogger.level = log.LevelInfo - case logging.DEBUG: - mailLogger.level = log.LevelDebug - default: - mailLogger.level = 0 + logger: slog.New(handler).With("component", "mail"), } return mailLogger } -func (m *MailLogger) Debugf(l log.Log) { - m.logger.Debugf(l.Format, l.Messages...) +func (m *MailLogger) Debugf(l maillog.Log) { + m.logger.Debug(fmt.Sprintf(l.Format, l.Messages...)) } -func (m *MailLogger) Infof(l log.Log) { - m.logger.Infof(l.Format, l.Messages...) +func (m *MailLogger) Infof(l maillog.Log) { + m.logger.Info(fmt.Sprintf(l.Format, l.Messages...)) } -func (m *MailLogger) Warnf(l log.Log) { - m.logger.Warningf(l.Format, l.Messages...) +func (m *MailLogger) Warnf(l maillog.Log) { + m.logger.Warn(fmt.Sprintf(l.Format, l.Messages...)) } -func (m *MailLogger) Errorf(l log.Log) { - m.logger.Errorf(l.Format, l.Messages...) +func (m *MailLogger) Errorf(l maillog.Log) { + m.logger.Error(fmt.Sprintf(l.Format, l.Messages...)) } diff --git a/pkg/log/noop.go b/pkg/log/noop.go index 615405171..d12f12839 100644 --- a/pkg/log/noop.go +++ b/pkg/log/noop.go @@ -16,13 +16,11 @@ package log -import ( - "github.com/op/go-logging" -) +import "log/slog" // NoopBackend doesn't log anything. Used in cases where we want to disable logging completely. type NoopBackend struct{} -func (n *NoopBackend) Log(_ logging.Level, _ int, _ *logging.Record) error { +func (n *NoopBackend) Log(_ slog.Level, _ int, _ *slog.Record) error { return nil } diff --git a/pkg/log/watermill_logger.go b/pkg/log/watermill_logger.go index 9aa7f5b47..4998023ab 100644 --- a/pkg/log/watermill_logger.go +++ b/pkg/log/watermill_logger.go @@ -18,45 +18,23 @@ package log import ( "fmt" - "strings" - "time" + "log/slog" "github.com/ThreeDotsLabs/watermill" - "github.com/op/go-logging" ) -const watermillFmt = `%{color}%{time:` + time.RFC3339Nano + `}: %{level}` + "\t" + `▶ [EVENTS] %{id:03x}%{color:reset} %{message}` - -const watermillLogModule = `vikunja_events` - type WatermillLogger struct { - logger *logging.Logger + logger *slog.Logger } -// NewXormLogger creates and initializes a new watermill logger -func NewWatermillLogger(configLogEnabled bool, configLogEvents string, configLogEventsLevel string) *WatermillLogger { - lvl := strings.ToUpper(configLogEventsLevel) - level, err := logging.LogLevel(lvl) - if err != nil { - Criticalf("Error setting events log level %s: %s", lvl, err.Error()) - } +// NewWatermillLogger creates and initializes a new watermill logger +func NewWatermillLogger(configLogEnabled bool, configLogEvents string, configLogEventsLevel string, configLogFormat string) *WatermillLogger { + handler, _ := makeLogHandler(configLogEnabled, configLogEvents, configLogEventsLevel, configLogFormat) watermillLogger := &WatermillLogger{ - logger: logging.MustGetLogger(watermillLogModule), + logger: slog.New(handler).With("component", "events"), } - var backend logging.Backend - backend = &NoopBackend{} - if configLogEnabled && configLogEvents != "off" { - logBackend := logging.NewLogBackend(GetLogWriter(configLogEvents, "events"), "", 0) - backend = logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(watermillFmt+"\n")) - } - - backendLeveled := logging.AddModuleLevel(backend) - backendLeveled.SetLevel(level, watermillLogModule) - - watermillLogger.logger.SetBackend(backendLeveled) - return watermillLogger } @@ -75,19 +53,19 @@ func concatFields(fields watermill.LogFields) string { } func (w *WatermillLogger) Error(msg string, err error, fields watermill.LogFields) { - w.logger.Errorf("%s: %s, %s", msg, err, concatFields(fields)) + w.logger.Error(fmt.Sprintf("%s: %s, %s", msg, err, concatFields(fields))) } func (w *WatermillLogger) Info(msg string, fields watermill.LogFields) { - w.logger.Infof("%s, %s", msg, concatFields(fields)) + w.logger.Info(fmt.Sprintf("%s, %s", msg, concatFields(fields))) } func (w *WatermillLogger) Debug(msg string, fields watermill.LogFields) { - w.logger.Debugf("%s, %s", msg, concatFields(fields)) + w.logger.Debug(fmt.Sprintf("%s, %s", msg, concatFields(fields))) } func (w *WatermillLogger) Trace(msg string, fields watermill.LogFields) { - w.logger.Debugf("%s, %s", msg, concatFields(fields)) + w.logger.Debug(fmt.Sprintf("%s, %s", msg, concatFields(fields))) } func (w *WatermillLogger) With(_ watermill.LogFields) watermill.LoggerAdapter { diff --git a/pkg/log/xorm_logger.go b/pkg/log/xorm_logger.go index fc772d5a8..e507cee36 100644 --- a/pkg/log/xorm_logger.go +++ b/pkg/log/xorm_logger.go @@ -17,62 +17,24 @@ package log import ( - "strings" - "time" + "fmt" + "log/slog" - "github.com/op/go-logging" "xorm.io/xorm/log" ) -// XormFmt defines the format for xorm logging strings -const XormFmt = `%{color}%{time:` + time.RFC3339Nano + `}: %{level}` + "\t" + `▶ [DATABASE] %{id:03x}%{color:reset} %{message}` - -const xormLogModule = `vikunja_database` - // XormLogger holds an implementation of the xorm logger interface. type XormLogger struct { - logger *logging.Logger - level log.LogLevel + logger *slog.Logger showSQL bool } // NewXormLogger creates and initializes a new xorm logger -func NewXormLogger(configLogEnabled bool, configLogDatabase string, configLogDatabaseLevel string) *XormLogger { - lvl := strings.ToUpper(configLogDatabaseLevel) - level, err := logging.LogLevel(lvl) - if err != nil { - Criticalf("Error setting database log level %s: %s", lvl, err.Error()) - } +func NewXormLogger(configLogEnabled bool, configLogDatabase string, configLogDatabaseLevel string, configLogFormat string) *XormLogger { + handler, _ := makeLogHandler(configLogEnabled, configLogDatabase, configLogDatabaseLevel, configLogFormat) xormLogger := &XormLogger{ - logger: logging.MustGetLogger(xormLogModule), - } - - var backend logging.Backend - backend = &NoopBackend{} - if configLogEnabled && configLogDatabase != "off" { - logBackend := logging.NewLogBackend(GetLogWriter(configLogDatabase, "database"), "", 0) - backend = logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(XormFmt+"\n")) - } - - backendLeveled := logging.AddModuleLevel(backend) - backendLeveled.SetLevel(level, xormLogModule) - - xormLogger.logger.SetBackend(backendLeveled) - - switch level { - case logging.CRITICAL: - case logging.ERROR: - xormLogger.level = log.LOG_ERR - case logging.WARNING: - xormLogger.level = log.LOG_WARNING - case logging.NOTICE: - case logging.INFO: - xormLogger.level = log.LOG_INFO - case logging.DEBUG: - xormLogger.level = log.LOG_DEBUG - default: - xormLogger.level = log.LOG_OFF + logger: slog.New(handler).With("component", "database"), } xormLogger.showSQL = true @@ -82,52 +44,51 @@ func NewXormLogger(configLogEnabled bool, configLogDatabase string, configLogDat // Debug logs a debug string func (x *XormLogger) Debug(v ...interface{}) { - x.logger.Debug(v...) + x.logger.Debug(fmt.Sprint(v...)) } // Debugf logs a debug string func (x *XormLogger) Debugf(format string, v ...interface{}) { - x.logger.Debugf(format, v...) + x.logger.Debug(fmt.Sprintf(format, v...)) } // Error logs a debug string func (x *XormLogger) Error(v ...interface{}) { - x.logger.Error(v...) + x.logger.Error(fmt.Sprint(v...)) } // Errorf logs a debug string func (x *XormLogger) Errorf(format string, v ...interface{}) { - x.logger.Errorf(format, v...) + x.logger.Error(fmt.Sprintf(format, v...)) } // Info logs an info string func (x *XormLogger) Info(v ...interface{}) { - x.logger.Info(v...) + x.logger.Info(fmt.Sprint(v...)) } // Infof logs an info string func (x *XormLogger) Infof(format string, v ...interface{}) { - x.logger.Infof(format, v...) + x.logger.Info(fmt.Sprintf(format, v...)) } // Warn logs a warning string func (x *XormLogger) Warn(v ...interface{}) { - x.logger.Warning(v...) + x.logger.Warn(fmt.Sprint(v...)) } // Warnf logs a warning string func (x *XormLogger) Warnf(format string, v ...interface{}) { - x.logger.Warningf(format, v...) + x.logger.Warn(fmt.Sprintf(format, v...)) } // Level returns the current set log level func (x *XormLogger) Level() log.LogLevel { - return x.level + return log.LOG_DEBUG } // SetLevel sets the log level -func (x *XormLogger) SetLevel(l log.LogLevel) { - x.level = l +func (x *XormLogger) SetLevel(_ log.LogLevel) { } // ShowSQL sets whether to show the log level or not diff --git a/pkg/mail/mail.go b/pkg/mail/mail.go index 9edcaba8c..21715a6e6 100644 --- a/pkg/mail/mail.go +++ b/pkg/mail/mail.go @@ -56,7 +56,7 @@ func getClient() (*mail.Client, error) { }), mail.WithPort(config.MailerPort.GetInt()), mail.WithTimeout((config.MailerQueueTimeout.GetDuration() + 3) * time.Second), // 3s more for us to close before mail server timeout - mail.WithLogger(log.NewMailLogger(config.LogEnabled.GetBool(), config.LogMail.GetString(), config.LogMailLevel.GetString())), + mail.WithLogger(log.NewMailLogger(config.LogEnabled.GetBool(), config.LogMail.GetString(), config.LogMailLevel.GetString(), config.LogFormat.GetString())), mail.WithDebugLog(), } diff --git a/pkg/mail/send_mail.go b/pkg/mail/send_mail.go index c31787c65..1ed697bea 100644 --- a/pkg/mail/send_mail.go +++ b/pkg/mail/send_mail.go @@ -98,7 +98,7 @@ func getMessage(opts *Opts) *mail.Msg { for name, fs := range opts.EmbedFS { err := m.EmbedFromEmbedFS(name, fs) if err != nil { - log.Errorf("Error embedding %s via embed.FS into mail: %v", err) + log.Errorf("Error embedding %s via embed.FS into mail: %v", name, err) } } diff --git a/pkg/migration/migration.go b/pkg/migration/migration.go index e0a9ce530..8da4303c6 100644 --- a/pkg/migration/migration.go +++ b/pkg/migration/migration.go @@ -58,7 +58,7 @@ func initMigration(x *xorm.Engine) *xormigrate.Xormigrate { }) m := xormigrate.New(x, migrations) - logger := log.NewXormLogger(config.LogEnabled.GetBool(), config.LogEvents.GetString(), config.LogEventsLevel.GetString()) + logger := log.NewXormLogger(config.LogEnabled.GetBool(), config.LogEvents.GetString(), config.LogEventsLevel.GetString(), config.LogFormat.GetString()) m.SetLogger(logger) m.InitSchema(initSchema) return m diff --git a/pkg/models/listeners.go b/pkg/models/listeners.go index 84ab9b6df..d1120894b 100644 --- a/pkg/models/listeners.go +++ b/pkg/models/listeners.go @@ -704,7 +704,7 @@ func (l *UpdateTaskInSavedFilterViews) Handle(msg *message.Message) (err error) IsErrInvalidTaskFilterConcatinator(err) || IsErrInvalidTaskFilterComparator(err) || IsErrInvalidTaskField(err) { - log.Debugf("Invalid filter expression for view %d, expression: %s", view.ID, view.Filter) + log.Debugf("Invalid filter expression for view %d, expression: %v", view.ID, view.Filter) continue } diff --git a/pkg/models/main_test.go b/pkg/models/main_test.go index 9ebdce533..6c755b13a 100644 --- a/pkg/models/main_test.go +++ b/pkg/models/main_test.go @@ -26,6 +26,7 @@ import ( "code.vikunja.io/api/pkg/events" "code.vikunja.io/api/pkg/files" "code.vikunja.io/api/pkg/i18n" + "code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/user" ) @@ -54,6 +55,9 @@ func TestMain(m *testing.M) { setupTime() + // Initialize logger for tests + log.InitLogger() + // Set default config config.InitDefaultConfig() // We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly diff --git a/pkg/modules/auth/ldap/main_test.go b/pkg/modules/auth/ldap/main_test.go index 78dd5ba89..27009ae90 100644 --- a/pkg/modules/auth/ldap/main_test.go +++ b/pkg/modules/auth/ldap/main_test.go @@ -22,12 +22,16 @@ import ( "code.vikunja.io/api/pkg/events" "code.vikunja.io/api/pkg/files" + "code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/user" ) // TestMain is the main test function used to bootstrap the test env func TestMain(m *testing.M) { + // Initialize logger for tests + log.InitLogger() + user.InitTests() files.InitTests() models.SetupTests() diff --git a/pkg/modules/auth/openid/main_test.go b/pkg/modules/auth/openid/main_test.go index 6afe1f618..e8b911e79 100644 --- a/pkg/modules/auth/openid/main_test.go +++ b/pkg/modules/auth/openid/main_test.go @@ -22,12 +22,16 @@ import ( "code.vikunja.io/api/pkg/events" "code.vikunja.io/api/pkg/files" + "code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/user" ) // TestMain is the main test function used to bootstrap the test env func TestMain(m *testing.M) { + // Initialize logger for tests + log.InitLogger() + user.InitTests() files.InitTests() models.SetupTests() diff --git a/pkg/modules/migration/main_test.go b/pkg/modules/migration/main_test.go index 5569a5432..b68acc07a 100644 --- a/pkg/modules/migration/main_test.go +++ b/pkg/modules/migration/main_test.go @@ -24,12 +24,16 @@ import ( "code.vikunja.io/api/pkg/config" "code.vikunja.io/api/pkg/files" + "code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/user" ) // TestMain is the main test function used to bootstrap the test env func TestMain(m *testing.M) { + // Initialize logger for tests + log.InitLogger() + // Set default config config.InitDefaultConfig() // We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly diff --git a/pkg/modules/migration/microsoft-todo/main_test.go b/pkg/modules/migration/microsoft-todo/main_test.go new file mode 100644 index 000000000..1c2424140 --- /dev/null +++ b/pkg/modules/migration/microsoft-todo/main_test.go @@ -0,0 +1,32 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package microsofttodo + +import ( + "os" + "testing" + + "code.vikunja.io/api/pkg/log" +) + +// TestMain is the main test function used to bootstrap the test env +func TestMain(m *testing.M) { + // Initialize logger for tests + log.InitLogger() + + os.Exit(m.Run()) +} diff --git a/pkg/modules/migration/todoist/main_test.go b/pkg/modules/migration/todoist/main_test.go new file mode 100644 index 000000000..f7d79bdee --- /dev/null +++ b/pkg/modules/migration/todoist/main_test.go @@ -0,0 +1,32 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package todoist + +import ( + "os" + "testing" + + "code.vikunja.io/api/pkg/log" +) + +// TestMain is the main test function used to bootstrap the test env +func TestMain(m *testing.M) { + // Initialize logger for tests + log.InitLogger() + + os.Exit(m.Run()) +} diff --git a/pkg/modules/migration/todoist/todoist.go b/pkg/modules/migration/todoist/todoist.go index 99565038b..63564446d 100644 --- a/pkg/modules/migration/todoist/todoist.go +++ b/pkg/modules/migration/todoist/todoist.go @@ -610,7 +610,7 @@ func (m *Migration) Migrate(u *user.User) (err error) { // we can't show them individually and the api returns a 404. buf := bytes.Buffer{} _, _ = buf.ReadFrom(resp.Body) - log.Debugf("[Todoist Migration] Could not retrieve task details for task %d: %s", i.TaskID, buf.String()) + log.Debugf("[Todoist Migration] Could not retrieve task details for task %s: %s", i.TaskID, buf.String()) continue } diff --git a/pkg/modules/migration/trello/main_test.go b/pkg/modules/migration/trello/main_test.go new file mode 100644 index 000000000..d665cd3ba --- /dev/null +++ b/pkg/modules/migration/trello/main_test.go @@ -0,0 +1,32 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package trello + +import ( + "os" + "testing" + + "code.vikunja.io/api/pkg/log" +) + +// TestMain is the main test function used to bootstrap the test env +func TestMain(m *testing.M) { + // Initialize logger for tests + log.InitLogger() + + os.Exit(m.Run()) +} diff --git a/pkg/modules/migration/vikunja-file/main_test.go b/pkg/modules/migration/vikunja-file/main_test.go index 7bc489064..e3bbfba9a 100644 --- a/pkg/modules/migration/vikunja-file/main_test.go +++ b/pkg/modules/migration/vikunja-file/main_test.go @@ -24,12 +24,16 @@ import ( "code.vikunja.io/api/pkg/config" "code.vikunja.io/api/pkg/files" + "code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/user" ) // TestMain is the main test function used to bootstrap the test env func TestMain(m *testing.M) { + // Initialize logger for tests + log.InitLogger() + // Set default config config.InitDefaultConfig() // We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly diff --git a/pkg/notifications/main_test.go b/pkg/notifications/main_test.go index 2574b6d27..85fb79b2f 100644 --- a/pkg/notifications/main_test.go +++ b/pkg/notifications/main_test.go @@ -45,6 +45,9 @@ func SetupTests() { // TestMain is the main test function used to bootstrap the test env func TestMain(m *testing.M) { + // Initialize logger for tests + log.InitLogger() + // Set default config config.InitDefaultConfig() // We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly diff --git a/pkg/routes/routes.go b/pkg/routes/routes.go index da802bad9..fce0ce239 100644 --- a/pkg/routes/routes.go +++ b/pkg/routes/routes.go @@ -53,6 +53,7 @@ package routes import ( "errors" + "log/slog" "net/url" "strings" "time" @@ -85,6 +86,30 @@ import ( "github.com/ulule/limiter/v3" ) +// slogHTTPMiddleware creates a custom HTTP logging middleware using slog +func slogHTTPMiddleware(logger *slog.Logger) echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return echo.HandlerFunc(func(c echo.Context) error { + start := time.Now() + + err := next(c) + + req := c.Request() + res := c.Response() + + logger.InfoContext(c.Request().Context(), + req.Method+" "+req.RequestURI, + "status", res.Status, + "remote_ip", c.RealIP(), + "latency", time.Since(start), + "user_agent", req.UserAgent(), + ) + + return err + }) + } +} + // NewEcho registers a new Echo instance func NewEcho() *echo.Echo { e := echo.New() @@ -101,11 +126,9 @@ func NewEcho() *echo.Echo { } // Logger - if !config.LogEnabled.GetBool() || config.LogHTTP.GetString() != "off" { - e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ - Format: log.WebFmt + "\n", - Output: log.GetLogWriter(config.LogHTTP.GetString(), "http"), - })) + if config.LogEnabled.GetBool() && config.LogHTTP.GetString() != "off" { + httpLogger := log.NewHTTPLogger(config.LogEnabled.GetBool(), config.LogHTTP.GetString(), config.LogFormat.GetString()) + e.Use(slogHTTPMiddleware(httpLogger)) } // panic recover diff --git a/pkg/user/main_test.go b/pkg/user/main_test.go index 1895393b2..a9be7e74e 100644 --- a/pkg/user/main_test.go +++ b/pkg/user/main_test.go @@ -19,10 +19,15 @@ package user import ( "os" "testing" + + "code.vikunja.io/api/pkg/log" ) // TestMain is the main test function used to bootstrap the test env func TestMain(m *testing.M) { + // Initialize logger for tests + log.InitLogger() + InitTests() os.Exit(m.Run()) } diff --git a/pkg/web/readme.md b/pkg/web/readme.md index 29049b985..bb175e23c 100644 --- a/pkg/web/readme.md +++ b/pkg/web/readme.md @@ -130,7 +130,7 @@ To define the thing which gets the appropriate auth object, you need to call a m #### Logging -You can provide your own instance of `logger.Logger` (using [go-logging](https://github.com/op/go-logging)) to the handler. +You can provide your own instance of `slog.Logger` from Go's standard library to the handler. It will use this instance to log errors which are not better specified or things like users trying to do something they're not allowed to do and so on. diff --git a/pkg/webtests/integrations.go b/pkg/webtests/integrations.go index 434dc6373..ff4d999a5 100644 --- a/pkg/webtests/integrations.go +++ b/pkg/webtests/integrations.go @@ -29,6 +29,7 @@ import ( "code.vikunja.io/api/pkg/db" "code.vikunja.io/api/pkg/events" "code.vikunja.io/api/pkg/files" + "code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/modules/auth" "code.vikunja.io/api/pkg/modules/keyvalue" @@ -64,6 +65,10 @@ func setupTestEnv() (e *echo.Echo, err error) { config.InitDefaultConfig() // We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH")) + + // Initialize logger for tests + log.InitLogger() + // Some tests use the file engine, so we'll need to initialize that files.InitTests() user.InitTests()