mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-07-02 00:17:40 +08:00
refactor(chromium): classify Pdf and Screenshot errors and set span error.type
This commit is contained in:
@@ -799,6 +799,8 @@ func (mod *Chromium) Routes() ([]api.Route, error) {
|
||||
}
|
||||
|
||||
// Pdf converts a URL to PDF.
|
||||
//
|
||||
//nolint:dupl
|
||||
func (mod *Chromium) Pdf(ctx context.Context, logger *slog.Logger, url, outputPath string, options PdfOptions) error {
|
||||
ctx, span := gotenberg.Tracer().Start(ctx, "chromium.Pdf",
|
||||
trace.WithSpanKind(trace.SpanKindClient),
|
||||
@@ -824,21 +826,12 @@ func (mod *Chromium) Pdf(ctx context.Context, logger *slog.Logger, url, outputPa
|
||||
status = "error"
|
||||
}
|
||||
|
||||
reason := "unknown"
|
||||
switch {
|
||||
case errors.Is(err, context.DeadlineExceeded):
|
||||
reason = "timeout"
|
||||
case errors.Is(err, context.Canceled):
|
||||
reason = "context_cancelled"
|
||||
case errors.Is(err, ErrInvalidHttpStatusCode) || errors.Is(err, ErrInvalidResourceHttpStatusCode) || errors.Is(err, ErrLoadingFailed) || errors.Is(err, ErrResourceLoadingFailed) || errors.Is(err, ErrInvalidEvaluationExpression) || errors.Is(err, ErrInvalidSelectorQuery):
|
||||
reason = "invalid_input"
|
||||
case errors.Is(err, gotenberg.ErrMaximumQueueSizeExceeded) || errors.Is(err, gotenberg.ErrProcessAlreadyRestarting):
|
||||
reason = "chromium_unavailable"
|
||||
}
|
||||
reason := chromiumErrorType(err, "chromium_unavailable")
|
||||
|
||||
mod.errsCounter.Add(ctx, 1, metric.WithAttributes(
|
||||
attribute.String("reason", reason),
|
||||
))
|
||||
gotenberg.SpanErrorType(span, reason)
|
||||
}
|
||||
|
||||
if !conversionStart.IsZero() {
|
||||
@@ -876,6 +869,9 @@ func (mod *Chromium) Pdf(ctx context.Context, logger *slog.Logger, url, outputPa
|
||||
return err
|
||||
}
|
||||
|
||||
// Screenshot captures a screenshot from a URL.
|
||||
//
|
||||
//nolint:dupl
|
||||
func (mod *Chromium) Screenshot(ctx context.Context, logger *slog.Logger, url, outputPath string, options ScreenshotOptions) error {
|
||||
ctx, span := gotenberg.Tracer().Start(ctx, "chromium.Screenshot",
|
||||
trace.WithSpanKind(trace.SpanKindClient),
|
||||
@@ -901,23 +897,12 @@ func (mod *Chromium) Screenshot(ctx context.Context, logger *slog.Logger, url, o
|
||||
status = "error"
|
||||
}
|
||||
|
||||
reason := "unknown"
|
||||
switch {
|
||||
case errors.Is(err, context.DeadlineExceeded):
|
||||
reason = "timeout"
|
||||
case errors.Is(err, context.Canceled):
|
||||
reason = "context_cancelled"
|
||||
case errors.Is(err, ErrInvalidHttpStatusCode) || errors.Is(err, ErrInvalidResourceHttpStatusCode) || errors.Is(err, ErrLoadingFailed) || errors.Is(err, ErrResourceLoadingFailed) || errors.Is(err, ErrInvalidEvaluationExpression) || errors.Is(err, ErrInvalidSelectorQuery):
|
||||
reason = "invalid_input"
|
||||
case errors.Is(err, gotenberg.ErrMaximumQueueSizeExceeded):
|
||||
reason = "chromium_maximum_queue_size_exceeded"
|
||||
case errors.Is(err, gotenberg.ErrProcessAlreadyRestarting):
|
||||
reason = "chromium_unavailable"
|
||||
}
|
||||
reason := chromiumErrorType(err, "chromium_maximum_queue_size_exceeded")
|
||||
|
||||
mod.errsCounter.Add(ctx, 1, metric.WithAttributes(
|
||||
attribute.String("reason", reason),
|
||||
))
|
||||
gotenberg.SpanErrorType(span, reason)
|
||||
}
|
||||
|
||||
if !conversionStart.IsZero() {
|
||||
@@ -955,6 +940,30 @@ func (mod *Chromium) Screenshot(ctx context.Context, logger *slog.Logger, url, o
|
||||
return err
|
||||
}
|
||||
|
||||
// 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
|
||||
// "chromium_unavailable", whereas Screenshot keeps
|
||||
// "chromium_maximum_queue_size_exceeded". Generic failures fall back to
|
||||
// [gotenberg.ClassifyError].
|
||||
func chromiumErrorType(err error, queueReason string) string {
|
||||
switch {
|
||||
case errors.Is(err, ErrInvalidHttpStatusCode),
|
||||
errors.Is(err, ErrInvalidResourceHttpStatusCode),
|
||||
errors.Is(err, ErrLoadingFailed),
|
||||
errors.Is(err, ErrResourceLoadingFailed),
|
||||
errors.Is(err, ErrInvalidEvaluationExpression),
|
||||
errors.Is(err, ErrInvalidSelectorQuery):
|
||||
return gotenberg.ErrorTypeInvalidInput
|
||||
case errors.Is(err, gotenberg.ErrMaximumQueueSizeExceeded):
|
||||
return queueReason
|
||||
case errors.Is(err, gotenberg.ErrProcessAlreadyRestarting):
|
||||
return "chromium_unavailable"
|
||||
default:
|
||||
return gotenberg.ClassifyError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Interface guards.
|
||||
var (
|
||||
_ gotenberg.Module = (*Chromium)(nil)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package chromium
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
|
||||
)
|
||||
|
||||
func TestChromiumErrorType(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
err error
|
||||
queueReason string
|
||||
want string
|
||||
}{
|
||||
{"deadline", context.DeadlineExceeded, "chromium_unavailable", "timeout"},
|
||||
{"canceled", context.Canceled, "chromium_unavailable", "context_cancelled"},
|
||||
{"invalid http status", ErrInvalidHttpStatusCode, "chromium_unavailable", "invalid_input"},
|
||||
{"invalid resource http status", ErrInvalidResourceHttpStatusCode, "chromium_unavailable", "invalid_input"},
|
||||
{"loading failed", ErrLoadingFailed, "chromium_unavailable", "invalid_input"},
|
||||
{"resource loading failed", ErrResourceLoadingFailed, "chromium_unavailable", "invalid_input"},
|
||||
{"invalid evaluation expression", ErrInvalidEvaluationExpression, "chromium_unavailable", "invalid_input"},
|
||||
{"invalid selector query", ErrInvalidSelectorQuery, "chromium_unavailable", "invalid_input"},
|
||||
{"pdf queue", gotenberg.ErrMaximumQueueSizeExceeded, "chromium_unavailable", "chromium_unavailable"},
|
||||
{"screenshot queue", gotenberg.ErrMaximumQueueSizeExceeded, "chromium_maximum_queue_size_exceeded", "chromium_maximum_queue_size_exceeded"},
|
||||
{"restarting", gotenberg.ErrProcessAlreadyRestarting, "chromium_maximum_queue_size_exceeded", "chromium_unavailable"},
|
||||
{"unknown", errors.New("boom"), "chromium_unavailable", "unknown"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := chromiumErrorType(tc.err, tc.queueReason); got != tc.want {
|
||||
t.Errorf("chromiumErrorType(%v, %q) = %q, want %q", tc.err, tc.queueReason, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user