mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-08 00:22:14 +01:00
feat(otel): emit gotenberg.startup span with engine versions
This commit is contained in:
@@ -192,6 +192,9 @@ func Run() {
|
|||||||
if parsedFlags.MustBool("gotenberg-build-debug-data") {
|
if parsedFlags.MustBool("gotenberg-build-debug-data") {
|
||||||
// Build the debug data.
|
// Build the debug data.
|
||||||
gotenberg.BuildDebug(ctx)
|
gotenberg.BuildDebug(ctx)
|
||||||
|
|
||||||
|
// Surface engine versions per trace once modules have reported them.
|
||||||
|
gotenberg.EmitStartupSpan(context.Background())
|
||||||
}
|
}
|
||||||
|
|
||||||
quit := make(chan os.Signal, 1)
|
quit := make(chan os.Signal, 1)
|
||||||
|
|||||||
59
pkg/gotenberg/startup_test.go
Normal file
59
pkg/gotenberg/startup_test.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package gotenberg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDebugModuleVersion(t *testing.T) {
|
||||||
|
info := DebugInfo{
|
||||||
|
ModulesAdditionalData: map[string]map[string]any{
|
||||||
|
"chromium": {"version": "Chromium 145.0"},
|
||||||
|
"broken": {"version": 42},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := debugModuleVersion(info, "chromium"); got != "Chromium 145.0" {
|
||||||
|
t.Errorf("expected chromium version, got %q", got)
|
||||||
|
}
|
||||||
|
if got := debugModuleVersion(info, "missing"); got != "" {
|
||||||
|
t.Errorf("expected empty for a missing module, got %q", got)
|
||||||
|
}
|
||||||
|
if got := debugModuleVersion(info, "broken"); got != "" {
|
||||||
|
t.Errorf("expected empty for a non-string version, got %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmitStartupSpan(t *testing.T) {
|
||||||
|
recorder := newTestSpanRecorder(t)
|
||||||
|
|
||||||
|
debugMu.Lock()
|
||||||
|
previous := debug
|
||||||
|
debug = &DebugInfo{
|
||||||
|
Version: "v8.0.0",
|
||||||
|
ModulesAdditionalData: map[string]map[string]any{
|
||||||
|
"chromium": {"version": "Chromium 145.0"},
|
||||||
|
"libreoffice-api": {"version": "LibreOffice 24.8"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
debugMu.Unlock()
|
||||||
|
t.Cleanup(func() {
|
||||||
|
debugMu.Lock()
|
||||||
|
debug = previous
|
||||||
|
debugMu.Unlock()
|
||||||
|
})
|
||||||
|
|
||||||
|
EmitStartupSpan(context.Background())
|
||||||
|
|
||||||
|
span := findSpan(recorder, "gotenberg.startup")
|
||||||
|
if span == nil {
|
||||||
|
t.Fatal("expected a gotenberg.startup span to be recorded")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := spanAttr(span, "gotenberg.chromium.version"); !ok || v.AsString() != "Chromium 145.0" {
|
||||||
|
t.Errorf("expected gotenberg.chromium.version=Chromium 145.0, got %q (present=%t)", v.AsString(), ok)
|
||||||
|
}
|
||||||
|
if v, ok := spanAttr(span, "gotenberg.libreoffice.version"); !ok || v.AsString() != "LibreOffice 24.8" {
|
||||||
|
t.Errorf("expected gotenberg.libreoffice.version=LibreOffice 24.8, got %q (present=%t)", v.AsString(), ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/hashicorp/go-retryablehttp"
|
"github.com/hashicorp/go-retryablehttp"
|
||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/metric"
|
"go.opentelemetry.io/otel/metric"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
|
||||||
@@ -188,6 +189,46 @@ func Meter() metric.Meter {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EmitStartupSpan records a single gotenberg.startup span carrying static,
|
||||||
|
// process-wide attributes that are only known once modules are provisioned,
|
||||||
|
// such as the chromium and libreoffice binary versions gathered by
|
||||||
|
// [BuildDebug]. It surfaces version data per trace without re-detecting it on
|
||||||
|
// every conversion. The engine versions live here, on a span, rather than on
|
||||||
|
// the resource because the resource is built before modules report them.
|
||||||
|
func EmitStartupSpan(ctx context.Context) {
|
||||||
|
info := Debug()
|
||||||
|
|
||||||
|
var attrs []attribute.KeyValue
|
||||||
|
if v := debugModuleVersion(info, "chromium"); v != "" {
|
||||||
|
attrs = append(attrs, attribute.String("gotenberg.chromium.version", v))
|
||||||
|
}
|
||||||
|
if v := debugModuleVersion(info, "libreoffice-api"); v != "" {
|
||||||
|
attrs = append(attrs, attribute.String("gotenberg.libreoffice.version", v))
|
||||||
|
}
|
||||||
|
|
||||||
|
_, span := Tracer().Start(ctx, "gotenberg.startup",
|
||||||
|
trace.WithSpanKind(trace.SpanKindInternal),
|
||||||
|
trace.WithAttributes(attrs...),
|
||||||
|
)
|
||||||
|
span.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
// debugModuleVersion returns the "version" entry reported by the module with
|
||||||
|
// the given ID, or an empty string when it is missing.
|
||||||
|
func debugModuleVersion(info DebugInfo, moduleID string) string {
|
||||||
|
data, ok := info.ModulesAdditionalData[moduleID]
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
version, ok := data["version"].(string)
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
|
||||||
// LeveledLogger is a wrapper around a [slog.Logger] so that it may be used by a
|
// LeveledLogger is a wrapper around a [slog.Logger] so that it may be used by a
|
||||||
// [retryablehttp.Client].
|
// [retryablehttp.Client].
|
||||||
type LeveledLogger struct {
|
type LeveledLogger struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user