mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-08 00:22:14 +01:00
feat(chromium): add conversion I/O attributes to chromium.Pdf span
This commit is contained in:
55
pkg/modules/chromium/attrs_test.go
Normal file
55
pkg/modules/chromium/attrs_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package chromium
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gotenberg/gotenberg/v8/pkg/modules/api"
|
||||
)
|
||||
|
||||
func TestConversionInputAttrs(t *testing.T) {
|
||||
tmp := filepath.Join(t.TempDir(), "index.html")
|
||||
content := []byte("<html></html>")
|
||||
if err := os.WriteFile(tmp, content, 0o600); err != nil {
|
||||
t.Fatalf("write temp file: %v", err)
|
||||
}
|
||||
|
||||
t.Run("file URL with non-api context", func(t *testing.T) {
|
||||
got := map[string]int64{}
|
||||
for _, kv := range conversionInputAttrs(context.Background(), "file://"+tmp) {
|
||||
got[string(kv.Key)] = kv.Value.AsInt64()
|
||||
}
|
||||
|
||||
if _, ok := got["gotenberg.conversion.input.files.count"]; ok {
|
||||
t.Error("did not expect files.count for a non-api context")
|
||||
}
|
||||
if got["gotenberg.conversion.input.html.bytes"] != int64(len(content)) {
|
||||
t.Errorf("expected html.bytes=%d, got %d", len(content), got["gotenberg.conversion.input.html.bytes"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("remote URL yields no html.bytes", func(t *testing.T) {
|
||||
for _, kv := range conversionInputAttrs(context.Background(), "https://example.com") {
|
||||
if string(kv.Key) == "gotenberg.conversion.input.html.bytes" {
|
||||
t.Error("did not expect html.bytes for a remote URL")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("api context yields files.count", func(t *testing.T) {
|
||||
var found bool
|
||||
for _, kv := range conversionInputAttrs(&api.Context{}, "https://example.com") {
|
||||
if string(kv.Key) == "gotenberg.conversion.input.files.count" {
|
||||
found = true
|
||||
if kv.Value.AsInt64() != 0 {
|
||||
t.Errorf("expected files.count=0, got %d", kv.Value.AsInt64())
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("expected files.count for an api context")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -802,12 +802,17 @@ func (mod *Chromium) Routes() ([]api.Route, error) {
|
||||
//
|
||||
//nolint:dupl
|
||||
func (mod *Chromium) Pdf(ctx context.Context, logger *slog.Logger, url, outputPath string, options PdfOptions) error {
|
||||
// Read input attributes before Start rebinds ctx to the span context, which
|
||||
// would shadow the underlying [api.Context].
|
||||
inputAttrs := conversionInputAttrs(ctx, url)
|
||||
|
||||
ctx, span := gotenberg.Tracer().Start(ctx, "chromium.Pdf",
|
||||
trace.WithSpanKind(trace.SpanKindClient),
|
||||
trace.WithAttributes(semconv.ServerAddress(mod.args.binPath)),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
span.SetAttributes(inputAttrs...)
|
||||
span.SetAttributes(
|
||||
attribute.Int64("gotenberg.queue.depth_at_arrival", mod.supervisor.ReqQueueSize()),
|
||||
attribute.Int64("gotenberg.conversions_since_last_restart", mod.supervisor.ConversionsSinceRestart()),
|
||||
@@ -863,6 +868,7 @@ func (mod *Chromium) Pdf(ctx context.Context, logger *slog.Logger, url, outputPa
|
||||
if err == nil {
|
||||
if fileInfo, statErr := os.Stat(outputPath); statErr == nil {
|
||||
mod.pdfOutputSizeCounter.Record(ctx, fileInfo.Size())
|
||||
span.SetAttributes(attribute.Int64("gotenberg.conversion.output.bytes", fileInfo.Size()))
|
||||
}
|
||||
|
||||
span.SetStatus(codes.Ok, "")
|
||||
@@ -950,6 +956,26 @@ func (mod *Chromium) Screenshot(ctx context.Context, logger *slog.Logger, url, o
|
||||
return err
|
||||
}
|
||||
|
||||
// conversionInputAttrs derives low-cardinality input attributes for a
|
||||
// conversion span: the number of received files (when ctx is an [api.Context])
|
||||
// and the size of the local HTML input (when url is a file:// URL). Remote URL
|
||||
// conversions yield no html.bytes.
|
||||
func conversionInputAttrs(ctx context.Context, url string) []attribute.KeyValue {
|
||||
var attrs []attribute.KeyValue
|
||||
|
||||
if apiCtx, ok := ctx.(*api.Context); ok {
|
||||
attrs = append(attrs, attribute.Int("gotenberg.conversion.input.files.count", apiCtx.FileCount()))
|
||||
}
|
||||
|
||||
if strings.HasPrefix(url, "file://") {
|
||||
if info, err := os.Stat(strings.TrimPrefix(url, "file://")); err == nil {
|
||||
attrs = append(attrs, attribute.Int64("gotenberg.conversion.input.html.bytes", info.Size()))
|
||||
}
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
// chromiumErrorType maps a conversion error to chromium's bounded reason value,
|
||||
// reused as the span error.type. queueReason preserves the historical,
|
||||
// route-specific label for a saturated queue: Pdf collapses it into
|
||||
|
||||
Reference in New Issue
Block a user