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
|
//nolint:dupl
|
||||||
func (mod *Chromium) Pdf(ctx context.Context, logger *slog.Logger, url, outputPath string, options PdfOptions) error {
|
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",
|
ctx, span := gotenberg.Tracer().Start(ctx, "chromium.Pdf",
|
||||||
trace.WithSpanKind(trace.SpanKindClient),
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
trace.WithAttributes(semconv.ServerAddress(mod.args.binPath)),
|
trace.WithAttributes(semconv.ServerAddress(mod.args.binPath)),
|
||||||
)
|
)
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
|
span.SetAttributes(inputAttrs...)
|
||||||
span.SetAttributes(
|
span.SetAttributes(
|
||||||
attribute.Int64("gotenberg.queue.depth_at_arrival", mod.supervisor.ReqQueueSize()),
|
attribute.Int64("gotenberg.queue.depth_at_arrival", mod.supervisor.ReqQueueSize()),
|
||||||
attribute.Int64("gotenberg.conversions_since_last_restart", mod.supervisor.ConversionsSinceRestart()),
|
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 err == nil {
|
||||||
if fileInfo, statErr := os.Stat(outputPath); statErr == nil {
|
if fileInfo, statErr := os.Stat(outputPath); statErr == nil {
|
||||||
mod.pdfOutputSizeCounter.Record(ctx, fileInfo.Size())
|
mod.pdfOutputSizeCounter.Record(ctx, fileInfo.Size())
|
||||||
|
span.SetAttributes(attribute.Int64("gotenberg.conversion.output.bytes", fileInfo.Size()))
|
||||||
}
|
}
|
||||||
|
|
||||||
span.SetStatus(codes.Ok, "")
|
span.SetStatus(codes.Ok, "")
|
||||||
@@ -950,6 +956,26 @@ func (mod *Chromium) Screenshot(ctx context.Context, logger *slog.Logger, url, o
|
|||||||
return err
|
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,
|
// chromiumErrorType maps a conversion error to chromium's bounded reason value,
|
||||||
// reused as the span error.type. queueReason preserves the historical,
|
// reused as the span error.type. queueReason preserves the historical,
|
||||||
// route-specific label for a saturated queue: Pdf collapses it into
|
// route-specific label for a saturated queue: Pdf collapses it into
|
||||||
|
|||||||
Reference in New Issue
Block a user