Skip to content

Commit 8515529

Browse files
committed
promhttp: Isolate zstd support and klauspost/compress library use to promhttp/zstd package
Signed-off-by: Jordan Liggitt <liggitt@google.com>
1 parent f2276aa commit 8515529

File tree

4 files changed

+110
-14
lines changed

4 files changed

+110
-14
lines changed

prometheus/promhttp/http.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ import (
4141
"sync"
4242
"time"
4343

44-
"github.com/klauspost/compress/zstd"
4544
"github.com/prometheus/common/expfmt"
4645

4746
"github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil"
4847
"github.com/prometheus/client_golang/prometheus"
48+
"github.com/prometheus/client_golang/prometheus/promhttp/internal"
4949
)
5050

5151
const (
@@ -65,7 +65,13 @@ const (
6565
Zstd Compression = "zstd"
6666
)
6767

68-
var defaultCompressionFormats = []Compression{Identity, Gzip, Zstd}
68+
func defaultCompressionFormats() []Compression {
69+
if internal.NewZstdWriter != nil {
70+
return []Compression{Identity, Gzip, Zstd}
71+
} else {
72+
return []Compression{Identity, Gzip}
73+
}
74+
}
6975

7076
var gzipPool = sync.Pool{
7177
New: func() interface{} {
@@ -138,7 +144,7 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO
138144
// Select compression formats to offer based on default or user choice.
139145
var compressions []string
140146
if !opts.DisableCompression {
141-
offers := defaultCompressionFormats
147+
offers := defaultCompressionFormats()
142148
if len(opts.OfferedCompressions) > 0 {
143149
offers = opts.OfferedCompressions
144150
}
@@ -466,14 +472,12 @@ func negotiateEncodingWriter(r *http.Request, rw io.Writer, compressions []strin
466472

467473
switch selected {
468474
case "zstd":
469-
// TODO(mrueg): Replace klauspost/compress with stdlib implementation once https://github.com/golang/go/issues/62513 is implemented.
470-
z, err := zstd.NewWriter(rw, zstd.WithEncoderLevel(zstd.SpeedFastest))
471-
if err != nil {
472-
return nil, "", func() {}, err
475+
if internal.NewZstdWriter == nil {
476+
// The content encoding was not implemented yet.
477+
return nil, "", func() {}, fmt.Errorf("content compression format not recognized: %s. Valid formats are: %s", selected, defaultCompressionFormats())
473478
}
474-
475-
z.Reset(rw)
476-
return z, selected, func() { _ = z.Close() }, nil
479+
writer, closeWriter, err := internal.NewZstdWriter(rw)
480+
return writer, selected, closeWriter, err
477481
case "gzip":
478482
gz := gzipPool.Get().(*gzip.Writer)
479483
gz.Reset(rw)
@@ -483,6 +487,6 @@ func negotiateEncodingWriter(r *http.Request, rw io.Writer, compressions []strin
483487
return rw, selected, func() {}, nil
484488
default:
485489
// The content encoding was not implemented yet.
486-
return nil, "", func() {}, fmt.Errorf("content compression format not recognized: %s. Valid formats are: %s", selected, defaultCompressionFormats)
490+
return nil, "", func() {}, fmt.Errorf("content compression format not recognized: %s. Valid formats are: %s", selected, defaultCompressionFormats())
487491
}
488492
}

prometheus/promhttp/http_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ import (
2626
"testing"
2727
"time"
2828

29-
"github.com/klauspost/compress/zstd"
3029
dto "github.com/prometheus/client_model/go"
3130

3231
"github.com/prometheus/client_golang/prometheus"
32+
"github.com/prometheus/client_golang/prometheus/promhttp/internal"
33+
_ "github.com/prometheus/client_golang/prometheus/promhttp/zstd"
3334
)
3435

3536
type errorCollector struct{}
@@ -90,7 +91,10 @@ func readCompressedBody(r io.Reader, comp Compression) (string, error) {
9091
got, err := io.ReadAll(reader)
9192
return string(got), err
9293
case Zstd:
93-
reader, err := zstd.NewReader(r)
94+
if internal.NewZstdReader == nil {
95+
return "", errors.New("Unsupported compression")
96+
}
97+
reader, err := internal.NewZstdReader(r)
9498
if err != nil {
9599
return "", err
96100
}
@@ -484,7 +488,7 @@ func TestInstrumentMetricHandlerWithCompression(t *testing.T) {
484488
func TestNegotiateEncodingWriter(t *testing.T) {
485489
var defaultCompressions []string
486490

487-
for _, comp := range defaultCompressionFormats {
491+
for _, comp := range defaultCompressionFormats() {
488492
defaultCompressions = append(defaultCompressions, string(comp))
489493
}
490494

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2025 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package internal
15+
16+
import (
17+
"io"
18+
)
19+
20+
// NewZstdWriter enables zstd write support if non-nil.
21+
var NewZstdWriter func(rw io.Writer) (_ io.Writer, closeWriter func(), _ error)
22+
23+
// NewZstdReader enables zstd read support if non-nil.
24+
var NewZstdReader func(r io.Reader) (io.ReadCloser, error)

prometheus/promhttp/zstd/zstd.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2025 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
// Package zstd activates support for zstd compression.
15+
// To enable zstd compression support, import like this:
16+
//
17+
// import (
18+
// _ "github.com/prometheus/client_golang/prometheus/promhttp/zstd"
19+
// )
20+
//
21+
// This support is currently implemented via the github.com/klauspost/compress library,
22+
// so importing this package requires linking and building that library.
23+
// Once stdlib support is added to the Go standard library (https://github.com/golang/go/issues/62513),
24+
// this package is expected to become a no-op, and zstd support will be enabled by default.
25+
package zstd
26+
27+
import (
28+
"io"
29+
30+
"github.com/klauspost/compress/zstd"
31+
"github.com/prometheus/client_golang/prometheus/promhttp/internal"
32+
)
33+
34+
func init() {
35+
// Enable zstd support
36+
internal.NewZstdWriter = func(rw io.Writer) (_ io.Writer, closeWriter func(), _ error) {
37+
// TODO(mrueg): Replace klauspost/compress with stdlib implementation once https://github.com/golang/go/issues/62513 is implemented, and move this package to be a no-op / backfill for older go versions.
38+
z, err := zstd.NewWriter(rw, zstd.WithEncoderLevel(zstd.SpeedFastest))
39+
if err != nil {
40+
return nil, func() {}, err
41+
}
42+
43+
z.Reset(rw)
44+
return z, func() { _ = z.Close() }, nil
45+
}
46+
internal.NewZstdReader = func(r io.Reader) (io.ReadCloser, error) {
47+
d, err := zstd.NewReader(r)
48+
if err != nil {
49+
return nil, err
50+
}
51+
return &zstdReadCloser{Decoder: d}, nil
52+
}
53+
}
54+
55+
// zstdReadCloser adapts zstd to io.ReadCloser
56+
type zstdReadCloser struct {
57+
*zstd.Decoder
58+
}
59+
60+
func (z *zstdReadCloser) Close() error {
61+
// no error returned
62+
z.Decoder.Close()
63+
return nil
64+
}

0 commit comments

Comments
 (0)