1
0
mirror of https://github.com/amir20/dozzle.git synced 2025-12-21 21:33:18 +01:00

chore: uses zerolog instead of logrus (#3203)

This commit is contained in:
Amir Raminfar
2024-08-14 10:04:18 -07:00
committed by GitHub
parent 37f0249065
commit 077eeb7917
38 changed files with 278 additions and 265 deletions

View File

@@ -54,7 +54,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: 1.22.x go-version: 1.23.x
check-latest: true check-latest: true
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4

4
go.mod
View File

@@ -15,7 +15,6 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect github.com/sergi/go-diff v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.3
github.com/spf13/afero v1.11.0 github.com/spf13/afero v1.11.0
github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
@@ -30,6 +29,7 @@ require (
github.com/go-chi/jwtauth/v5 v5.3.1 github.com/go-chi/jwtauth/v5 v5.3.1
github.com/goccy/go-json v0.10.3 github.com/goccy/go-json v0.10.3
github.com/puzpuzpuz/xsync/v3 v3.4.0 github.com/puzpuzpuz/xsync/v3 v3.4.0
github.com/rs/zerolog v1.33.0
github.com/samber/lo v1.47.0 github.com/samber/lo v1.47.0
github.com/wk8/go-ordered-map/v2 v2.1.8 github.com/wk8/go-ordered-map/v2 v2.1.8
github.com/yuin/goldmark v1.7.4 github.com/yuin/goldmark v1.7.4
@@ -59,6 +59,8 @@ require (
github.com/lestrrat-go/jwx/v2 v2.1.1 // indirect github.com/lestrrat-go/jwx/v2 v2.1.1 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect github.com/lestrrat-go/option v1.0.1 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect

19
go.sum
View File

@@ -20,6 +20,7 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -28,8 +29,6 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnN
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v27.1.2+incompatible h1:AhGzR1xaQIy53qCkxARaFluI00WPGtXn0AJuoQsVYTY= github.com/docker/docker v27.1.2+incompatible h1:AhGzR1xaQIy53qCkxARaFluI00WPGtXn0AJuoQsVYTY=
github.com/docker/docker v27.1.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v27.1.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
@@ -51,6 +50,7 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -85,6 +85,11 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
@@ -102,8 +107,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4= github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4=
github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/samber/lo v1.46.0 h1:w8G+oaCPgz1PoCJztqymCFaKwXt+5cCXn51uPxExFfQ= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/samber/lo v1.46.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
@@ -121,7 +127,6 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@@ -183,10 +188,12 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

View File

@@ -13,7 +13,7 @@ import (
"github.com/amir20/dozzle/internal/agent/pb" "github.com/amir20/dozzle/internal/agent/pb"
"github.com/amir20/dozzle/internal/docker" "github.com/amir20/dozzle/internal/docker"
"github.com/amir20/dozzle/internal/utils" "github.com/amir20/dozzle/internal/utils"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
orderedmap "github.com/wk8/go-ordered-map/v2" orderedmap "github.com/wk8/go-ordered-map/v2"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
@@ -32,7 +32,7 @@ func NewClient(endpoint string, certificates tls.Certificate, opts ...grpc.DialO
caCertPool := x509.NewCertPool() caCertPool := x509.NewCertPool()
c, err := x509.ParseCertificate(certificates.Certificate[0]) c, err := x509.ParseCertificate(certificates.Certificate[0])
if err != nil { if err != nil {
log.Fatalf("failed to parse certificate: %v", err) return nil, fmt.Errorf("failed to parse certificate: %w", err)
} }
caCertPool.AddCert(c) caCertPool.AddCert(c)
tlsConfig := &tls.Config{ tlsConfig := &tls.Config{
@@ -126,7 +126,8 @@ func sendLogs(stream pb.AgentService_StreamLogsClient, events chan<- *docker.Log
m, err := resp.Event.Message.UnmarshalNew() m, err := resp.Event.Message.UnmarshalNew()
if err != nil { if err != nil {
log.Fatalf("cannot unpack message %v", err) log.Error().Err(err).Msg("agent client: failed to unmarshal message")
continue
} }
var message any var message any
@@ -136,8 +137,10 @@ func sendLogs(stream pb.AgentService_StreamLogsClient, events chan<- *docker.Log
case *pb.ComplexMessage: case *pb.ComplexMessage:
message = jsonBytesToOrderedMap(m.Data) message = jsonBytesToOrderedMap(m.Data)
default: default:
log.Fatalf("agent client: unknown type %T", m) log.Error().Type("message", m).Msg("agent client: unknown message type")
continue
} }
events <- &docker.LogEvent{ events <- &docker.LogEvent{
@@ -175,7 +178,7 @@ func (c *Client) StreamRawBytes(ctx context.Context, containerID string, since t
if err == io.EOF || err == context.Canceled { if err == io.EOF || err == context.Canceled {
return return
} else { } else {
log.Warnf("error while streaming raw bytes %v", err) log.Error().Err(err).Msg("agent client: failed to receive raw bytes")
return return
} }
} }

View File

@@ -127,7 +127,8 @@ func init() {
Stats: utils.NewRingBuffer[docker.ContainerStat](300), Stats: utils.NewRingBuffer[docker.ContainerStat](300),
}, nil) }, nil)
server := NewServer(client, certs, "test") server, _ := NewServer(client, certs, "test")
go server.Serve(lis) go server.Serve(lis)
} }

View File

@@ -6,13 +6,14 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
"fmt"
"time" "time"
"github.com/amir20/dozzle/internal/agent/pb" "github.com/amir20/dozzle/internal/agent/pb"
"github.com/amir20/dozzle/internal/docker" "github.com/amir20/dozzle/internal/docker"
"github.com/rs/zerolog/log"
orderedmap "github.com/wk8/go-ordered-map/v2" orderedmap "github.com/wk8/go-ordered-map/v2"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
@@ -302,11 +303,11 @@ func (s *server) ContainerAction(ctx context.Context, in *pb.ContainerActionRequ
return &pb.ContainerActionResponse{}, nil return &pb.ContainerActionResponse{}, nil
} }
func NewServer(client docker.Client, certificates tls.Certificate, dozzleVersion string) *grpc.Server { func NewServer(client docker.Client, certificates tls.Certificate, dozzleVersion string) (*grpc.Server, error) {
caCertPool := x509.NewCertPool() caCertPool := x509.NewCertPool()
c, err := x509.ParseCertificate(certificates.Certificate[0]) c, err := x509.ParseCertificate(certificates.Certificate[0])
if err != nil { if err != nil {
log.Fatalf("failed to parse certificate: %v", err) return nil, fmt.Errorf("failed to parse certificate: %w", err)
} }
caCertPool.AddCert(c) caCertPool.AddCert(c)
@@ -323,7 +324,7 @@ func NewServer(client docker.Client, certificates tls.Certificate, dozzleVersion
grpcServer := grpc.NewServer(grpc.Creds(creds)) grpcServer := grpc.NewServer(grpc.Creds(creds))
pb.RegisterAgentServiceServer(grpcServer, newServer(client, dozzleVersion)) pb.RegisterAgentServiceServer(grpcServer, newServer(client, dozzleVersion))
return grpcServer return grpcServer, nil
} }
func logEventToPb(event *docker.LogEvent) *pb.LogEvent { func logEventToPb(event *docker.LogEvent) *pb.LogEvent {
@@ -344,7 +345,7 @@ func logEventToPb(event *docker.LogEvent) *pb.LogEvent {
}) })
default: default:
log.Fatalf("agent server: unknown type %T", event.Message) log.Error().Type("message", event.Message).Msg("agent server: unknown message type")
} }
return &pb.LogEvent{ return &pb.LogEvent{

View File

@@ -7,11 +7,11 @@ import (
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func SendBeacon(e BeaconEvent) error { func SendBeacon(e BeaconEvent) error {
log.Tracef("sending beacon: %+v", e) log.Trace().Interface("event", e).Msg("sending beacon")
jsonValue, err := json.Marshal(e) jsonValue, err := json.Marshal(e)
if err != nil { if err != nil {
return err return err
@@ -33,7 +33,7 @@ func SendBeacon(e BeaconEvent) error {
if err != nil { if err != nil {
return err return err
} }
log.Debugf("%v", string(dump)) log.Debug().Str("response", string(dump)).Msg("google analytics returned non-2xx status code")
return fmt.Errorf("google analytics returned non-2xx status code: %v", response.Status) return fmt.Errorf("google analytics returned non-2xx status code: %v", response.Status)
} }

View File

@@ -7,7 +7,7 @@ import (
"net/http" "net/http"
"strings" "strings"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
type contextKey string type contextKey string
@@ -49,6 +49,6 @@ func (p *proxyAuthContext) AuthMiddleware(next http.Handler) http.Handler {
} }
func (p *proxyAuthContext) CreateToken(username, password string) (string, error) { func (p *proxyAuthContext) CreateToken(username, password string) (string, error) {
log.Fatalf("CreateToken not implemented for proxy auth") log.Fatal().Msg("CreateToken not implemented in proxy auth")
return "", nil return "", nil
} }

View File

@@ -12,7 +12,7 @@ import (
"time" "time"
"github.com/go-chi/jwtauth/v5" "github.com/go-chi/jwtauth/v5"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -90,11 +90,11 @@ func decodeUsersFromFile(path string) (UserDatabase, error) {
for username, user := range users.Users { for username, user := range users.Users {
user.Username = username user.Username = username
if user.Password == "" { if user.Password == "" {
log.Fatalf("User %s has no password", username) log.Fatal().Msgf("User %s has an empty password", username)
} }
if len(user.Password) != 64 { if len(user.Password) != 64 {
log.Fatalf("User %s has an invalid password hash", username) log.Fatal().Str("password", user.Password).Msgf("User %s has an invalid password hash", username)
} }
if user.Name == "" { if user.Name == "" {
@@ -115,7 +115,7 @@ func (u *UserDatabase) readFileIfChanged() error {
} }
if info.ModTime().After(u.LastRead) { if info.ModTime().After(u.LastRead) {
log.Infof("Found changes to %s. Updating users...", u.Path) log.Info().Msg("Reloading user database")
users, err := decodeUsersFromFile(u.Path) users, err := decodeUsersFromFile(u.Path)
if err != nil { if err != nil {
return err return err
@@ -129,7 +129,8 @@ func (u *UserDatabase) readFileIfChanged() error {
func (u *UserDatabase) Find(username string) *User { func (u *UserDatabase) Find(username string) *User {
if err := u.readFileIfChanged(); err != nil { if err := u.readFileIfChanged(); err != nil {
log.Errorf("Error reading users file: %s", err) log.Error().Err(err).Msg("Failed to read user database")
return nil
} }
user, ok := u.Users[username] user, ok := u.Users[username]
if !ok { if !ok {

View File

@@ -4,7 +4,7 @@ import (
"sync" "sync"
"time" "time"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
type Cache[T any] struct { type Cache[T any] struct {
@@ -36,11 +36,7 @@ func (c *Cache[T]) GetWithHit() (T, error, bool) {
} }
c.Timestamp = time.Now() c.Timestamp = time.Now()
} }
if hit { log.Debug().Bool("hit", hit).Type("data", c.Data).Msg("Cache hit")
log.Debugf("Cache hit for %T", c.Data)
} else {
log.Debugf("Cache miss for %T", c.Data)
}
return c.Data, nil, hit return c.Data, nil, hit
} }

View File

@@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"path/filepath"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@@ -21,7 +20,7 @@ import (
"github.com/docker/docker/api/types/system" "github.com/docker/docker/api/types/system"
"github.com/docker/docker/client" "github.com/docker/docker/client"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
type StdType int type StdType int
@@ -83,7 +82,7 @@ type httpClient struct {
func NewClient(cli DockerCLI, filters filters.Args, host Host) Client { func NewClient(cli DockerCLI, filters filters.Args, host Host) Client {
info, err := cli.Info(context.Background()) info, err := cli.Info(context.Background())
if err != nil { if err != nil {
log.Errorf("unable to get docker info: %v", err) log.Error().Err(err).Msg("Failed to get docker info")
} }
host.NCPU = info.NCPU host.NCPU = info.NCPU
@@ -107,7 +106,7 @@ func NewLocalClient(f map[string][]string, hostname string) (Client, error) {
} }
} }
log.Debugf("filterArgs = %v", filterArgs) log.Debug().Interface("filterArgs", filterArgs).Msg("Creating local client")
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
@@ -149,10 +148,10 @@ func NewRemoteClient(f map[string][]string, host Host) (Client, error) {
} }
} }
log.Debugf("filterArgs = %v", filterArgs) log.Debug().Interface("filterArgs", filterArgs).Msg("Creating remote client")
if host.URL.Scheme != "tcp" { if host.URL.Scheme != "tcp" {
log.Fatal("Only tcp scheme is supported") return nil, fmt.Errorf("invalid scheme: %s", host.URL.Scheme)
} }
opts := []client.Opt{ opts := []client.Opt{
@@ -160,10 +159,10 @@ func NewRemoteClient(f map[string][]string, host Host) (Client, error) {
} }
if host.ValidCerts { if host.ValidCerts {
log.Debugf("Using TLS client config with certs at: %s", filepath.Dir(host.CertPath)) log.Debug().Str("caCertPath", host.CACertPath).Str("certPath", host.CertPath).Str("keyPath", host.KeyPath).Msg("Using TLS for remote client")
opts = append(opts, client.WithTLSClientConfig(host.CACertPath, host.CertPath, host.KeyPath)) opts = append(opts, client.WithTLSClientConfig(host.CACertPath, host.CertPath, host.KeyPath))
} else { } else {
log.Debugf("No valid certs found, using plain TCP") log.Debug().Msg("Not using TLS for remote client")
} }
opts = append(opts, client.WithAPIVersionNegotiation()) opts = append(opts, client.WithAPIVersionNegotiation())
@@ -181,7 +180,7 @@ func NewRemoteClient(f map[string][]string, host Host) (Client, error) {
// Finds a container by id, skipping the filters // Finds a container by id, skipping the filters
func (d *httpClient) FindContainer(id string) (Container, error) { func (d *httpClient) FindContainer(id string) (Container, error) {
log.Debugf("finding container with id: %s", id) log.Debug().Str("id", id).Msg("Finding container")
if json, err := d.cli.ContainerInspect(context.Background(), id); err == nil { if json, err := d.cli.ContainerInspect(context.Background(), id); err == nil {
return newContainerFromJSON(json, d.host.ID), nil return newContainerFromJSON(json, d.host.ID), nil
} else { } else {
@@ -204,7 +203,7 @@ func (d *httpClient) ContainerActions(action ContainerAction, containerID string
} }
func (d *httpClient) ListContainers() ([]Container, error) { func (d *httpClient) ListContainers() ([]Container, error) {
log.Debugf("listing containers with filters: %v", d.filters) log.Debug().Interface("filter", d.filters).Str("host", d.host.Name).Msg("Listing containers")
containerListOptions := container.ListOptions{ containerListOptions := container.ListOptions{
Filters: d.filters, Filters: d.filters,
All: true, All: true,
@@ -277,7 +276,7 @@ func (d *httpClient) ContainerStats(ctx context.Context, id string, stats chan<-
} }
func (d *httpClient) ContainerLogs(ctx context.Context, id string, since time.Time, stdType StdType) (io.ReadCloser, error) { func (d *httpClient) ContainerLogs(ctx context.Context, id string, since time.Time, stdType StdType) (io.ReadCloser, error) {
log.WithField("id", id).WithField("since", since).WithField("stdType", stdType).Debug("streaming logs for container") log.Debug().Str("id", id).Time("since", since).Stringer("stdType", stdType).Str("host", d.host.Name).Msg("Streaming logs for container")
sinceQuery := since.Add(-50 * time.Millisecond).Format(time.RFC3339Nano) sinceQuery := since.Add(-50 * time.Millisecond).Format(time.RFC3339Nano)
options := container.LogsOptions{ options := container.LogsOptions{
@@ -320,6 +319,7 @@ func (d *httpClient) ContainerEvents(ctx context.Context, messages chan<- Contai
} }
func (d *httpClient) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType StdType) (io.ReadCloser, error) { func (d *httpClient) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType StdType) (io.ReadCloser, error) {
log.Debug().Str("id", id).Time("from", from).Time("to", to).Stringer("stdType", stdType).Str("host", d.host.Name).Msg("Fetching logs between dates for container")
options := container.LogsOptions{ options := container.LogsOptions{
ShowStdout: stdType&STDOUT != 0, ShowStdout: stdType&STDOUT != 0,
ShowStderr: stdType&STDERR != 0, ShowStderr: stdType&STDERR != 0,
@@ -328,8 +328,6 @@ func (d *httpClient) ContainerLogsBetweenDates(ctx context.Context, id string, f
Until: to.Format(time.RFC3339Nano), Until: to.Format(time.RFC3339Nano),
} }
log.Debugf("fetching logs from Docker with option: %+v", options)
reader, err := d.cli.ContainerLogs(ctx, id, options) reader, err := d.cli.ContainerLogs(ctx, id, options)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -7,8 +7,8 @@ import (
"sync/atomic" "sync/atomic"
"github.com/puzpuzpuz/xsync/v3" "github.com/puzpuzpuz/xsync/v3"
"github.com/rs/zerolog/log"
"github.com/samber/lo" "github.com/samber/lo"
log "github.com/sirupsen/logrus"
"golang.org/x/sync/semaphore" "golang.org/x/sync/semaphore"
) )
@@ -51,10 +51,10 @@ var (
func (s *ContainerStore) checkConnectivity() error { func (s *ContainerStore) checkConnectivity() error {
if s.connected.CompareAndSwap(false, true) { if s.connected.CompareAndSwap(false, true) {
go func() { go func() {
log.Debugf("subscribing to docker events from container store %s", s.client.Host()) log.Debug().Str("host", s.client.Host().Name).Msg("docker store subscribing docker events")
err := s.client.ContainerEvents(s.ctx, s.events) err := s.client.ContainerEvents(s.ctx, s.events)
if !errors.Is(err, context.Canceled) { if !errors.Is(err, context.Canceled) {
log.Errorf("docker store unexpectedly disconnected from docker events from %s with %v", s.client.Host(), err) log.Error().Err(err).Str("host", s.client.Host().Name).Msg("docker store unexpectedly disconnected from docker events")
} }
s.connected.Store(false) s.connected.Store(false)
}() }()
@@ -76,7 +76,7 @@ func (s *ContainerStore) checkConnectivity() error {
for i, c := range running { for i, c := range running {
if err := sem.Acquire(s.ctx, 1); err != nil { if err := sem.Acquire(s.ctx, 1); err != nil {
log.Errorf("failed to acquire semaphore: %v", err) log.Error().Err(err).Msg("failed to acquire semaphore")
break break
} }
go func(c Container, i int) { go func(c Container, i int) {
@@ -88,10 +88,10 @@ func (s *ContainerStore) checkConnectivity() error {
} }
if err := sem.Acquire(s.ctx, maxFetchParallelism); err != nil { if err := sem.Acquire(s.ctx, maxFetchParallelism); err != nil {
log.Errorf("failed to acquire semaphore: %v", err) log.Error().Err(err).Msg("failed to acquire semaphore")
} }
log.Debugf("finished initializing container store with %d containers", len(containers)) log.Debug().Int("containers", len(containers)).Msg("finished initializing container store")
} }
} }
@@ -120,7 +120,7 @@ func (s *ContainerStore) FindContainer(id string) (Container, error) {
if ok { if ok {
return *container, nil return *container, nil
} else { } else {
log.Warnf("container %s not found in store", id) log.Warn().Str("id", id).Msg("container not found")
return Container{}, ErrContainerNotFound return Container{}, ErrContainerNotFound
} }
} }
@@ -132,7 +132,6 @@ func (s *ContainerStore) Client() Client {
func (s *ContainerStore) SubscribeEvents(ctx context.Context, events chan<- ContainerEvent) { func (s *ContainerStore) SubscribeEvents(ctx context.Context, events chan<- ContainerEvent) {
go func() { go func() {
if s.statsCollector.Start(s.ctx) { if s.statsCollector.Start(s.ctx) {
log.Debug("clearing container stats as stats collector has been stopped")
s.containers.Range(func(_ string, c *Container) bool { s.containers.Range(func(_ string, c *Container) bool {
c.Stats.Clear() c.Stats.Clear()
return true return true
@@ -171,7 +170,7 @@ func (s *ContainerStore) init() {
for { for {
select { select {
case event := <-s.events: case event := <-s.events:
log.Tracef("received event: %+v", event) log.Trace().Str("event", event.Name).Str("id", event.ActorID).Msg("received container event")
switch event.Name { switch event.Name {
case "start": case "start":
if container, err := s.client.FindContainer(event.ActorID); err == nil { if container, err := s.client.FindContainer(event.ActorID); err == nil {
@@ -183,7 +182,7 @@ func (s *ContainerStore) init() {
}) })
if valid { if valid {
log.Debugf("container %s started", container.ID) log.Debug().Str("id", container.ID).Msg("container started")
s.containers.Store(container.ID, &container) s.containers.Store(container.ID, &container)
s.newContainerSubscribers.Range(func(c context.Context, containers chan<- Container) bool { s.newContainerSubscribers.Range(func(c context.Context, containers chan<- Container) bool {
select { select {
@@ -195,13 +194,13 @@ func (s *ContainerStore) init() {
} }
} }
case "destroy": case "destroy":
log.Debugf("container %s destroyed", event.ActorID) log.Debug().Str("id", event.ActorID).Msg("container destroyed")
s.containers.Delete(event.ActorID) s.containers.Delete(event.ActorID)
case "die": case "die":
s.containers.Compute(event.ActorID, func(c *Container, loaded bool) (*Container, bool) { s.containers.Compute(event.ActorID, func(c *Container, loaded bool) (*Container, bool) {
if loaded { if loaded {
log.Debugf("container %s died", c.ID) log.Debug().Str("id", c.ID).Msg("container died")
c.State = "exited" c.State = "exited"
return c, false return c, false
} else { } else {
@@ -216,7 +215,7 @@ func (s *ContainerStore) init() {
s.containers.Compute(event.ActorID, func(c *Container, loaded bool) (*Container, bool) { s.containers.Compute(event.ActorID, func(c *Container, loaded bool) (*Container, bool) {
if loaded { if loaded {
log.Debugf("health status for container %s is %s", c.ID, healthy) log.Debug().Str("id", c.ID).Str("health", healthy).Msg("container health status changed")
c.Health = healthy c.Health = healthy
return c, false return c, false
} else { } else {

View File

@@ -17,7 +17,7 @@ import (
orderedmap "github.com/wk8/go-ordered-map/v2" orderedmap "github.com/wk8/go-ordered-map/v2"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
type EventGenerator struct { type EventGenerator struct {
@@ -100,7 +100,6 @@ func (g *EventGenerator) consumeReader() {
if readerError != nil { if readerError != nil {
if readerError != ErrBadHeader { if readerError != ErrBadHeader {
log.Tracef("reader error: %v", readerError)
g.Errors <- readerError g.Errors <- readerError
close(g.buffer) close(g.buffer)
break break
@@ -141,7 +140,7 @@ func readEvent(reader *bufio.Reader, tty bool) (string, StdType, error) {
return "", streamType, err return "", streamType, err
} }
if n != 8 { if n != 8 {
log.Warnf("unable to read header: %v", header) log.Warn().Bytes("header", header).Msg("short read")
message, _ := reader.ReadString('\n') message, _ := reader.ReadString('\n')
return message, streamType, ErrBadHeader return message, streamType, ErrBadHeader
} }
@@ -152,7 +151,7 @@ func readEvent(reader *bufio.Reader, tty bool) (string, StdType, error) {
case 2: case 2:
streamType = STDERR streamType = STDERR
default: default:
log.Warnf("unknown stream type: %v", header[0]) log.Warn().Bytes("header", header).Msg("unknown stream type")
} }
count := binary.BigEndian.Uint32(header[4:]) count := binary.BigEndian.Uint32(header[4:])
@@ -183,7 +182,7 @@ func createEvent(message string, streamType StdType) *LogEvent {
var jsonErr *json.UnmarshalTypeError var jsonErr *json.UnmarshalTypeError
if errors.As(err, &jsonErr) { if errors.As(err, &jsonErr) {
if jsonErr.Value == "string" { if jsonErr.Value == "string" {
log.Warnf("unable to parse json logs - error was \"%v\" while trying unmarshal \"%v\"", err.Error(), message) log.Warn().Err(err).Str("value", jsonErr.Value).Msg("failed to unmarshal json")
} }
} }
} else { } else {

View File

@@ -7,7 +7,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
type Host struct { type Host struct {
@@ -49,14 +49,14 @@ func ParseConnection(connection string) (Host, error) {
basePath, err := filepath.Abs("./certs") basePath, err := filepath.Abs("./certs")
if err != nil { if err != nil {
log.Fatalf("error converting certs path to absolute: %s", err) return Host{}, err
} }
host := remoteUrl.Hostname() host := remoteUrl.Hostname()
if _, err := os.Stat(filepath.Join(basePath, host)); !os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(basePath, host)); !os.IsNotExist(err) {
basePath = filepath.Join(basePath, host) basePath = filepath.Join(basePath, host)
} else { } else {
log.Debugf("Remote host certificate path does not exist %s, falling back to default: %s", filepath.Join(basePath, host), basePath) log.Debug().Msgf("Remote host certificate path does not exist %s, falling back to default: %s", filepath.Join(basePath, host), basePath)
} }
cacertPath := filepath.Join(basePath, "ca.pem") cacertPath := filepath.Join(basePath, "ca.pem")

View File

@@ -4,7 +4,7 @@ import (
"regexp" "regexp"
"strings" "strings"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
orderedmap "github.com/wk8/go-ordered-map/v2" orderedmap "github.com/wk8/go-ordered-map/v2"
) )
@@ -74,7 +74,7 @@ func guessLogLevel(logEvent *LogEvent) string {
} }
default: default:
log.Debugf("unknown type to guess level: %T", value) log.Debug().Type("type", value).Msg("unknown logEvent type")
} }
return "" return ""

View File

@@ -9,7 +9,7 @@ import (
"time" "time"
"github.com/puzpuzpuz/xsync/v3" "github.com/puzpuzpuz/xsync/v3"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
type StatsCollector struct { type StatsCollector struct {
@@ -48,7 +48,7 @@ func (c *StatsCollector) forceStop() {
if c.stopper != nil { if c.stopper != nil {
c.stopper() c.stopper()
c.stopper = nil c.stopper = nil
log.Debug("stopping container stats collector") log.Debug().Str("host", c.client.Host().ID).Msg("stopped container stats collector")
} }
} }
@@ -56,7 +56,6 @@ func (c *StatsCollector) Stop() {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
if c.totalStarted.Add(-1) == 0 { if c.totalStarted.Add(-1) == 0 {
log.Tracef("scheduled to stop container stats collector %s", c.client.Host())
c.timer = time.AfterFunc(timeToStop, func() { c.timer = time.AfterFunc(timeToStop, func() {
c.forceStop() c.forceStop()
}) })
@@ -66,7 +65,6 @@ func (c *StatsCollector) Stop() {
func (c *StatsCollector) reset() { func (c *StatsCollector) reset() {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
log.Tracef("resetting timer for container stats collector %s", c.client.Host())
if c.timer != nil { if c.timer != nil {
c.timer.Stop() c.timer.Stop()
} }
@@ -76,11 +74,11 @@ func (c *StatsCollector) reset() {
func streamStats(parent context.Context, sc *StatsCollector, id string) { func streamStats(parent context.Context, sc *StatsCollector, id string) {
ctx, cancel := context.WithCancel(parent) ctx, cancel := context.WithCancel(parent)
sc.cancelers.Store(id, cancel) sc.cancelers.Store(id, cancel)
log.Debugf("starting to stream stats for: %s", id) log.Debug().Str("container", id).Str("host", sc.client.Host().Name).Msg("starting to stream stats")
if err := sc.client.ContainerStats(ctx, id, sc.stream); err != nil { if err := sc.client.ContainerStats(ctx, id, sc.stream); err != nil {
log.Debugf("stopping to stream stats for: %s", id) log.Debug().Str("container", id).Str("host", sc.client.Host().Name).Err(err).Msg("stopping to stream stats")
if !errors.Is(err, context.Canceled) && !errors.Is(err, io.EOF) { if !errors.Is(err, context.Canceled) && !errors.Is(err, io.EOF) {
log.Errorf("unexpected error when streaming container stats: %v", err) log.Error().Str("container", id).Str("host", sc.client.Host().Name).Err(err).Msg("unexpected error while streaming stats")
} }
} }
} }
@@ -106,16 +104,16 @@ func (sc *StatsCollector) Start(parentCtx context.Context) bool {
} }
} }
} else { } else {
log.Errorf("error while listing containers: %v", err) log.Error().Str("host", sc.client.Host().Name).Err(err).Msg("failed to list containers")
} }
events := make(chan ContainerEvent) events := make(chan ContainerEvent)
go func() { go func() {
log.Debugf("subscribing to docker events from stats collector %s", sc.client.Host()) log.Debug().Str("host", sc.client.Host().Name).Msg("starting to listen to docker events")
err := sc.client.ContainerEvents(context.Background(), events) err := sc.client.ContainerEvents(context.Background(), events)
if !errors.Is(err, context.Canceled) { if !errors.Is(err, context.Canceled) {
log.Errorf("stats collector unexpectedly disconnected from docker events from %s with %v", sc.client.Host(), err) log.Error().Str("host", sc.client.Host().Name).Err(err).Msg("unexpected error while listening to docker events")
} }
sc.forceStop() sc.forceStop()
}() }()
@@ -137,7 +135,7 @@ func (sc *StatsCollector) Start(parentCtx context.Context) bool {
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
log.Info("stopped collecting container stats") log.Info().Str("host", sc.client.Host().Name).Msg("stopped container stats collector")
return true return true
case stat := <-sc.stream: case stat := <-sc.stream:
sc.subscribers.Range(func(c context.Context, stats chan<- ContainerStat) bool { sc.subscribers.Range(func(c context.Context, stats chan<- ContainerStat) bool {

View File

@@ -5,7 +5,7 @@ import (
"net/http" "net/http"
"strings" "strings"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func HttpRequest(addr string, base string) error { func HttpRequest(addr string, base string) error {
@@ -23,7 +23,7 @@ func HttpRequest(addr string, base string) error {
url = "http://" + url url = "http://" + url
} }
log.Info("Checking health of " + url) log.Info().Str("url", url).Msg("performing healthcheck")
resp, err := http.Get(url) resp, err := http.Get(url)
if err != nil { if err != nil {

View File

@@ -4,15 +4,15 @@ import (
"crypto/tls" "crypto/tls"
"github.com/amir20/dozzle/internal/agent" "github.com/amir20/dozzle/internal/agent"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func RPCRequest(addr string, certs tls.Certificate) error { func RPCRequest(addr string, certs tls.Certificate) error {
client, err := agent.NewClient(addr, certs) client, err := agent.NewClient(addr, certs)
if err != nil { if err != nil {
log.Fatalf("Failed to create agent client: %v", err) log.Fatal().Err(err).Msg("Failed to create agent client")
} }
containers, err := client.ListContainers() containers, err := client.ListContainers()
log.Tracef("Found %d containers.", len(containers)) log.Trace().Int("containers", len(containers)).Msg("Healtcheck RPC request completed")
return err return err
} }

View File

@@ -10,7 +10,7 @@ import (
"path/filepath" "path/filepath"
"github.com/amir20/dozzle/internal/auth" "github.com/amir20/dozzle/internal/auth"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
const ( const (
@@ -51,12 +51,12 @@ var mux = &sync.Mutex{}
func init() { func init() {
path, err := filepath.Abs("./data") path, err := filepath.Abs("./data")
if err != nil { if err != nil {
log.Fatalf("Unable to get absolute path for data directory: %s", err) log.Fatal().Err(err).Msg("Unable to get absolute path")
return return
} }
if _, err := os.Stat(path); os.IsNotExist(err) { if _, err := os.Stat(path); os.IsNotExist(err) {
if err := os.Mkdir(path, 0755); err != nil { if err := os.Mkdir(path, 0755); err != nil {
log.Fatalf("Unable to create data directory: %s", err) log.Fatal().Err(err).Msg("Unable to create data directory")
return return
} }
} }
@@ -103,7 +103,7 @@ func Save(user auth.User, profile Profile) error {
return err return err
} }
log.Debugf("Saved settings for user %s", user.Username) log.Debug().Str("path", filePath).Msg("Profile saved")
return f.Sync() return f.Sync()
} }

View File

@@ -3,7 +3,7 @@ package cli
import ( import (
"github.com/amir20/dozzle/internal/analytics" "github.com/amir20/dozzle/internal/analytics"
"github.com/amir20/dozzle/internal/docker" "github.com/amir20/dozzle/internal/docker"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func StartEvent(args Args, mode string, client docker.Client, subCommand string) { func StartEvent(args Args, mode string, client docker.Client, subCommand string) {
@@ -33,8 +33,8 @@ func StartEvent(args Args, mode string, client docker.Client, subCommand string)
event.ServerID = "n/a" event.ServerID = "n/a"
} }
log.Tracef("sending beacon event: %+v", event) log.Trace().Interface("event", event).Msg("Sending analytics event")
if err := analytics.SendBeacon(event); err != nil { if err := analytics.SendBeacon(event); err != nil {
log.Debug(err) log.Debug().Err(err).Msg("Failed to send analytics event")
} }
} }

View File

@@ -5,17 +5,16 @@ import (
"embed" "embed"
"os" "os"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func ReadCertificates(certs embed.FS) (tls.Certificate, error) { func ReadCertificates(certs embed.FS) (tls.Certificate, error) {
if pair, err := tls.LoadX509KeyPair("dozzle_cert.pem", "dozzle_key.pem"); err == nil { if pair, err := tls.LoadX509KeyPair("dozzle_cert.pem", "dozzle_key.pem"); err == nil {
log.Infof("Found dozzle certificate and key at ./dozzle_cert.pem and ./dozzle_key.pem") log.Info().Msg("Loaded custom dozzle certificate and key")
return pair, nil return pair, nil
} else { } else {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
log.Errorf("Failed to load dozzle certificate and key: %v", err) log.Fatal().Err(err).Msg("Failed to load custom dozzle certificate and key. Stopping...")
log.Warnf("Falling back to shared certificate and key")
} }
} }

View File

@@ -5,31 +5,30 @@ import (
"github.com/amir20/dozzle/internal/docker" "github.com/amir20/dozzle/internal/docker"
docker_support "github.com/amir20/dozzle/internal/support/docker" docker_support "github.com/amir20/dozzle/internal/support/docker"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func CreateMultiHostService(embeddedCerts embed.FS, args Args) (docker.Client, *docker_support.MultiHostService) { func CreateMultiHostService(embeddedCerts embed.FS, args Args) (docker.Client, *docker_support.MultiHostService) {
var clients []docker_support.ClientService var clients []docker_support.ClientService
if len(args.RemoteHost) > 0 { if len(args.RemoteHost) > 0 {
log.Warnf(`Remote host flag is deprecated and will be removed in future versions. Agents will replace remote hosts as a safer and performant option. See https://github.com/amir20/dozzle/issues/3066 for discussion.`) log.Warn().Msg(`Remote host flag is deprecated and will be removed in future versions. Agents will replace remote hosts as a safer and performant option. See https://github.com/amir20/dozzle/issues/3066 for discussion.`)
} }
for _, remoteHost := range args.RemoteHost { for _, remoteHost := range args.RemoteHost {
host, err := docker.ParseConnection(remoteHost) host, err := docker.ParseConnection(remoteHost)
if err != nil { if err != nil {
log.Fatalf("Could not parse remote host %s: %s", remoteHost, err) log.Fatal().Err(err).Interface("host", remoteHost).Msg("Could not parse remote host")
} }
log.Debugf("creating remote client for %s with %+v", host.Name, host)
log.Infof("Creating client for %s with %s", host.Name, host.URL.String()) log.Info().Interface("host", host).Msg("Adding remote host")
if client, err := docker.NewRemoteClient(args.Filter, host); err == nil { if client, err := docker.NewRemoteClient(args.Filter, host); err == nil {
if _, err := client.ListContainers(); err == nil { if _, err := client.ListContainers(); err == nil {
log.Debugf("connected to local Docker Engine")
clients = append(clients, docker_support.NewDockerClientService(client)) clients = append(clients, docker_support.NewDockerClientService(client))
} else { } else {
log.Warnf("Could not connect to remote host %s: %s", host.ID, err) log.Warn().Err(err).Interface("host", host).Msg("Could not connect to remote host")
} }
} else { } else {
log.Warnf("Could not create client for %s: %s", host.ID, err) log.Warn().Err(err).Interface("host", host).Msg("Could not create remote client")
} }
} }
@@ -37,16 +36,16 @@ func CreateMultiHostService(embeddedCerts embed.FS, args Args) (docker.Client, *
if err == nil { if err == nil {
_, err := localClient.ListContainers() _, err := localClient.ListContainers()
if err != nil { if err != nil {
log.Debugf("could not connect to local Docker Engine: %s", err) log.Debug().Err(err).Msg("Could not connect to local Docker Engine")
} else { } else {
log.Debugf("connected to local Docker Engine") log.Debug().Msg("Adding local Docker Engine")
clients = append(clients, docker_support.NewDockerClientService(localClient)) clients = append(clients, docker_support.NewDockerClientService(localClient))
} }
} }
certs, err := ReadCertificates(embeddedCerts) certs, err := ReadCertificates(embeddedCerts)
if err != nil { if err != nil {
log.Fatalf("Could not read certificates: %v", err) log.Fatal().Err(err).Msg("Could not read certificates")
} }
clientManager := docker_support.NewRetriableClientManager(args.RemoteAgent, certs, clients...) clientManager := docker_support.NewRetriableClientManager(args.RemoteAgent, certs, clients...)

View File

@@ -1,17 +1,25 @@
package cli package cli
import ( import (
log "github.com/sirupsen/logrus" "os"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
) )
func ConfigureLogger(level string) { func ConfigureLogger(level string) {
if l, err := log.ParseLevel(level); err == nil { if level, err := zerolog.ParseLevel(level); err == nil {
log.SetLevel(l) zerolog.SetGlobalLevel(level)
} else { } else {
panic(err) panic(err)
} }
log.SetFormatter(&log.TextFormatter{ _, dev := os.LookupEnv("DEV")
DisableLevelTruncation: true,
if dev {
writer := zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
w.FieldsOrder = []string{"id", "from", "to", "since"}
}) })
log.Logger = log.Output(writer)
}
} }

View File

@@ -5,7 +5,7 @@ import (
"reflect" "reflect"
"strings" "strings"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func ValidateEnvVars(types ...interface{}) { func ValidateEnvVars(types ...interface{}) {
@@ -26,7 +26,7 @@ func ValidateEnvVars(types ...interface{}) {
for _, env := range os.Environ() { for _, env := range os.Environ() {
actual := strings.Split(env, "=")[0] actual := strings.Split(env, "=")[0]
if strings.HasPrefix(actual, "DOZZLE_") && !expectedEnvs[actual] { if strings.HasPrefix(actual, "DOZZLE_") && !expectedEnvs[actual] {
log.Warnf("Unexpected environment variable %s", actual) log.Warn().Str("env", actual).Msg("Unexpected environment variable")
} }
} }
} }

View File

@@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"github.com/amir20/dozzle/internal/docker" "github.com/amir20/dozzle/internal/docker"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
type ContainerFilter = func(*docker.Container) bool type ContainerFilter = func(*docker.Container) bool
@@ -36,8 +36,6 @@ func NewMultiHostService(manager ClientManager) *MultiHostService {
manager: manager, manager: manager,
} }
log.Debugf("created multi host service manager %s", manager)
return m return m
} }
@@ -75,7 +73,7 @@ func (m *MultiHostService) ListAllContainers() ([]docker.Container, []error) {
list, err := client.ListContainers() list, err := client.ListContainers()
if err != nil { if err != nil {
host, _ := client.Host() host, _ := client.Host()
log.Debugf("error listing containers for host %s: %v", host.ID, err) log.Debug().Err(err).Str("host", host.Name).Msg("error listing containers")
host.Available = false host.Available = false
errors = append(errors, &HostUnavailableError{Host: host, Err: err}) errors = append(errors, &HostUnavailableError{Host: host, Err: err})
continue continue

View File

@@ -11,7 +11,7 @@ import (
"github.com/puzpuzpuz/xsync/v3" "github.com/puzpuzpuz/xsync/v3"
lop "github.com/samber/lo/parallel" lop "github.com/samber/lo/parallel"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
type RetriableClientManager struct { type RetriableClientManager struct {
@@ -23,18 +23,16 @@ type RetriableClientManager struct {
} }
func NewRetriableClientManager(agents []string, certs tls.Certificate, clients ...ClientService) *RetriableClientManager { func NewRetriableClientManager(agents []string, certs tls.Certificate, clients ...ClientService) *RetriableClientManager {
log.Debugf("creating retriable client manager with %d clients and %d agents", len(clients), len(agents))
clientMap := make(map[string]ClientService) clientMap := make(map[string]ClientService)
for _, client := range clients { for _, client := range clients {
host, err := client.Host() host, err := client.Host()
if err != nil { if err != nil {
log.Warnf("error fetching host info for client %s: %v", host.ID, err) log.Warn().Err(err).Str("host", host.Name).Msg("error fetching host info for client")
continue continue
} }
if _, ok := clientMap[host.ID]; ok { if _, ok := clientMap[host.ID]; ok {
log.Warnf("duplicate client found for host %s", host.ID) log.Warn().Str("host", host.Name).Msg("duplicate client found")
} else { } else {
clientMap[host.ID] = client clientMap[host.ID] = client
} }
@@ -44,20 +42,20 @@ func NewRetriableClientManager(agents []string, certs tls.Certificate, clients .
for _, endpoint := range agents { for _, endpoint := range agents {
agent, err := agent.NewClient(endpoint, certs) agent, err := agent.NewClient(endpoint, certs)
if err != nil { if err != nil {
log.Warnf("error creating agent client for %s: %v", endpoint, err) log.Warn().Err(err).Str("endpoint", endpoint).Msg("error creating agent client")
failed = append(failed, endpoint) failed = append(failed, endpoint)
continue continue
} }
host, err := agent.Host() host, err := agent.Host()
if err != nil { if err != nil {
log.Warnf("error fetching host info for agent %s: %v", endpoint, err) log.Warn().Err(err).Str("endpoint", endpoint).Msg("error fetching host info for agent")
failed = append(failed, endpoint) failed = append(failed, endpoint)
continue continue
} }
if _, ok := clientMap[host.ID]; ok { if _, ok := clientMap[host.ID]; ok {
log.Warnf("duplicate client found for host %s", host.ID) log.Warn().Str("host", host.Name).Str("id", host.ID).Msg("duplicate host with same ID found")
} else { } else {
clientMap[host.ID] = NewAgentService(agent) clientMap[host.ID] = NewAgentService(agent)
} }
@@ -88,7 +86,7 @@ func (m *RetriableClientManager) RetryAndList() ([]ClientService, []error) {
for _, endpoint := range m.failedAgents { for _, endpoint := range m.failedAgents {
agent, err := agent.NewClient(endpoint, m.certs) agent, err := agent.NewClient(endpoint, m.certs)
if err != nil { if err != nil {
log.Warnf("error creating agent client for %s: %v", endpoint, err) log.Warn().Err(err).Str("endpoint", endpoint).Msg("error creating agent client")
errors = append(errors, err) errors = append(errors, err)
newFailed = append(newFailed, endpoint) newFailed = append(newFailed, endpoint)
continue continue
@@ -96,7 +94,7 @@ func (m *RetriableClientManager) RetryAndList() ([]ClientService, []error) {
host, err := agent.Host() host, err := agent.Host()
if err != nil { if err != nil {
log.Warnf("error fetching host info for agent %s: %v", endpoint, err) log.Warn().Err(err).Str("endpoint", endpoint).Msg("error fetching host info for agent")
errors = append(errors, err) errors = append(errors, err)
newFailed = append(newFailed, endpoint) newFailed = append(newFailed, endpoint)
continue continue
@@ -153,7 +151,6 @@ func (m *RetriableClientManager) Hosts() []docker.Host {
hosts := lop.Map(clients, func(client ClientService, _ int) docker.Host { hosts := lop.Map(clients, func(client ClientService, _ int) docker.Host {
host, err := client.Host() host, err := client.Host()
log.Debugf("host: %v, err: %v", host, err)
if err != nil { if err != nil {
host.Available = false host.Available = false
} else { } else {

View File

@@ -14,7 +14,7 @@ import (
"github.com/samber/lo" "github.com/samber/lo"
lop "github.com/samber/lo/parallel" lop "github.com/samber/lo/parallel"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
type SwarmClientManager struct { type SwarmClientManager struct {
@@ -51,17 +51,17 @@ func NewSwarmClientManager(localClient docker.Client, certs tls.Certificate) *Sw
id, ok := os.LookupEnv("HOSTNAME") id, ok := os.LookupEnv("HOSTNAME")
if !ok { if !ok {
log.Fatal("HOSTNAME environment variable not set when looking for swarm service name") log.Fatal().Msg("HOSTNAME environment variable not set when looking for swarm service name")
} }
container, err := localClient.FindContainer(id) container, err := localClient.FindContainer(id)
if err != nil { if err != nil {
log.Fatalf("error finding container %s: %v", id, err) log.Fatal().Err(err).Msg("error finding own container when looking for swarm service name")
} }
serviceName := container.Labels["com.docker.swarm.service.name"] serviceName := container.Labels["com.docker.swarm.service.name"]
log.Debugf("found swarm internal service name: %s", serviceName) log.Debug().Str("service", serviceName).Msg("found swarm service name")
return &SwarmClientManager{ return &SwarmClientManager{
localClient: localClient, localClient: localClient,
@@ -85,12 +85,11 @@ func (m *SwarmClientManager) Subscribe(ctx context.Context, channel chan<- docke
func (m *SwarmClientManager) RetryAndList() ([]ClientService, []error) { func (m *SwarmClientManager) RetryAndList() ([]ClientService, []error) {
m.mu.Lock() m.mu.Lock()
log.Debugf("looking up swarm services: tasks.%s", m.name)
ips, err := net.LookupIP(fmt.Sprintf("tasks.%s", m.name)) ips, err := net.LookupIP(fmt.Sprintf("tasks.%s", m.name))
errors := make([]error, 0) errors := make([]error, 0)
if err != nil { if err != nil {
log.Fatalf("error looking up swarm services: %v", err) log.Fatal().Err(err).Msg("error looking up swarm service tasks")
errors = append(errors, err) errors = append(errors, err)
m.mu.Unlock() m.mu.Unlock()
return m.List(), errors return m.List(), errors
@@ -102,47 +101,51 @@ func (m *SwarmClientManager) RetryAndList() ([]ClientService, []error) {
return host.Endpoint return host.Endpoint
}) })
log.Debugf("tasks.dozzle = %v, localIP = %v, clients.endpoints = %v", ips, m.localIPs, lo.Keys(endpoints)) ipStrings := lo.Map(ips, func(ip net.IP, _ int) string {
return ip.String()
})
log.Debug().Strs(fmt.Sprintf("tasks.%s", m.name), ipStrings).Strs("localIPs", m.localIPs).Strs("clients.endpoints", lo.Keys(endpoints)).Msg("found swarm service tasks")
for _, ip := range ips { for _, ip := range ips {
if lo.Contains(m.localIPs, ip.String()) { if lo.Contains(m.localIPs, ip.String()) {
log.Debugf("skipping local ip %s", ip.String()) log.Debug().Stringer("ip", ip).Msg("skipping local IP")
continue continue
} }
if _, ok := endpoints[ip.String()+":7007"]; ok { if _, ok := endpoints[ip.String()+":7007"]; ok {
log.Debugf("skipping existing client for %s", ip.String()) log.Debug().Stringer("ip", ip).Msg("skipping existing client")
continue continue
} }
agent, err := agent.NewClient(ip.String()+":7007", m.certs) agent, err := agent.NewClient(ip.String()+":7007", m.certs)
if err != nil { if err != nil {
log.Warnf("error creating client for %s: %v", ip, err) log.Warn().Err(err).Stringer("ip", ip).Msg("error creating agent client")
errors = append(errors, err) errors = append(errors, err)
continue continue
} }
host, err := agent.Host() host, err := agent.Host()
if err != nil { if err != nil {
log.Warnf("error getting host data for agent %s: %v", ip, err) log.Warn().Err(err).Stringer("ip", ip).Msg("error getting host from agent client")
errors = append(errors, err) errors = append(errors, err)
if err := agent.Close(); err != nil { if err := agent.Close(); err != nil {
log.Warnf("error closing local client: %v", err) log.Warn().Err(err).Stringer("ip", ip).Msg("error closing agent client")
} }
continue continue
} }
if host.ID == m.localClient.Host().ID { if host.ID == m.localClient.Host().ID {
log.Debugf("skipping local client with ID %s", host.ID) log.Debug().Stringer("ip", ip).Msg("skipping local client")
if err := agent.Close(); err != nil { if err := agent.Close(); err != nil {
log.Warnf("error closing local client: %v", err) log.Warn().Err(err).Stringer("ip", ip).Msg("error closing agent client")
} }
continue continue
} }
client := NewAgentService(agent) client := NewAgentService(agent)
m.clients[host.ID] = client m.clients[host.ID] = client
log.Infof("added client for %s", host.ID) log.Info().Stringer("ip", ip).Str("id", host.ID).Str("name", host.Name).Msg("added new swarm agent")
m.subscribers.Range(func(ctx context.Context, channel chan<- docker.Host) bool { m.subscribers.Range(func(ctx context.Context, channel chan<- docker.Host) bool {
host.Available = true host.Available = true

View File

@@ -5,35 +5,33 @@ import (
"github.com/amir20/dozzle/internal/docker" "github.com/amir20/dozzle/internal/docker"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func (h *handler) containerActions(w http.ResponseWriter, r *http.Request) { func (h *handler) containerActions(w http.ResponseWriter, r *http.Request) {
action := chi.URLParam(r, "action") action := chi.URLParam(r, "action")
id := chi.URLParam(r, "id") id := chi.URLParam(r, "id")
log.Debugf("container action: %s, container id: %s", action, id)
containerService, err := h.multiHostService.FindContainer(hostKey(r), id) containerService, err := h.multiHostService.FindContainer(hostKey(r), id)
if err != nil { if err != nil {
log.Errorf("error while trying to find container: %v", err) log.Error().Err(err).Msg("error while trying to find container")
http.Error(w, err.Error(), http.StatusNotFound) http.Error(w, err.Error(), http.StatusNotFound)
return return
} }
parsedAction, err := docker.ParseContainerAction(action) parsedAction, err := docker.ParseContainerAction(action)
if err != nil { if err != nil {
log.Errorf("error while trying to parse action: %s", action) log.Error().Err(err).Msg("error while trying to parse action")
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
if err := containerService.Action(parsedAction); err != nil { if err := containerService.Action(parsedAction); err != nil {
log.Errorf("error while trying to perform action: %s", action) log.Error().Err(err).Msg("error while trying to perform container action")
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
log.Infof("container action performed: %s; container id: %s", action, id) log.Info().Str("action", action).Str("container", containerService.Container.Name).Msg("container action performed")
http.Error(w, "", http.StatusNoContent) http.Error(w, "", http.StatusNoContent)
} }

View File

@@ -4,7 +4,7 @@ import (
"net/http" "net/http"
"time" "time"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func (h *handler) createToken(w http.ResponseWriter, r *http.Request) { func (h *handler) createToken(w http.ResponseWriter, r *http.Request) {
@@ -19,11 +19,11 @@ func (h *handler) createToken(w http.ResponseWriter, r *http.Request) {
Path: "/", Path: "/",
SameSite: http.SameSiteLaxMode, SameSite: http.SameSiteLaxMode,
}) })
log.Infof("Token created for user %s", user) log.Info().Str("user", user).Msg("Token created")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write([]byte(http.StatusText(http.StatusOK))) w.Write([]byte(http.StatusText(http.StatusOK)))
} else { } else {
log.Errorf("Error while creating token: %v", err) log.Error().Err(err).Msg("Failed to create token")
http.Error(w, err.Error(), http.StatusUnauthorized) http.Error(w, err.Error(), http.StatusUnauthorized)
} }
} }

View File

@@ -9,8 +9,7 @@ import (
"github.com/amir20/dozzle/internal/analytics" "github.com/amir20/dozzle/internal/analytics"
"github.com/amir20/dozzle/internal/docker" "github.com/amir20/dozzle/internal/docker"
docker_support "github.com/amir20/dozzle/internal/support/docker" docker_support "github.com/amir20/dozzle/internal/support/docker"
"github.com/rs/zerolog/log"
log "github.com/sirupsen/logrus"
) )
func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) { func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
@@ -37,17 +36,18 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
allContainers, errors := h.multiHostService.ListAllContainers() allContainers, errors := h.multiHostService.ListAllContainers()
for _, err := range errors { for _, err := range errors {
log.Warnf("error listing containers: %v", err) log.Warn().Err(err).Msg("error listing containers")
if hostNotAvailableError, ok := err.(*docker_support.HostUnavailableError); ok { if hostNotAvailableError, ok := err.(*docker_support.HostUnavailableError); ok {
bytes, _ := json.Marshal(hostNotAvailableError.Host) bytes, _ := json.Marshal(hostNotAvailableError.Host)
if _, err := fmt.Fprintf(w, "event: update-host\ndata: %s\n\n", string(bytes)); err != nil { if _, err := fmt.Fprintf(w, "event: update-host\ndata: %s\n\n", string(bytes)); err != nil {
log.Errorf("error writing event to event stream: %v", err) log.Error().Err(err).Msg("error writing event to event stream")
} }
} }
} }
if err := sendContainersJSON(allContainers, w); err != nil { if err := sendContainersJSON(allContainers, w); err != nil {
log.Errorf("error writing containers to event stream: %v", err) log.Error().Err(err).Msg("error writing containers to event stream")
return
} }
f.Flush() f.Flush()
@@ -59,13 +59,13 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
case host := <-availableHosts: case host := <-availableHosts:
bytes, _ := json.Marshal(host) bytes, _ := json.Marshal(host)
if _, err := fmt.Fprintf(w, "event: update-host\ndata: %s\n\n", string(bytes)); err != nil { if _, err := fmt.Fprintf(w, "event: update-host\ndata: %s\n\n", string(bytes)); err != nil {
log.Errorf("error writing event to event stream: %v", err) log.Error().Err(err).Msg("error writing event to event stream")
} }
f.Flush() f.Flush()
case stat := <-stats: case stat := <-stats:
bytes, _ := json.Marshal(stat) bytes, _ := json.Marshal(stat)
if _, err := fmt.Fprintf(w, "event: container-stat\ndata: %s\n\n", string(bytes)); err != nil { if _, err := fmt.Fprintf(w, "event: container-stat\ndata: %s\n\n", string(bytes)); err != nil {
log.Errorf("error writing stat to event stream: %v", err) log.Error().Err(err).Msg("error writing event to event stream")
return return
} }
f.Flush() f.Flush()
@@ -76,10 +76,10 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
switch event.Name { switch event.Name {
case "start", "die", "destroy": case "start", "die", "destroy":
if event.Name == "start" { if event.Name == "start" {
log.Debugf("found new container with id: %v", event.ActorID) log.Debug().Str("container", event.ActorID).Msg("container started")
if containers, err := h.multiHostService.ListContainersForHost(event.Host); err == nil { if containers, err := h.multiHostService.ListContainersForHost(event.Host); err == nil {
if err := sendContainersJSON(containers, w); err != nil { if err := sendContainersJSON(containers, w); err != nil {
log.Errorf("error encoding containers to stream: %v", err) log.Error().Err(err).Msg("error writing containers to event stream")
return return
} }
} }
@@ -87,14 +87,14 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
bytes, _ := json.Marshal(event) bytes, _ := json.Marshal(event)
if _, err := fmt.Fprintf(w, "event: container-event\ndata: %s\n\n", string(bytes)); err != nil { if _, err := fmt.Fprintf(w, "event: container-event\ndata: %s\n\n", string(bytes)); err != nil {
log.Errorf("error writing event to event stream: %v", err) log.Error().Err(err).Msg("error writing event to event stream")
return return
} }
f.Flush() f.Flush()
case "health_status: healthy", "health_status: unhealthy": case "health_status: healthy", "health_status: unhealthy":
log.Debugf("triggering docker health event: %v", event.Name) log.Debug().Str("container", event.ActorID).Str("health", event.Name).Msg("container health status")
healthy := "unhealthy" healthy := "unhealthy"
if event.Name == "health_status: healthy" { if event.Name == "health_status: healthy" {
healthy = "healthy" healthy = "healthy"
@@ -105,13 +105,12 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
} }
bytes, _ := json.Marshal(payload) bytes, _ := json.Marshal(payload)
if _, err := fmt.Fprintf(w, "event: container-health\ndata: %s\n\n", string(bytes)); err != nil { if _, err := fmt.Fprintf(w, "event: container-health\ndata: %s\n\n", string(bytes)); err != nil {
log.Errorf("error writing event to event stream: %v", err) log.Error().Err(err).Msg("error writing event to event stream")
return return
} }
f.Flush() f.Flush()
} }
case <-ctx.Done(): case <-ctx.Done():
log.Debugf("context done, closing event stream")
return return
} }
} }
@@ -140,7 +139,7 @@ func sendBeaconEvent(h *handler, r *http.Request, runningContainers int) {
} }
if err := analytics.SendBeacon(b); err != nil { if err := analytics.SendBeacon(b); err != nil {
log.Debugf("error sending beacon: %v", err) log.Debug().Err(err).Msg("error sending beacon")
} }
} }

View File

@@ -3,17 +3,17 @@ package web
import ( import (
"net/http" "net/http"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func (h *handler) healthcheck(w http.ResponseWriter, r *http.Request) { func (h *handler) healthcheck(w http.ResponseWriter, r *http.Request) {
log.Trace("Executing healthcheck request") log.Trace().Msg("Healthcheck request received")
_, errors := h.multiHostService.ListAllContainers() _, errors := h.multiHostService.ListAllContainers()
if len(errors) > 0 { if len(errors) > 0 {
log.Error(errors) log.Error().Err(errors[0]).Msg("Error listing containers")
http.Error(w, "Error listing containers", http.StatusInternalServerError) http.Error(w, "Error listing containers", http.StatusInternalServerError)
} else { } else {
http.Error(w, "OK", http.StatusOK) w.WriteHeader(http.StatusOK)
} }
} }

View File

@@ -16,7 +16,7 @@ import (
"github.com/amir20/dozzle/internal/auth" "github.com/amir20/dozzle/internal/auth"
"github.com/amir20/dozzle/internal/profile" "github.com/amir20/dozzle/internal/profile"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func (h *handler) index(w http.ResponseWriter, req *http.Request) { func (h *handler) index(w http.ResponseWriter, req *http.Request) {
@@ -70,15 +70,15 @@ func (h *handler) executeTemplate(w http.ResponseWriter, req *http.Request) {
} }
config["user"] = user config["user"] = user
} else if h.config.Authorization.Provider == FORWARD_PROXY { } else if h.config.Authorization.Provider == FORWARD_PROXY {
log.Error("Unable to find remote user. Please check your proxy configuration. Expecting headers Remote-Email, Remote-User, Remote-Name.") log.Error().Msg("Unable to find remote user. Please check your proxy configuration. Expecting headers Remote-Email, Remote-User, Remote-Name.")
log.Debugf("Dumping all headers for url /%s", req.URL.String()) log.Debug().Str("url", req.URL.String()).Msg("Dumping all headers for request")
for k, v := range req.Header { for k, v := range req.Header {
log.Debugf("%s: %s", k, v) log.Debug().Strs(k, v).Send()
} }
http.Error(w, "Unauthorized user", http.StatusUnauthorized) http.Error(w, "Unauthorized user", http.StatusUnauthorized)
return return
} else if h.config.Authorization.Provider == SIMPLE && req.URL.Path != "login" { } else if h.config.Authorization.Provider == SIMPLE && req.URL.Path != "login" {
log.Debugf("Redirecting to login page for url /%s", req.URL.String()) log.Debug().Str("url", req.URL.String()).Msg("Redirecting to login page")
http.Redirect(w, req, path.Clean(h.config.Base+"/login")+"?redirectUrl=/"+req.URL.String(), http.StatusTemporaryRedirect) http.Redirect(w, req, path.Clean(h.config.Base+"/login")+"?redirectUrl=/"+req.URL.String(), http.StatusTemporaryRedirect)
return return
} }
@@ -91,11 +91,11 @@ func (h *handler) executeTemplate(w http.ResponseWriter, req *http.Request) {
} }
file, err := h.content.Open("index.html") file, err := h.content.Open("index.html")
if err != nil { if err != nil {
log.Panic(err) log.Fatal().Err(err).Msg("Could not open index.html")
} }
bytes, err := io.ReadAll(file) bytes, err := io.ReadAll(file)
if err != nil { if err != nil {
log.Panic(err) log.Fatal().Err(err).Msg("Could not read index.html")
} }
tmpl, err := template.New("index.html").Funcs(template.FuncMap{ tmpl, err := template.New("index.html").Funcs(template.FuncMap{
"marshal": func(v interface{}) template.JS { "marshal": func(v interface{}) template.JS {
@@ -109,13 +109,12 @@ func (h *handler) executeTemplate(w http.ResponseWriter, req *http.Request) {
}, },
}).Parse(string(bytes)) }).Parse(string(bytes))
if err != nil { if err != nil {
log.Panic(err) log.Fatal().Err(err).Msg("Could not parse index.html")
} }
err = tmpl.Execute(w, data) err = tmpl.Execute(w, data)
if err != nil { if err != nil {
log.Panic(err) log.Fatal().Err(err).Msg("Could not execute index.html")
http.Error(w, err.Error(), http.StatusInternalServerError)
} }
} }
@@ -130,12 +129,12 @@ func (h *handler) readManifest() map[string]interface{} {
} }
bytes, err := io.ReadAll(file) bytes, err := io.ReadAll(file)
if err != nil { if err != nil {
log.Fatalf("Could not read .vite/manifest.json: %v", err) log.Fatal().Err(err).Msg("Could not read .vite/manifest.json")
} }
var manifest map[string]interface{} var manifest map[string]interface{}
err = json.Unmarshal(bytes, &manifest) err = json.Unmarshal(bytes, &manifest)
if err != nil { if err != nil {
log.Fatalf("Could not parse .vite/manifest.json: %v", err) log.Fatal().Err(err).Msg("Could not unmarshal .vite/manifest.json")
} }
return manifest return manifest
} }

View File

@@ -21,7 +21,7 @@ import (
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func (h *handler) downloadLogs(w http.ResponseWriter, r *http.Request) { func (h *handler) downloadLogs(w http.ResponseWriter, r *http.Request) {
@@ -105,7 +105,9 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request)
events, err := containerService.LogsBetweenDates(r.Context(), from, to, stdTypes) events, err := containerService.LogsBetweenDates(r.Context(), from, to, stdTypes)
if err != nil { if err != nil {
log.Errorf("error while streaming logs %v", err.Error()) log.Error().Err(err).Msg("error fetching logs")
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} }
buffer := utils.NewRingBuffer[*docker.LogEvent](500) buffer := utils.NewRingBuffer[*docker.LogEvent](500)
@@ -117,7 +119,8 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request)
encoder := json.NewEncoder(w) encoder := json.NewEncoder(w)
for _, event := range buffer.Data() { for _, event := range buffer.Data() {
if err := encoder.Encode(event); err != nil { if err := encoder.Encode(event); err != nil {
log.Errorf("json encoding error while streaming %v", err.Error()) log.Error().Err(err).Msg("error encoding log event")
return
} }
} }
} }
@@ -202,22 +205,22 @@ func streamLogsForContainers(w http.ResponseWriter, r *http.Request, multiHostCl
defer ticker.Stop() defer ticker.Stop()
existingContainers, errs := multiHostClient.ListAllContainersFiltered(filter) existingContainers, errs := multiHostClient.ListAllContainersFiltered(filter)
if len(errs) > 0 { if len(errs) > 0 {
log.Warnf("error while listing containers %v", errs) log.Warn().Err(errs[0]).Msg("error while listing containers")
} }
streamLogs := func(container docker.Container) { streamLogs := func(container docker.Container) {
containerService, err := multiHostClient.FindContainer(container.Host, container.ID) containerService, err := multiHostClient.FindContainer(container.Host, container.ID)
if err != nil { if err != nil {
log.Errorf("error while finding container %v", err.Error()) log.Error().Err(err).Msg("error while finding container")
return return
} }
err = containerService.StreamLogs(r.Context(), container.StartedAt, stdTypes, logs) err = containerService.StreamLogs(r.Context(), container.StartedAt, stdTypes, logs)
if err != nil { if err != nil {
if errors.Is(err, io.EOF) { if errors.Is(err, io.EOF) {
log.WithError(err).Debugf("stream closed for container %v", container.Name) log.Debug().Str("container", container.ID).Msg("streaming ended")
events <- &docker.ContainerEvent{ActorID: container.ID, Name: "container-stopped", Host: container.Host} events <- &docker.ContainerEvent{ActorID: container.ID, Name: "container-stopped", Host: container.Host}
} else if !errors.Is(err, context.Canceled) { } else if !errors.Is(err, context.Canceled) {
log.Errorf("unknown error while streaming %v", err.Error()) log.Error().Err(err).Str("container", container.ID).Msg("unknown error while streaming logs")
} }
} }
} }
@@ -234,7 +237,7 @@ loop:
select { select {
case event := <-logs: case event := <-logs:
if buf, err := json.Marshal(event); err != nil { if buf, err := json.Marshal(event); err != nil {
log.Errorf("json encoding error while streaming %v", err.Error()) log.Error().Err(err).Msg("error encoding log event")
} else { } else {
fmt.Fprintf(w, "data: %s\n", buf) fmt.Fprintf(w, "data: %s\n", buf)
} }
@@ -251,28 +254,26 @@ loop:
go streamLogs(container) go streamLogs(container)
case event := <-events: case event := <-events:
log.Debugf("received container event %v", event) log.Debug().Str("event", event.Name).Str("container", event.ActorID).Msg("received event")
if buf, err := json.Marshal(event); err != nil { if buf, err := json.Marshal(event); err != nil {
log.Errorf("json encoding error while streaming %v", err.Error()) log.Error().Err(err).Msg("error encoding container event")
} else { } else {
fmt.Fprintf(w, "event: container-event\ndata: %s\n\n", buf) fmt.Fprintf(w, "event: container-event\ndata: %s\n\n", buf)
f.Flush() f.Flush()
} }
case <-r.Context().Done(): case <-r.Context().Done():
log.Debugf("context cancelled")
break loop break loop
} }
} }
if log.IsLevelEnabled(log.DebugLevel) { if e := log.Debug(); e.Enabled() {
var m runtime.MemStats var m runtime.MemStats
runtime.ReadMemStats(&m) runtime.ReadMemStats(&m)
log.WithFields(log.Fields{ e.Str("allocated", humanize.Bytes(m.Alloc)).
"allocated": humanize.Bytes(m.Alloc), Str("totalAllocated", humanize.Bytes(m.TotalAlloc)).
"totalAllocated": humanize.Bytes(m.TotalAlloc), Str("system", humanize.Bytes(m.Sys)).
"system": humanize.Bytes(m.Sys), Int("routines", runtime.NumGoroutine()).
"routines": runtime.NumGoroutine(), Msg("runtime mem stats")
}).Debug("runtime mem stats")
} }
} }

View File

@@ -6,7 +6,7 @@ import (
"github.com/amir20/dozzle/internal/auth" "github.com/amir20/dozzle/internal/auth"
"github.com/amir20/dozzle/internal/profile" "github.com/amir20/dozzle/internal/profile"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
func (h *handler) updateProfile(w http.ResponseWriter, r *http.Request) { func (h *handler) updateProfile(w http.ResponseWriter, r *http.Request) {
@@ -18,7 +18,7 @@ func (h *handler) updateProfile(w http.ResponseWriter, r *http.Request) {
if err := profile.UpdateFromReader(*user, r.Body); err != nil { if err := profile.UpdateFromReader(*user, r.Body); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
log.Errorf("Unable to save user settings: %s", err) log.Error().Err(err).Msg("Failed to update profile")
return return
} }
@@ -39,7 +39,7 @@ func (h *handler) avatar(w http.ResponseWriter, r *http.Request) {
return return
} }
log.Debugf("Fetching avatar from %s", url) log.Trace().Str("url", url).Msg("Fetching avatar")
response, err := http.Get(url) response, err := http.Get(url)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -49,7 +49,7 @@ func (h *handler) avatar(w http.ResponseWriter, r *http.Request) {
defer response.Body.Close() defer response.Body.Close()
if response.StatusCode != http.StatusOK { if response.StatusCode != http.StatusOK {
log.Errorf("Received status code %d from %s", response.StatusCode, url) log.Error().Str("url", url).Int("status", response.StatusCode).Msg("Failed to fetch avatar")
return return
} }

View File

@@ -7,7 +7,7 @@ import (
"github.com/amir20/dozzle/internal/cache" "github.com/amir20/dozzle/internal/cache"
"github.com/amir20/dozzle/internal/releases" "github.com/amir20/dozzle/internal/releases"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
var cachedReleases *cache.Cache[[]releases.Release] var cachedReleases *cache.Cache[[]releases.Release]
@@ -22,7 +22,7 @@ func (h *handler) releases(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
log.Debugf("error reading releases: %v", err) log.Debug().Err(err).Msg("error fetching releases")
return return
} }
@@ -32,6 +32,7 @@ func (h *handler) releases(w http.ResponseWriter, r *http.Request) {
} }
if err := json.NewEncoder(w).Encode(releases); err != nil { if err := json.NewEncoder(w).Encode(releases); err != nil {
log.Errorf("json encoding error while streaming %v", err.Error()) log.Error().Err(err).Msg("error encoding releases")
http.Error(w, err.Error(), http.StatusInternalServerError)
} }
} }

View File

@@ -10,7 +10,7 @@ import (
docker_support "github.com/amir20/dozzle/internal/support/docker" docker_support "github.com/amir20/dozzle/internal/support/docker"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
log "github.com/sirupsen/logrus" "github.com/rs/zerolog/log"
) )
type AuthProvider string type AuthProvider string
@@ -73,7 +73,7 @@ func createRouter(h *handler) *chi.Mux {
} }
if h.config.Authorization.Provider != NONE && h.config.Authorization.Authorizer == nil { if h.config.Authorization.Provider != NONE && h.config.Authorization.Authorizer == nil {
log.Panic("Authorization provider is set but no authorizer is provided") log.Fatal().Msg("Authorization provider is set but no authorizer is provided")
} }
r.Route(base, func(r chi.Router) { r.Route(base, func(r chi.Router) {
@@ -134,7 +134,7 @@ func hostKey(r *http.Request) string {
host := chi.URLParam(r, "host") host := chi.URLParam(r, "host")
if host == "" { if host == "" {
log.Fatalf("No host found for url %v", r.URL) log.Fatal().Str("url", r.URL.String()).Msg("Host parameter not found in the URL path")
} }
return host return host

102
main.go
View File

@@ -21,8 +21,7 @@ import (
"github.com/amir20/dozzle/internal/support/cli" "github.com/amir20/dozzle/internal/support/cli"
docker_support "github.com/amir20/dozzle/internal/support/docker" docker_support "github.com/amir20/dozzle/internal/support/docker"
"github.com/amir20/dozzle/internal/web" "github.com/amir20/dozzle/internal/web"
"github.com/rs/zerolog/log"
log "github.com/sirupsen/logrus"
) )
//go:embed all:dist //go:embed all:dist
@@ -40,44 +39,48 @@ func main() {
case *cli.AgentCmd: case *cli.AgentCmd:
client, err := docker.NewLocalClient(args.Filter, args.Hostname) client, err := docker.NewLocalClient(args.Filter, args.Hostname)
if err != nil { if err != nil {
log.Fatalf("Could not create docker client: %v", err) log.Fatal().Err(err).Msg("Could not create docker client")
} }
certs, err := cli.ReadCertificates(certs) certs, err := cli.ReadCertificates(certs)
if err != nil { if err != nil {
log.Fatalf("Could not read certificates: %v", err) log.Fatal().Err(err).Msg("Could not read certificates")
} }
listener, err := net.Listen("tcp", args.Agent.Addr) listener, err := net.Listen("tcp", args.Agent.Addr)
if err != nil { if err != nil {
log.Fatalf("failed to listen: %v", err) log.Fatal().Err(err).Msg("failed to listen")
} }
tempFile, err := os.CreateTemp("./", "agent-*.addr") tempFile, err := os.CreateTemp("./", "agent-*.addr")
if err != nil { if err != nil {
log.Fatalf("failed to create temp file: %v", err) log.Fatal().Err(err).Msg("failed to create temp file")
} }
io.WriteString(tempFile, listener.Addr().String()) io.WriteString(tempFile, listener.Addr().String())
go cli.StartEvent(args, "", client, "agent") go cli.StartEvent(args, "", client, "agent")
server := agent.NewServer(client, certs, args.Version()) server, err := agent.NewServer(client, certs, args.Version())
if err != nil {
log.Fatal().Err(err).Msg("failed to create agent server")
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop() defer stop()
go func() { go func() {
log.Infof("Dozzle agent version %s", args.Version()) log.Info().Msgf("Dozzle agent version %s", args.Version())
log.Infof("Agent listening on %s", listener.Addr().String()) log.Info().Msgf("Agent listening on %s", listener.Addr().String())
if err := server.Serve(listener); err != nil { if err := server.Serve(listener); err != nil {
log.Fatalf("failed to serve: %v", err) log.Error().Err(err).Msg("failed to serve")
} }
}() }()
<-ctx.Done() <-ctx.Done()
stop() stop()
log.Info("Shutting down agent") log.Info().Msg("Shutting down agent")
server.Stop() server.Stop()
log.Debugf("deleting %s", tempFile.Name()) log.Debug().Str("file", tempFile.Name()).Msg("Removing temp file")
os.Remove(tempFile.Name()) os.Remove(tempFile.Name())
case *cli.HealthcheckCmd: case *cli.HealthcheckCmd:
files, err := os.ReadDir(".") files, err := os.ReadDir(".")
if err != nil { if err != nil {
log.Fatalf("Failed to read directory: %v", err) log.Fatal().Err(err).Msg("Failed to read directory")
} }
agentAddress := "" agentAddress := ""
@@ -85,7 +88,7 @@ func main() {
if match, _ := filepath.Match("agent-*.addr", file.Name()); match { if match, _ := filepath.Match("agent-*.addr", file.Name()); match {
data, err := os.ReadFile(file.Name()) data, err := os.ReadFile(file.Name())
if err != nil { if err != nil {
log.Fatalf("Failed to read file: %v", err) log.Fatal().Err(err).Msg("Failed to read file")
} }
agentAddress = string(data) agentAddress = string(data)
break break
@@ -93,22 +96,22 @@ func main() {
} }
if agentAddress == "" { if agentAddress == "" {
if err := healthcheck.HttpRequest(args.Addr, args.Base); err != nil { if err := healthcheck.HttpRequest(args.Addr, args.Base); err != nil {
log.Fatalf("Failed to make request: %v", err) log.Fatal().Err(err).Msg("Failed to make request")
} }
} else { } else {
certs, err := cli.ReadCertificates(certs) certs, err := cli.ReadCertificates(certs)
if err != nil { if err != nil {
log.Fatalf("Could not read certificates: %v", err) log.Fatal().Err(err).Msg("Could not read certificates")
} }
if err := healthcheck.RPCRequest(agentAddress, certs); err != nil { if err := healthcheck.RPCRequest(agentAddress, certs); err != nil {
log.Fatalf("Failed to make request: %v", err) log.Fatal().Err(err).Msg("Failed to make request")
} }
} }
case *cli.GenerateCmd: case *cli.GenerateCmd:
cli.StartEvent(args, "", nil, "generate") cli.StartEvent(args, "", nil, "generate")
if args.Generate.Username == "" || args.Generate.Password == "" { if args.Generate.Username == "" || args.Generate.Password == "" {
log.Fatal("Username and password are required") log.Fatal().Msg("Username and password are required")
} }
buffer := auth.GenerateUsers(auth.User{ buffer := auth.GenerateUsers(auth.User{
@@ -119,7 +122,7 @@ func main() {
}, true) }, true)
if _, err := os.Stdout.Write(buffer.Bytes()); err != nil { if _, err := os.Stdout.Write(buffer.Bytes()); err != nil {
log.Fatalf("Failed to write to stdout: %v", err) log.Fatal().Err(err).Msg("Failed to write to stdout")
} }
} }
@@ -127,55 +130,58 @@ func main() {
} }
if args.AuthProvider != "none" && args.AuthProvider != "forward-proxy" && args.AuthProvider != "simple" { if args.AuthProvider != "none" && args.AuthProvider != "forward-proxy" && args.AuthProvider != "simple" {
log.Fatalf("Invalid auth provider %s", args.AuthProvider) log.Fatal().Str("provider", args.AuthProvider).Msg("Invalid auth provider")
} }
log.Infof("Dozzle version %s", args.Version()) log.Info().Msgf("Dozzle version %s", args.Version())
var multiHostService *docker_support.MultiHostService var multiHostService *docker_support.MultiHostService
if args.Mode == "server" { if args.Mode == "server" {
var localClient docker.Client var localClient docker.Client
localClient, multiHostService = cli.CreateMultiHostService(certs, args) localClient, multiHostService = cli.CreateMultiHostService(certs, args)
if multiHostService.TotalClients() == 0 { if multiHostService.TotalClients() == 0 {
log.Fatal("Could not connect to any Docker Engines") log.Fatal().Msg("Could not connect to any Docker Engine")
} else { } else {
log.Infof("Connected to %d Docker Engine(s)", multiHostService.TotalClients()) log.Info().Int("clients", multiHostService.TotalClients()).Msg("Connected to Docker")
} }
go cli.StartEvent(args, "server", localClient, "") go cli.StartEvent(args, "server", localClient, "")
} else if args.Mode == "swarm" { } else if args.Mode == "swarm" {
localClient, err := docker.NewLocalClient(args.Filter, args.Hostname) localClient, err := docker.NewLocalClient(args.Filter, args.Hostname)
if err != nil { if err != nil {
log.Fatalf("Could not connect to local Docker Engine: %s", err) log.Fatal().Err(err).Msg("Could not create docker client")
} }
certs, err := cli.ReadCertificates(certs) certs, err := cli.ReadCertificates(certs)
if err != nil { if err != nil {
log.Fatalf("Could not read certificates: %v", err) log.Fatal().Err(err).Msg("Could not read certificates")
} }
manager := docker_support.NewSwarmClientManager(localClient, certs) manager := docker_support.NewSwarmClientManager(localClient, certs)
multiHostService = docker_support.NewMultiHostService(manager) multiHostService = docker_support.NewMultiHostService(manager)
log.Infof("Starting in Swarm mode") log.Info().Msg("Starting in swarm mode")
listener, err := net.Listen("tcp", ":7007") listener, err := net.Listen("tcp", ":7007")
if err != nil { if err != nil {
log.Fatalf("failed to listen: %v", err) log.Fatal().Err(err).Msg("failed to listen")
}
server, err := agent.NewServer(localClient, certs, args.Version())
if err != nil {
log.Fatal().Err(err).Msg("failed to create agent")
} }
server := agent.NewServer(localClient, certs, args.Version())
go cli.StartEvent(args, "swarm", localClient, "") go cli.StartEvent(args, "swarm", localClient, "")
go func() { go func() {
log.Infof("Agent listening on %s", listener.Addr().String()) log.Info().Msgf("Dozzle agent version %s", args.Version())
if err := server.Serve(listener); err != nil { if err := server.Serve(listener); err != nil {
log.Fatalf("failed to serve: %v", err) log.Error().Err(err).Msg("failed to serve")
} }
}() }()
} else { } else {
log.Fatalf("Invalid mode %s", args.Mode) log.Fatal().Str("mode", args.Mode).Msg("Invalid mode")
} }
srv := createServer(args, multiHostService) srv := createServer(args, multiHostService)
go func() { go func() {
log.Infof("Accepting connections on %s", srv.Addr) log.Info().Msgf("Accepting connections on %s", args.Addr)
if err := srv.ListenAndServe(); err != http.ErrServerClosed { if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err) log.Fatal().Err(err).Msg("failed to listen")
} }
}() }()
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
@@ -183,13 +189,13 @@ func main() {
<-ctx.Done() <-ctx.Done()
stop() stop()
log.Info("shutting down gracefully, press Ctrl+C again to force") log.Info().Msg("shutting down gracefully, press Ctrl+C again to force")
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel() defer cancel()
if err := srv.Shutdown(ctx); err != nil { if err := srv.Shutdown(ctx); err != nil {
log.Fatal(err) log.Error().Err(err).Msg("failed to shut down")
} }
log.Debug("shutdown complete") log.Debug().Msg("shut down complete")
} }
func createServer(args cli.Args, multiHostService *docker_support.MultiHostService) *http.Server { func createServer(args cli.Args, multiHostService *docker_support.MultiHostService) *http.Server {
@@ -198,29 +204,29 @@ func createServer(args cli.Args, multiHostService *docker_support.MultiHostServi
var provider web.AuthProvider = web.NONE var provider web.AuthProvider = web.NONE
var authorizer web.Authorizer var authorizer web.Authorizer
if args.AuthProvider == "forward-proxy" { if args.AuthProvider == "forward-proxy" {
log.Debug("Using forward proxy authentication") log.Debug().Msg("Using forward proxy authentication")
provider = web.FORWARD_PROXY provider = web.FORWARD_PROXY
authorizer = auth.NewForwardProxyAuth(args.AuthHeaderUser, args.AuthHeaderEmail, args.AuthHeaderName) authorizer = auth.NewForwardProxyAuth(args.AuthHeaderUser, args.AuthHeaderEmail, args.AuthHeaderName)
} else if args.AuthProvider == "simple" { } else if args.AuthProvider == "simple" {
log.Debug("Using simple authentication") log.Debug().Msg("Using simple authentication")
provider = web.SIMPLE provider = web.SIMPLE
path, err := filepath.Abs("./data/users.yml") path, err := filepath.Abs("./data/users.yml")
if err != nil { if err != nil {
log.Fatalf("Could not find absolute path to users.yml file: %s", err) log.Fatal().Err(err).Msg("Could not get absolute path")
} }
if _, err := os.Stat(path); os.IsNotExist(err) { if _, err := os.Stat(path); os.IsNotExist(err) {
log.Fatalf("Could not find users.yml file at %s", path) log.Fatal().Msg("users.yml file does not exist")
} }
log.Debugf("Reading users from %s", path) log.Debug().Str("path", path).Msg("Reading users.yml file")
db, err := auth.ReadUsersFromFile(path) db, err := auth.ReadUsersFromFile(path)
if err != nil { if err != nil {
log.Fatalf("Could not read users.yml file at %s: %s", path, err) log.Fatal().Err(err).Msg("Could not read users.yml file")
} }
log.Debugf("Read %d users", len(db.Users)) log.Debug().Int("users", len(db.Users)).Msg("Loaded users")
authorizer = auth.NewSimpleAuth(db) authorizer = auth.NewSimpleAuth(db)
} }
@@ -240,25 +246,25 @@ func createServer(args cli.Args, multiHostService *docker_support.MultiHostServi
assets, err := fs.Sub(content, "dist") assets, err := fs.Sub(content, "dist")
if err != nil { if err != nil {
log.Fatalf("Could not open embedded dist folder: %v", err) log.Fatal().Err(err).Msg("Could not get sub filesystem")
} }
if _, ok := os.LookupEnv("LIVE_FS"); ok { if _, ok := os.LookupEnv("LIVE_FS"); ok {
if dev { if dev {
log.Info("Using live filesystem at ./public") log.Info().Msg("Using live filesystem at ./public")
assets = os.DirFS("./public") assets = os.DirFS("./public")
} else { } else {
log.Info("Using live filesystem at ./dist") log.Info().Msg("Using live filesystem at ./dist")
assets = os.DirFS("./dist") assets = os.DirFS("./dist")
} }
} }
if !dev { if !dev {
if _, err := assets.Open(".vite/manifest.json"); err != nil { if _, err := assets.Open(".vite/manifest.json"); err != nil {
log.Fatal(".vite/manifest.json not found") log.Fatal().Msg("manifest.json not found")
} }
if _, err := assets.Open("index.html"); err != nil { if _, err := assets.Open("index.html"); err != nil {
log.Fatal("index.html not found") log.Fatal().Msg("index.html not found")
} }
} }

View File

@@ -15,7 +15,7 @@
"license": "ISC", "license": "ISC",
"author": "Amir Raminfar <findamir@gmail.com>", "author": "Amir Raminfar <findamir@gmail.com>",
"scripts": { "scripts": {
"agent:dev": "DOZZLE_AGENT_ADDR=localhost:7007 reflex -c .reflex.agent", "agent:dev": "DOZZLE_AGENT_ADDR=localhost:7007 DEV=true reflex -c .reflex.agent",
"watch:frontend": "vite --open http://localhost:3100/", "watch:frontend": "vite --open http://localhost:3100/",
"watch:backend": "LIVE_FS=true DEV=true DOZZLE_ADDR=localhost:3100 reflex -c .reflex.server", "watch:backend": "LIVE_FS=true DEV=true DOZZLE_ADDR=localhost:3100 reflex -c .reflex.server",
"dev": "concurrently --kill-others \"npm:watch:*\"", "dev": "concurrently --kill-others \"npm:watch:*\"",