Skip to content

Commit d1ebad3

Browse files
lzapezr-ondrej
authored andcommitted
feat: provide pgx logging wrapper
1 parent 38ad6be commit d1ebad3

File tree

11 files changed

+458
-23
lines changed

11 files changed

+458
-23
lines changed

go.mod

+7
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,18 @@ require (
88
github.com/getsentry/sentry-go v0.31.1
99
github.com/google/go-cmp v0.5.9
1010
github.com/hashicorp/go-retryablehttp v0.7.7
11+
github.com/jackc/pgx/v5 v5.7.2
1112
github.com/labstack/gommon v0.4.2
1213
github.com/lzap/cloudwatchwriter2 v1.4.1
1314
github.com/samber/slog-sentry/v2 v2.9.2
1415
)
1516

17+
require (
18+
github.com/jackc/pgpassfile v1.0.0 // indirect
19+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
20+
golang.org/x/crypto v0.31.0 // indirect
21+
)
22+
1623
require (
1724
github.com/aws/aws-sdk-go-v2 v1.32.7 // indirect
1825
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect

go.sum

+17
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.1 h1:f6jhr4U8osQQrJrzK
1212
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.1/go.mod h1:u8Bi6DG9tLOVIS9MNqtE3vh9T6I/U/8RBpYvy/VyMjc=
1313
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
1414
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
15+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1516
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1617
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1718
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
@@ -28,6 +29,14 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1
2829
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
2930
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
3031
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
32+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
33+
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
34+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
35+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
36+
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
37+
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
38+
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
39+
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
3140
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
3241
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
3342
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -55,6 +64,9 @@ github.com/samber/slog-common v0.18.0 h1:zPeXHM+WhMl2zSx76Rg3EE0jwXdkut9s45K+pwh
5564
github.com/samber/slog-common v0.18.0/go.mod h1:6Krf+hemckfEiRDqy3J/sTpKTJQvmOoFLj9Riz3IkRU=
5665
github.com/samber/slog-sentry/v2 v2.9.2 h1:JW3mQvza3YX+QN1EZ1ZL0ERGgS2uS/3RTZt5zp3hkfk=
5766
github.com/samber/slog-sentry/v2 v2.9.2/go.mod h1:kPT5LvrwBwS0PqbsUcMzJHVeQrJLJd/xJMpYAzI409A=
67+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
68+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
69+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
5870
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
5971
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
6072
github.com/systemd/slog-journal v0.1.0 h1:/SdZsEp21ZzFWS3w6rcmiFCkaVKX3dt8W8mwcbNPdrM=
@@ -65,6 +77,10 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
6577
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
6678
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
6779
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
80+
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
81+
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
82+
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
83+
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
6884
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
6985
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
7086
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
@@ -74,5 +90,6 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
7490
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
7591
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
7692
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
93+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
7794
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
7895
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/example_sinit/go.mod

+4
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ require (
2020
github.com/getsentry/sentry-go v0.31.1 // indirect
2121
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
2222
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
23+
github.com/jackc/pgpassfile v1.0.0 // indirect
24+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
25+
github.com/jackc/pgx/v5 v5.7.2 // indirect
2326
github.com/lzap/cloudwatchwriter2 v1.4.1 // indirect
2427
github.com/pmezard/go-difflib v1.0.0 // indirect
2528
github.com/samber/lo v1.47.0 // indirect
2629
github.com/samber/slog-common v0.18.0 // indirect
2730
github.com/samber/slog-sentry/v2 v2.9.2 // indirect
2831
github.com/stretchr/testify v1.10.0 // indirect
2932
github.com/systemd/slog-journal v0.1.0 // indirect
33+
golang.org/x/crypto v0.31.0 // indirect
3034
golang.org/x/sys v0.29.0 // indirect
3135
golang.org/x/text v0.21.0 // indirect
3236
gopkg.in/yaml.v3 v3.0.1 // indirect

internal/example_sinit/go.sum

+17
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.1 h1:f6jhr4U8osQQrJrzK
1212
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.1/go.mod h1:u8Bi6DG9tLOVIS9MNqtE3vh9T6I/U/8RBpYvy/VyMjc=
1313
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
1414
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
15+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1516
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1617
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1718
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
@@ -28,6 +29,14 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1
2829
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
2930
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
3031
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
32+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
33+
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
34+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
35+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
36+
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
37+
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
38+
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
39+
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
3140
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
3241
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
3342
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -52,18 +61,26 @@ github.com/samber/slog-common v0.18.0 h1:zPeXHM+WhMl2zSx76Rg3EE0jwXdkut9s45K+pwh
5261
github.com/samber/slog-common v0.18.0/go.mod h1:6Krf+hemckfEiRDqy3J/sTpKTJQvmOoFLj9Riz3IkRU=
5362
github.com/samber/slog-sentry/v2 v2.9.2 h1:JW3mQvza3YX+QN1EZ1ZL0ERGgS2uS/3RTZt5zp3hkfk=
5463
github.com/samber/slog-sentry/v2 v2.9.2/go.mod h1:kPT5LvrwBwS0PqbsUcMzJHVeQrJLJd/xJMpYAzI409A=
64+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
65+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
66+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
5567
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
5668
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
5769
github.com/systemd/slog-journal v0.1.0 h1:/SdZsEp21ZzFWS3w6rcmiFCkaVKX3dt8W8mwcbNPdrM=
5870
github.com/systemd/slog-journal v0.1.0/go.mod h1:RroeO1jghpRimGgddhGx+TS/CmRJdPjK9g2ZeYLr9P8=
5971
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
6072
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
73+
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
74+
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
75+
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
76+
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
6177
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
6278
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
6379
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
6480
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
6581
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
6682
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
6783
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
84+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
6885
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
6986
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

pkg/collect/buffer_handler.go

+30
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package collect
22

33
import (
4+
"cmp"
45
"context"
56
"fmt"
67
"log/slog"
78
"runtime"
9+
"sort"
810
"sync"
911
)
1012

@@ -179,6 +181,34 @@ func (h *CollectorHandler) All() []map[string]any {
179181
return h.data.fields
180182
}
181183

184+
// Can be replaced by slices.SortedKeys after Go 1.23+ upgrade
185+
func sortedKeys[K cmp.Ordered, V any](m map[K]V) []K {
186+
keys := make([]K, len(m))
187+
i := 0
188+
for k := range m {
189+
keys[i] = k
190+
i++
191+
}
192+
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
193+
return keys
194+
}
195+
196+
// String returns all records formatted as a string.
197+
func (h *CollectorHandler) String() string {
198+
h.data.mu.RLock()
199+
defer h.data.mu.RUnlock()
200+
201+
var s string
202+
for _, m := range h.data.fields {
203+
var keys []string
204+
for _, k := range sortedKeys(m) {
205+
keys = append(keys, fmt.Sprintf("%s=%v", k, m[k]))
206+
}
207+
s += fmt.Sprintf("%s\n", keys)
208+
}
209+
return s
210+
}
211+
182212
// Reset removes all Entries from this test hook.
183213
func (h *CollectorHandler) Reset() {
184214
h.data.mu.Lock()

pkg/collect/buffer_handler_test.go

+52
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"testing"
77
"testing/slogtest"
88

9+
"github.com/google/go-cmp/cmp"
910
"github.com/osbuild/logging/pkg/collect"
1011
)
1112

@@ -26,3 +27,54 @@ func TestStandardLibraryHelper(t *testing.T) {
2627
func parseLogEntries(_ *testing.T, ms []map[string]any) []map[string]any {
2728
return ms
2829
}
30+
31+
func TestLast(t *testing.T) {
32+
h := collect.NewTestHandler(slog.LevelDebug, false, false, false)
33+
logger := slog.New(h)
34+
35+
tests := []struct {
36+
f func()
37+
want map[string]any
38+
}{
39+
{
40+
f: func() {
41+
logger.Debug("test", "key", "value")
42+
},
43+
want: map[string]any{
44+
"msg": "test",
45+
"key": "value",
46+
},
47+
},
48+
{
49+
f: func() {
50+
logger.Debug("test", slog.Group("g", "key", "value"))
51+
},
52+
want: map[string]any{
53+
"msg": "test",
54+
"g": map[string]any{
55+
"key": "value",
56+
},
57+
},
58+
},
59+
{
60+
f: func() {
61+
logger.WithGroup("g").Debug("test", "key", "value")
62+
},
63+
want: map[string]any{
64+
"msg": "test",
65+
"g": map[string]any{
66+
"key": "value",
67+
},
68+
},
69+
},
70+
}
71+
72+
for _, tt := range tests {
73+
tt.f()
74+
got := h.Last()
75+
76+
if !cmp.Equal(got, tt.want) {
77+
t.Errorf("Got: %v, want: %v", got, tt.want)
78+
}
79+
}
80+
}

pkg/sinit/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,11 @@ export AWS_KEY=xxx
1313
export SENTRY_DSN=xxx
1414
go run github.com/osbuild/logging/internal/example_sinit/
1515
```
16+
17+
### pgx logging
18+
19+
This package provides a function which returns a wrapper that can be used for pgx SQL driver logging:
20+
21+
```go
22+
pgxConfig.Tracer = sinit.PgxTracer(slog.Default())
23+
```

pkg/sinit/pgx.go

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package sinit
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"log/slog"
8+
9+
"github.com/jackc/pgx/v5"
10+
"github.com/osbuild/logging/pkg/strc"
11+
)
12+
13+
// PgxTracer returns a wrapper for PGX logger which logs into the initialized slog.
14+
// Function InitializeLogging must be called first. Usage:
15+
//
16+
// pgxConfig.Tracer = sinit.PgxTracer(slog.Default())
17+
func PgxTracer(logger *slog.Logger) pgx.QueryTracer {
18+
if logger == nil {
19+
panic("called with nil logger")
20+
}
21+
22+
return &dbTracer{logger: logger}
23+
}
24+
25+
// Used for pgx logging with context information
26+
type dbTracer struct {
27+
logger *slog.Logger
28+
}
29+
30+
var _ pgx.QueryTracer = &dbTracer{}
31+
32+
func (dt *dbTracer) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {
33+
if !dt.logger.Enabled(ctx, slog.LevelDebug) {
34+
return ctx
35+
}
36+
37+
sql, args := formatSqlLog(data)
38+
var pid uint32
39+
if conn != nil {
40+
pid = conn.PgConn().PID()
41+
}
42+
43+
tracer := strc.NewTracer(dt.logger)
44+
span, ctx := tracer.Start(ctx, "query",
45+
"conn_id", pid,
46+
"sql", sql,
47+
"args", args,
48+
)
49+
return strc.WithSpan(ctx, span)
50+
}
51+
52+
func (dt *dbTracer) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {
53+
if !dt.logger.Enabled(ctx, slog.LevelDebug) {
54+
return
55+
}
56+
span := strc.SpanFromContext(ctx)
57+
if span == nil {
58+
return
59+
}
60+
61+
if data.Err != nil {
62+
span.End("err", data.Err, "ct", data.CommandTag.String())
63+
} else {
64+
span.End("ct", data.CommandTag.String())
65+
}
66+
}
67+
68+
const maxSqlLogLength = 100
69+
70+
func formatSqlLog(data pgx.TraceQueryStartData) (string, string) {
71+
d := make([]any, len(data.Args))
72+
copy(d, data.Args)
73+
for i, v := range d {
74+
if b, ok := v.([]byte); ok {
75+
d[i] = ellipsis(string(b), maxSqlLogLength)
76+
} else if j, ok := v.(json.RawMessage); ok {
77+
d[i] = ellipsis(string(j), maxSqlLogLength)
78+
}
79+
}
80+
81+
return data.SQL, fmt.Sprintf("%v", d)
82+
}
83+
84+
func ellipsis(s string, maxLen int) string {
85+
runes := []rune(s)
86+
if len(runes) <= maxLen {
87+
return s
88+
}
89+
if maxLen < 3 {
90+
maxLen = 3
91+
}
92+
return string(runes[0:maxLen-3]) + "..."
93+
}

0 commit comments

Comments
 (0)