feat(otel): add OpenTelemetry support

This commit is contained in:
Julien Neuhart
2026-03-27 16:28:45 +01:00
parent 08088c15f4
commit 4e9f63004d
86 changed files with 4396 additions and 1283 deletions

View File

@@ -1102,7 +1102,7 @@ Feature: /forms/chromium/convert/html
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_chromium_convert_html"
Then the Gotenberg container should log the following entries:
| "trace":"forms_chromium_convert_html" |
| "correlation_id":"forms_chromium_convert_html" |
@download-from
Scenario: POST /forms/chromium/convert/html (Download From)

View File

@@ -1074,7 +1074,7 @@ Feature: /forms/chromium/convert/markdown
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_chromium_convert_html"
Then the Gotenberg container should log the following entries:
| "trace":"forms_chromium_convert_html" |
| "correlation_id":"forms_chromium_convert_html" |
@download-from
Scenario: POST /forms/chromium/convert/markdown (Download From)

View File

@@ -1169,7 +1169,7 @@ Feature: /forms/chromium/convert/url
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_chromium_convert_url"
Then the Gotenberg container should log the following entries:
| "trace":"forms_chromium_convert_url" |
| "correlation_id":"forms_chromium_convert_url" |
@webhook
Scenario: POST /forms/chromium/convert/url (Webhook)

View File

@@ -25,7 +25,6 @@ Feature: /debug
"libreoffice",
"libreoffice-api",
"libreoffice-pdfengine",
"logging",
"pdfcpu",
"pdfengines",
"pdftk",
@@ -56,8 +55,10 @@ Feature: /debug
"flags": {
"api-bind-ip": "",
"api-body-limit": "",
"api-correlation-id-header": "Gotenberg-Trace",
"api-disable-download-from": "false",
"api-disable-health-check-logging": "false",
"api-disable-health-check-route-telemetry": "false",
"api-download-from-allow-list": "[]",
"api-download-from-deny-list": "[]",
"api-download-from-max-retry": "4",
@@ -96,9 +97,12 @@ Feature: /debug
"libreoffice-max-queue-size": "0",
"libreoffice-restart-after": "10",
"libreoffice-start-timeout": "20s",
"log-enable-gcp-fields": "false",
"log-fields-prefix": "",
"log-format": "auto",
"log-level": "info",
"log-std-enable-gcp-fields": "false",
"log-std-format": "auto",
"pdfengines-convert-engines": "[libreoffice-pdfengine]",
"pdfengines-disable-routes": "false",
"pdfengines-engines": "[]",
@@ -147,7 +151,6 @@ Feature: /debug
"libreoffice",
"libreoffice-api",
"libreoffice-pdfengine",
"logging",
"pdfcpu",
"pdfengines",
"pdftk",
@@ -178,8 +181,10 @@ Feature: /debug
"flags": {
"api-bind-ip": "",
"api-body-limit": "",
"api-correlation-id-header": "Gotenberg-Trace",
"api-disable-download-from": "false",
"api-disable-health-check-logging": "false",
"api-disable-health-check-route-telemetry": "false",
"api-download-from-allow-list": "[]",
"api-download-from-deny-list": "[]",
"api-download-from-max-retry": "4",
@@ -218,9 +223,12 @@ Feature: /debug
"libreoffice-max-queue-size": "0",
"libreoffice-restart-after": "10",
"libreoffice-start-timeout": "20s",
"log-enable-gcp-fields": "false",
"log-fields-prefix": "",
"log-format": "auto",
"log-level": "info",
"log-std-enable-gcp-fields": "false",
"log-std-format": "auto",
"pdfengines-convert-engines": "[libreoffice-pdfengine]",
"pdfengines-disable-routes": "false",
"pdfengines-engines": "[]",
@@ -276,7 +284,7 @@ Feature: /debug
Then the response status code should be 200
Then the response header "Gotenberg-Trace" should be "debug"
Then the Gotenberg container should log the following entries:
| "trace":"debug" |
| "correlation_id":"debug" |
Scenario: GET /debug (Basic Auth)
Given I have a Gotenberg container with the following environment variable(s):

View File

@@ -31,7 +31,7 @@ Feature: /health
Scenario: GET /health (No Logging)
Given I have a Gotenberg container with the following environment variable(s):
| API_DISABLE_HEALTH_CHECK_LOGGING | true |
| API_DISABLE_HEALTH_CHECK_ROUTE_TELEMETRY | true |
When I make a "GET" request to Gotenberg at the "/health" endpoint
Then the response status code should be 200
Then the Gotenberg container should NOT log the following entries:
@@ -44,7 +44,7 @@ Feature: /health
Then the response status code should be 200
Then the response header "Gotenberg-Trace" should be "get_health"
Then the Gotenberg container should log the following entries:
| "trace":"get_health" |
| "correlation_id":"get_health" |
Scenario: GET /health (Basic Auth)
Given I have a Gotenberg container with the following environment variable(s):
@@ -78,11 +78,11 @@ Feature: /health
Then the response status code should be 200
Then the response header "Gotenberg-Trace" should be "head_health"
Then the Gotenberg container should log the following entries:
| "trace":"head_health" |
| "correlation_id":"head_health" |
Scenario: HEAD /health (No Logging)
Given I have a Gotenberg container with the following environment variable(s):
| API_DISABLE_HEALTH_CHECK_LOGGING | true |
| API_DISABLE_HEALTH_CHECK_ROUTE_TELEMETRY | true |
When I make a "HEAD" request to Gotenberg at the "/health" endpoint
Then the response status code should be 200
Then the Gotenberg container should NOT log the following entries:

View File

@@ -716,7 +716,7 @@ Feature: /forms/libreoffice/convert
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_libreoffice_convert"
Then the Gotenberg container should log the following entries:
| "trace":"forms_libreoffice_convert" |
| "correlation_id":"forms_libreoffice_convert" |
@download-from
Scenario: POST /forms/libreoffice/convert (Download From)

View File

@@ -193,7 +193,7 @@ Feature: /forms/pdfengines/bookmarks/{write|read}
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_pdfengines_bookmarks_write"
Then the Gotenberg container should log the following entries:
| "trace":"forms_pdfengines_bookmarks_write" |
| "correlation_id":"forms_pdfengines_bookmarks_write" |
Scenario: POST /forms/pdfengines/bookmarks/read (Gotenberg Trace)
Given I have a default Gotenberg container
@@ -204,7 +204,7 @@ Feature: /forms/pdfengines/bookmarks/{write|read}
Then the response header "Content-Type" should be "application/json"
Then the response header "Gotenberg-Trace" should be "forms_pdfengines_bookmarks_read"
Then the Gotenberg container should log the following entries:
| "trace":"forms_pdfengines_bookmarks_read" |
| "correlation_id":"forms_pdfengines_bookmarks_read" |
@output-filename
Scenario: POST /forms/pdfengines/bookmarks/write (Output Filename - Single PDF)

View File

@@ -115,7 +115,7 @@ Feature: /forms/pdfengines/convert
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_pdfengines_convert"
Then the Gotenberg container should log the following entries:
| "trace":"forms_pdfengines_convert" |
| "correlation_id":"forms_pdfengines_convert" |
@output-filename
Scenario: POST /forms/pdfengines/convert (Output Filename - Single PDF)

View File

@@ -133,7 +133,7 @@ Feature: /forms/pdfengines/encrypt
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_pdfengines_encrypt"
Then the Gotenberg container should log the following entries:
| "trace":"forms_pdfengines_encrypt" |
| "correlation_id":"forms_pdfengines_encrypt" |
@download-from
Scenario: POST /forms/pdfengines/encrypt (Download From)

View File

@@ -49,7 +49,7 @@ Feature: /forms/pdfengines/flatten
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_pdfengines_flatten"
Then the Gotenberg container should log the following entries:
| "trace":"forms_pdfengines_flatten" |
| "correlation_id":"forms_pdfengines_flatten" |
@output-filename
Scenario: POST /forms/pdfengines/flatten (Output Filename - Single PDF)

View File

@@ -549,7 +549,7 @@ Feature: /forms/pdfengines/merge
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_pdfengines_merge"
Then the Gotenberg container should log the following entries:
| "trace":"forms_pdfengines_merge" |
| "correlation_id":"forms_pdfengines_merge" |
@download-from
Scenario: POST /forms/pdfengines/merge (Download From)

View File

@@ -141,7 +141,7 @@ Feature: /forms/pdfengines/metadata/{write|read}
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_pdfengines_metadata_write"
Then the Gotenberg container should log the following entries:
| "trace":"forms_pdfengines_metadata_write" |
| "correlation_id":"forms_pdfengines_metadata_write" |
Scenario: POST /forms/pdfengines/metadata/read (Gotenberg Trace)
Given I have a default Gotenberg container
@@ -152,7 +152,7 @@ Feature: /forms/pdfengines/metadata/{write|read}
Then the response header "Content-Type" should be "application/json"
Then the response header "Gotenberg-Trace" should be "forms_pdfengines_metadata_read"
Then the Gotenberg container should log the following entries:
| "trace":"forms_pdfengines_metadata_read" |
| "correlation_id":"forms_pdfengines_metadata_read" |
@output-filename
Scenario: POST /forms/pdfengines/metadata/write (Output Filename - Single PDF)

View File

@@ -127,7 +127,7 @@ Feature: /forms/pdfengines/rotate
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_pdfengines_rotate"
Then the Gotenberg container should log the following entries:
| "trace":"forms_pdfengines_rotate" |
| "correlation_id":"forms_pdfengines_rotate" |
@webhook
Scenario: POST /forms/pdfengines/rotate (Webhook)

View File

@@ -632,7 +632,7 @@ Feature: /forms/pdfengines/split
Then the response header "Content-Type" should be "application/zip"
Then the response header "Gotenberg-Trace" should be "forms_pdfengines_split"
Then the Gotenberg container should log the following entries:
| "trace":"forms_pdfengines_split" |
| "correlation_id":"forms_pdfengines_split" |
@output-filename
Scenario: POST /forms/pdfengines/split (Output Filename - Single PDF)

View File

@@ -197,7 +197,7 @@ Feature: /forms/pdfengines/stamp
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_pdfengines_stamp"
Then the Gotenberg container should log the following entries:
| "trace":"forms_pdfengines_stamp" |
| "correlation_id":"forms_pdfengines_stamp" |
@webhook
Scenario: POST /forms/pdfengines/stamp (Webhook)

View File

@@ -197,7 +197,7 @@ Feature: /forms/pdfengines/watermark
Then the response header "Content-Type" should be "application/pdf"
Then the response header "Gotenberg-Trace" should be "forms_pdfengines_watermark"
Then the Gotenberg container should log the following entries:
| "trace":"forms_pdfengines_watermark" |
| "correlation_id":"forms_pdfengines_watermark" |
@webhook
Scenario: POST /forms/pdfengines/watermark (Webhook)

View File

@@ -98,7 +98,7 @@ Feature: /prometheus/metrics
Then the response status code should be 200
Then the response header "Gotenberg-Trace" should be "prometheus_metrics"
Then the Gotenberg container should log the following entries:
| "trace":"prometheus_metrics" |
| "correlation_id":"prometheus_metrics" |
Scenario: GET /prometheus/metrics (Basic Auth)
Given I have a Gotenberg container with the following environment variable(s):

View File

@@ -18,7 +18,7 @@ Feature: /
Then the response status code should be 200
Then the response header "Gotenberg-Trace" should be "root"
Then the Gotenberg container should log the following entries:
| "trace":"root" |
| "correlation_id":"root" |
Scenario: GET / (Basic Auth)
Given I have a Gotenberg container with the following environment variable(s):
@@ -46,7 +46,7 @@ Feature: /
Then the response status code should be 204
Then the response header "Gotenberg-Trace" should be "favicon"
Then the Gotenberg container should log the following entries:
| "trace":"favicon" |
| "correlation_id":"favicon" |
Scenario: GET /favicon.ico (Basic Auth)
Given I have a Gotenberg container with the following environment variable(s):

View File

@@ -18,7 +18,7 @@ Feature: /version
Then the response status code should be 200
Then the response header "Gotenberg-Trace" should be "version"
Then the Gotenberg container should log the following entries:
| "trace":"version" |
| "correlation_id":"version" |
Scenario: GET /version (Basic Auth)
Given I have a Gotenberg container with the following environment variable(s):

View File

@@ -525,13 +525,14 @@ func (s *scenario) theResponseStatusCodeShouldBe(expected int) error {
func (s *scenario) theHeaderValueShouldBe(kind, name string, expected string) error {
var actual string
if kind == "response" {
switch {
case kind == "response":
actual = s.resp.Header().Get(name)
} else if s.server == nil {
case s.server == nil:
return errors.New("server not initialized")
} else if s.server.req == nil {
case s.server.req == nil:
return errors.New("no webhook request found")
} else {
default:
actual = s.server.req.Header.Get(name)
}
@@ -543,13 +544,14 @@ func (s *scenario) theHeaderValueShouldBe(kind, name string, expected string) er
func (s *scenario) theCookieValueShouldBe(kind, name, expected string) error {
var cookies []*http.Cookie
if kind == "response" {
switch {
case kind == "response":
cookies = s.resp.Result().Cookies()
} else if s.server == nil {
case s.server == nil:
return errors.New("server not initialized")
} else if s.server.req == nil {
case s.server.req == nil:
return errors.New("no webhook request found")
} else {
default:
cookies = s.server.req.Cookies()
}
@@ -577,13 +579,14 @@ func (s *scenario) theCookieValueShouldBe(kind, name, expected string) error {
func (s *scenario) theBodyShouldMatchString(kind string, expectedDoc *godog.DocString) error {
var actual string
if kind == "response" {
switch {
case kind == "response":
actual = s.resp.Body.String()
} else if s.server == nil {
case s.server == nil:
return errors.New("server not initialized")
} else if s.server.req == nil {
case s.server.req == nil:
return errors.New("no webhook request found")
} else {
default:
actual = string(s.server.bodyCopy)
}
@@ -597,13 +600,14 @@ func (s *scenario) theBodyShouldMatchString(kind string, expectedDoc *godog.DocS
func (s *scenario) theBodyShouldContainString(kind string, expectedDoc *godog.DocString) error {
var actual string
if kind == "response" {
switch {
case kind == "response":
actual = s.resp.Body.String()
} else if s.server == nil {
case s.server == nil:
return errors.New("server not initialized")
} else if s.server.req == nil {
case s.server.req == nil:
return errors.New("no webhook request found")
} else {
default:
actual = string(s.server.bodyCopy)
}
@@ -617,13 +621,14 @@ func (s *scenario) theBodyShouldContainString(kind string, expectedDoc *godog.Do
func (s *scenario) theBodyShouldMatchJSON(kind string, expectedDoc *godog.DocString) error {
var body []byte
if kind == "response" {
switch {
case kind == "response":
body = s.resp.Body.Bytes()
} else if s.server == nil {
case s.server == nil:
return errors.New("server not initialized")
} else if s.server.req == nil {
case s.server.req == nil:
return errors.New("no webhook request found")
} else {
default:
body = s.server.bodyCopy
}