mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-25 00:01:55 +00:00
feat: otel REST tracing (#7571)
This commit is contained in:
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT created_by FROM v2_job WHERE id = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "002d68d7c4437522a6dae95af007a356217bbae06b8453f0c32046f0cbf20dcb"
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO otel_traces (\n trace_id, span_id, trace_state, parent_span_id, flags,\n name, kind, start_time_unix_nano, end_time_unix_nano,\n attributes, dropped_attributes_count,\n events, dropped_events_count,\n links, dropped_links_count,\n status\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Bytea",
|
||||
"Bytea",
|
||||
"Text",
|
||||
"Bytea",
|
||||
"Int4",
|
||||
"Text",
|
||||
"Int4",
|
||||
"Int8",
|
||||
"Int8",
|
||||
"Jsonb",
|
||||
"Int4",
|
||||
"Jsonb",
|
||||
"Int4",
|
||||
"Jsonb",
|
||||
"Int4",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5b5cb5339208847bd542f8d903d028803a286e69443a151cd1e3d16da7e8e4f7"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT json_build_object(\n 'trace_id', encode(trace_id, 'hex'), -- BYTEA to hex string\n 'span_id', encode(span_id, 'hex'), -- BYTEA to hex string\n 'parent_span_id', encode(parent_span_id, 'hex'), -- BYTEA to hex string\n 'trace_state', trace_state,\n 'flags', flags,\n 'name', name,\n 'kind', kind,\n 'start_time_unix_nano', start_time_unix_nano,\n 'end_time_unix_nano', end_time_unix_nano,\n 'attributes', attributes,\n 'dropped_attributes_count', dropped_attributes_count,\n 'events', events,\n 'dropped_events_count', dropped_events_count,\n 'links', links,\n 'dropped_links_count', dropped_links_count,\n 'status', status\n ) as \"span!\"\n FROM otel_traces\n WHERE trace_id = $1\n ORDER BY start_time_unix_nano",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "span!",
|
||||
"type_info": "Json"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Bytea"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "90d93fd3bd91e468c1e796e41e31e4f15a825b442346c7386836127bc5723c93"
|
||||
}
|
||||
Generated
+370
-26
@@ -517,8 +517,24 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0"
|
||||
dependencies = [
|
||||
"asn1-rs-derive",
|
||||
"asn1-rs-impl",
|
||||
"asn1-rs-derive 0.4.0",
|
||||
"asn1-rs-impl 0.1.0",
|
||||
"displaydoc",
|
||||
"nom 7.1.3",
|
||||
"num-traits",
|
||||
"rusticata-macros",
|
||||
"thiserror 1.0.69",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048"
|
||||
dependencies = [
|
||||
"asn1-rs-derive 0.5.1",
|
||||
"asn1-rs-impl 0.2.0",
|
||||
"displaydoc",
|
||||
"nom 7.1.3",
|
||||
"num-traits",
|
||||
@@ -539,6 +555,18 @@ dependencies = [
|
||||
"synstructure 0.12.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs-derive"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"synstructure 0.13.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs-impl"
|
||||
version = "0.1.0"
|
||||
@@ -550,6 +578,17 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs-impl"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ast_node"
|
||||
version = "0.9.9"
|
||||
@@ -620,6 +659,17 @@ dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "3.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
|
||||
dependencies = [
|
||||
"event-listener 5.4.1",
|
||||
"event-listener-strategy",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-nats"
|
||||
version = "0.38.0"
|
||||
@@ -1853,6 +1903,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -4148,7 +4199,7 @@ dependencies = [
|
||||
"winapi",
|
||||
"windows-sys 0.59.0",
|
||||
"x25519-dalek",
|
||||
"x509-parser",
|
||||
"x509-parser 0.15.1",
|
||||
"yoke 0.7.5",
|
||||
]
|
||||
|
||||
@@ -4429,11 +4480,11 @@ dependencies = [
|
||||
"hyper-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
"opentelemetry",
|
||||
"opentelemetry 0.27.1",
|
||||
"opentelemetry-http",
|
||||
"opentelemetry-otlp",
|
||||
"opentelemetry-semantic-conventions",
|
||||
"opentelemetry_sdk",
|
||||
"opentelemetry_sdk 0.27.1",
|
||||
"pin-project",
|
||||
"serde",
|
||||
"thiserror 2.0.18",
|
||||
@@ -4671,7 +4722,21 @@ version = "8.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
"asn1-rs 0.5.2",
|
||||
"displaydoc",
|
||||
"nom 7.1.3",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"rusticata-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der-parser"
|
||||
version = "9.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553"
|
||||
dependencies = [
|
||||
"asn1-rs 0.6.2",
|
||||
"displaydoc",
|
||||
"nom 7.1.3",
|
||||
"num-bigint",
|
||||
@@ -4755,13 +4820,33 @@ dependencies = [
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
|
||||
dependencies = [
|
||||
"derive_more-impl 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
"derive_more-impl 2.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6045,6 +6130,21 @@ dependencies = [
|
||||
"seq-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generator"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"log",
|
||||
"rustversion",
|
||||
"windows-link 0.2.1",
|
||||
"windows-result 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
@@ -6764,6 +6864,35 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hudsucker"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bb9d62508d54891fe529dc3a3e169aa7938b89898ba5ab0431ac5bafe66a249"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"bstr",
|
||||
"futures",
|
||||
"http 1.4.0",
|
||||
"http-body-util",
|
||||
"hyper 1.8.1",
|
||||
"hyper-rustls 0.26.0",
|
||||
"hyper-tls",
|
||||
"hyper-tungstenite",
|
||||
"hyper-util",
|
||||
"moka",
|
||||
"rand 0.8.5",
|
||||
"rcgen",
|
||||
"thiserror 1.0.69",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-graceful",
|
||||
"tokio-rustls 0.25.0",
|
||||
"tokio-tungstenite 0.21.0",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.3.0"
|
||||
@@ -6829,10 +6958,13 @@ dependencies = [
|
||||
"http 1.4.0",
|
||||
"hyper 1.8.1",
|
||||
"hyper-rustls 0.27.7",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"pin-project-lite",
|
||||
"rustls-native-certs 0.7.3",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls 0.26.4",
|
||||
"tower-service",
|
||||
]
|
||||
@@ -6868,6 +7000,25 @@ dependencies = [
|
||||
"tokio-rustls 0.24.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 1.4.0",
|
||||
"hyper 1.8.1",
|
||||
"hyper-util",
|
||||
"log",
|
||||
"rustls 0.22.4",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls 0.25.0",
|
||||
"tower-service",
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.7"
|
||||
@@ -6916,6 +7067,21 @@ dependencies = [
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tungstenite"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a343d17fe7885302ed7252767dc7bb83609a874b6ff581142241ec4b73957ad"
|
||||
dependencies = [
|
||||
"http-body-util",
|
||||
"hyper 1.8.1",
|
||||
"hyper-util",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-tungstenite 0.21.0",
|
||||
"tungstenite 0.21.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.19"
|
||||
@@ -7953,6 +8119,22 @@ version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "loom"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"generator",
|
||||
"pin-utils",
|
||||
"scoped-tls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.12.5"
|
||||
@@ -8387,10 +8569,13 @@ version = "0.12.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3dec6bd31b08944e08b58fd99373893a6c17054d6f3ea5006cc894f4f4eee2a"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"equivalent",
|
||||
"event-listener 5.4.1",
|
||||
"futures-util",
|
||||
"parking_lot",
|
||||
"portable-atomic",
|
||||
"smallvec",
|
||||
@@ -9143,7 +9328,16 @@ version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
"asn1-rs 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oid-registry"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9"
|
||||
dependencies = [
|
||||
"asn1-rs 0.6.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9303,13 +9497,27 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry"
|
||||
version = "0.29.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e87237e2775f74896f9ad219d26a2081751187eb7c9f5c58dde20a23b95d16c"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"js-sys",
|
||||
"pin-project-lite",
|
||||
"thiserror 2.0.17",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-appender-tracing"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5feffc321035ad94088a7e5333abb4d84a8726e54a802e736ce9dd7237e85b"
|
||||
dependencies = [
|
||||
"opentelemetry",
|
||||
"opentelemetry 0.27.1",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
@@ -9324,7 +9532,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"http 1.4.0",
|
||||
"opentelemetry",
|
||||
"opentelemetry 0.27.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9336,10 +9544,10 @@ dependencies = [
|
||||
"async-trait",
|
||||
"futures-core",
|
||||
"http 1.4.0",
|
||||
"opentelemetry",
|
||||
"opentelemetry 0.27.1",
|
||||
"opentelemetry-http",
|
||||
"opentelemetry-proto",
|
||||
"opentelemetry_sdk",
|
||||
"opentelemetry-proto 0.27.0",
|
||||
"opentelemetry_sdk 0.27.1",
|
||||
"prost",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
@@ -9355,13 +9563,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6e05acbfada5ec79023c85368af14abd0b307c015e9064d249b2a950ef459a6"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"opentelemetry",
|
||||
"opentelemetry_sdk",
|
||||
"opentelemetry 0.27.1",
|
||||
"opentelemetry_sdk 0.27.1",
|
||||
"prost",
|
||||
"serde",
|
||||
"tonic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-proto"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c40da242381435e18570d5b9d50aca2a4f4f4d8e146231adb4e7768023309b3"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"hex",
|
||||
"opentelemetry 0.29.1",
|
||||
"opentelemetry_sdk 0.29.0",
|
||||
"prost",
|
||||
"serde",
|
||||
"tonic",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-semantic-conventions"
|
||||
version = "0.27.0"
|
||||
@@ -9379,7 +9603,7 @@ dependencies = [
|
||||
"futures-executor",
|
||||
"futures-util",
|
||||
"glob",
|
||||
"opentelemetry",
|
||||
"opentelemetry 0.27.1",
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"serde_json",
|
||||
@@ -9389,6 +9613,23 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry_sdk"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afdefb21d1d47394abc1ba6c57363ab141be19e27cc70d0e422b7f303e4d290b"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-executor",
|
||||
"futures-util",
|
||||
"glob",
|
||||
"opentelemetry 0.29.1",
|
||||
"percent-encoding",
|
||||
"rand 0.9.0",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
@@ -10600,6 +10841,20 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rcgen"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2"
|
||||
dependencies = [
|
||||
"pem 3.0.5",
|
||||
"ring 0.17.14",
|
||||
"rustls-pki-types",
|
||||
"time",
|
||||
"x509-parser 0.16.0",
|
||||
"yasna",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rdkafka"
|
||||
version = "0.36.2"
|
||||
@@ -13780,6 +14035,19 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-graceful"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "627ba4daa4cbce14740603401c895e72d47ecd86690a18e3f0841266e9340de7"
|
||||
dependencies = [
|
||||
"loom",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.5.0"
|
||||
@@ -13928,6 +14196,24 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"native-tls",
|
||||
"rustls 0.22.4",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls 0.25.0",
|
||||
"tungstenite 0.21.0",
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.24.0"
|
||||
@@ -13939,7 +14225,7 @@ dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tungstenite",
|
||||
"tungstenite 0.24.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -14245,8 +14531,8 @@ checksum = "97a971f6058498b5c0f1affa23e7ea202057a7301dbff68e968b2d578bcbd053"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"opentelemetry",
|
||||
"opentelemetry_sdk",
|
||||
"opentelemetry 0.27.1",
|
||||
"opentelemetry_sdk 0.27.1",
|
||||
"smallvec",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
@@ -14360,6 +14646,28 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http 1.4.0",
|
||||
"httparse",
|
||||
"log",
|
||||
"native-tls",
|
||||
"rand 0.8.5",
|
||||
"rustls 0.22.4",
|
||||
"rustls-pki-types",
|
||||
"sha1",
|
||||
"thiserror 1.0.69",
|
||||
"url",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.24.0"
|
||||
@@ -15265,6 +15573,7 @@ dependencies = [
|
||||
"memchr",
|
||||
"object_store",
|
||||
"once_cell",
|
||||
"opentelemetry-proto 0.29.0",
|
||||
"pep440_rs",
|
||||
"prometheus",
|
||||
"quote",
|
||||
@@ -15401,7 +15710,7 @@ dependencies = [
|
||||
"tokio-postgres 0.7.11",
|
||||
"tokio-postgres 0.7.13",
|
||||
"tokio-stream",
|
||||
"tokio-tungstenite",
|
||||
"tokio-tungstenite 0.24.0",
|
||||
"tokio-util",
|
||||
"tonic",
|
||||
"tower 0.5.3",
|
||||
@@ -15515,11 +15824,11 @@ dependencies = [
|
||||
"object_store",
|
||||
"once_cell",
|
||||
"openidconnect",
|
||||
"opentelemetry",
|
||||
"opentelemetry 0.27.1",
|
||||
"opentelemetry-appender-tracing",
|
||||
"opentelemetry-otlp",
|
||||
"opentelemetry-semantic-conventions",
|
||||
"opentelemetry_sdk",
|
||||
"opentelemetry_sdk 0.27.1",
|
||||
"pep440_rs",
|
||||
"phf 0.11.3",
|
||||
"pin-project-lite",
|
||||
@@ -15949,6 +16258,7 @@ dependencies = [
|
||||
"deno_url",
|
||||
"deno_web",
|
||||
"deno_webidl",
|
||||
"derive_more 1.0.0",
|
||||
"dotenv",
|
||||
"dyn-iter",
|
||||
"eventsource-stream",
|
||||
@@ -15957,6 +16267,10 @@ dependencies = [
|
||||
"gcp_auth",
|
||||
"git-version",
|
||||
"hex",
|
||||
"hudsucker",
|
||||
"hyper-http-proxy",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
"itertools 0.14.0",
|
||||
"jsonwebtoken 8.3.0",
|
||||
"lazy_static",
|
||||
@@ -15969,7 +16283,8 @@ dependencies = [
|
||||
"nix 0.27.1",
|
||||
"object_store",
|
||||
"once_cell",
|
||||
"opentelemetry",
|
||||
"opentelemetry 0.27.1",
|
||||
"opentelemetry-proto 0.29.0",
|
||||
"oracle",
|
||||
"pem 3.0.5",
|
||||
"pep440_rs",
|
||||
@@ -15977,6 +16292,7 @@ dependencies = [
|
||||
"process-wrap",
|
||||
"prometheus",
|
||||
"rand 0.9.0",
|
||||
"rcgen",
|
||||
"regex",
|
||||
"reqwest 0.13.1",
|
||||
"reqwest-middleware",
|
||||
@@ -15992,6 +16308,7 @@ dependencies = [
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"tracing-opentelemetry",
|
||||
"ulid",
|
||||
"url",
|
||||
"urlencoding",
|
||||
@@ -16673,12 +16990,30 @@ version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
"asn1-rs 0.5.2",
|
||||
"data-encoding",
|
||||
"der-parser",
|
||||
"der-parser 8.2.0",
|
||||
"lazy_static",
|
||||
"nom 7.1.3",
|
||||
"oid-registry",
|
||||
"oid-registry 0.6.1",
|
||||
"rusticata-macros",
|
||||
"thiserror 1.0.69",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x509-parser"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69"
|
||||
dependencies = [
|
||||
"asn1-rs 0.6.2",
|
||||
"data-encoding",
|
||||
"der-parser 9.0.0",
|
||||
"lazy_static",
|
||||
"nom 7.1.3",
|
||||
"oid-registry 0.7.1",
|
||||
"ring 0.17.14",
|
||||
"rusticata-macros",
|
||||
"thiserror 1.0.69",
|
||||
"time",
|
||||
@@ -16730,6 +17065,15 @@ version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
||||
[[package]]
|
||||
name = "yasna"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
|
||||
dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.5"
|
||||
|
||||
@@ -161,6 +161,7 @@ k8s-openapi.workspace = true
|
||||
libloading.workspace = true
|
||||
bitflags.workspace = true
|
||||
globset.workspace = true
|
||||
opentelemetry-proto.workspace = true
|
||||
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
@@ -219,6 +220,8 @@ memchr = "2.7.4"
|
||||
axum = { version = "^0.7", features = ["multipart", "macros"] }
|
||||
headers = "^0"
|
||||
hyper = { version = "^1", features = ["full"] }
|
||||
hyper-tls = "^0.6"
|
||||
hyper-util = { version = "^0.1", features = ["client-legacy", "http1", "tokio"] }
|
||||
tokio = { version = "=1.46.1", features = ["full", "tracing", "time"] }
|
||||
tokio-stream = { version = "0.1.17" }
|
||||
tower = "^0"
|
||||
@@ -233,6 +236,7 @@ thiserror = "^2"
|
||||
anyhow = "^1"
|
||||
chrono = { version = "^0.4", features = ["serde"] }
|
||||
chrono-tz = "^0.10.1"
|
||||
derive_more = { version = "1", features = ["deref", "deref_mut"], default-features = false }
|
||||
tracing = "^0"
|
||||
tracing-subscriber = { version = "^0", features = ["env-filter", "json"] }
|
||||
tracing-appender = "^0"
|
||||
@@ -415,6 +419,7 @@ opentelemetry_sdk = { version = "0.27.1", features = ["rt-tokio"] }
|
||||
opentelemetry-otlp = { version = "0.27.0", features = ["grpc-tonic", "tls"] }
|
||||
opentelemetry-appender-tracing = "0.27.0"
|
||||
opentelemetry-semantic-conventions = { version = "0.27.0", features = ["semconv_experimental"] }
|
||||
opentelemetry-proto = { version = "0.29.0", features = ["with-serde", "gen-tonic"] }
|
||||
|
||||
bollard = "0.18.1"
|
||||
|
||||
@@ -456,3 +461,6 @@ oracle = { version = "0.6.3", features = ["chrono"] }
|
||||
rumqttc = { version = "0.24.0", features = ["use-native-tls"]}
|
||||
strum = { version = "0.27", features = ["derive"] }
|
||||
strum_macros = "^0"
|
||||
hudsucker = { version = "0.22", features = ["rcgen-ca", "native-tls-client"] }
|
||||
hyper-http-proxy = { version = "1", default-features = false, features = ["native-tls"] }
|
||||
rcgen = "0.13"
|
||||
|
||||
@@ -1 +1 @@
|
||||
62cc5aaf46a4f825d9b0cf446924c07eeb95b8d5
|
||||
0056bec3ccc0d7a80461a2d036c555364d30b0d6
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
DROP INDEX IF EXISTS otel_traces_time_idx;
|
||||
DROP INDEX IF EXISTS otel_traces_trace_time_idx;
|
||||
DROP TABLE IF EXISTS otel_traces;
|
||||
@@ -0,0 +1,36 @@
|
||||
-- OpenTelemetry Span storage (all fields from proto::Span).
|
||||
-- See: https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/trace/v1/trace.proto
|
||||
|
||||
CREATE TABLE IF NOT EXISTS otel_traces (
|
||||
-- Identity fields (BYTEA for efficient storage and querying)
|
||||
trace_id BYTEA NOT NULL, -- 16 bytes (proto: bytes)
|
||||
span_id BYTEA NOT NULL, -- 8 bytes (proto: bytes)
|
||||
trace_state TEXT NOT NULL DEFAULT '', -- W3C trace-context (proto: string)
|
||||
parent_span_id BYTEA NOT NULL DEFAULT '', -- 8 bytes, empty if root span (proto: bytes)
|
||||
flags INTEGER NOT NULL DEFAULT 0, -- W3C trace flags (proto: fixed32)
|
||||
-- Core fields
|
||||
name TEXT NOT NULL, -- operation name (proto: string)
|
||||
kind INTEGER NOT NULL, -- SpanKind enum (proto: int32)
|
||||
start_time_unix_nano BIGINT NOT NULL, -- (proto: fixed64, postgres has no u64)
|
||||
end_time_unix_nano BIGINT NOT NULL, -- (proto: fixed64, postgres has no u64)
|
||||
-- Attributes
|
||||
attributes JSONB NOT NULL DEFAULT '[]', -- (proto: repeated KeyValue)
|
||||
dropped_attributes_count INTEGER NOT NULL DEFAULT 0, -- (proto: uint32)
|
||||
-- Events
|
||||
events JSONB NOT NULL DEFAULT '[]', -- (proto: repeated Event)
|
||||
dropped_events_count INTEGER NOT NULL DEFAULT 0, -- (proto: uint32)
|
||||
-- Links
|
||||
links JSONB NOT NULL DEFAULT '[]', -- (proto: repeated Link)
|
||||
dropped_links_count INTEGER NOT NULL DEFAULT 0, -- (proto: uint32)
|
||||
-- Status
|
||||
status JSONB, -- (proto: optional Status message)
|
||||
PRIMARY KEY (trace_id, span_id)
|
||||
);
|
||||
|
||||
-- Query spans by trace_id, ordered by time
|
||||
CREATE INDEX IF NOT EXISTS otel_traces_trace_time_idx ON otel_traces (trace_id, start_time_unix_nano);
|
||||
|
||||
-- Time-based cleanup (retention policy)
|
||||
CREATE INDEX IF NOT EXISTS otel_traces_time_idx ON otel_traces (start_time_unix_nano);
|
||||
|
||||
-- trace_id = job_id.as_bytes()
|
||||
+53
-4
@@ -44,7 +44,7 @@ use windmill_common::{
|
||||
EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING,
|
||||
HUB_API_SECRET_SETTING, HUB_BASE_URL_SETTING, INDEXER_SETTING,
|
||||
INSTANCE_PYTHON_VERSION_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING,
|
||||
KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, MAVEN_REPOS_SETTING,
|
||||
KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, MAVEN_REPOS_SETTING, OTEL_TRACING_PROXY_SETTING,
|
||||
MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NO_DEFAULT_MAVEN_SETTING,
|
||||
NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, OAUTH_SETTING, OTEL_SETTING,
|
||||
PIP_INDEX_URL_SETTING, POWERSHELL_REPO_PAT_SETTING, POWERSHELL_REPO_URL_SETTING,
|
||||
@@ -99,9 +99,9 @@ use crate::monitor::{
|
||||
reload_bunfig_install_scopes_setting, reload_critical_alert_mute_ui_setting,
|
||||
reload_critical_error_channels_setting, reload_extra_pip_index_url_setting,
|
||||
reload_hub_api_secret_setting, reload_hub_base_url_setting, reload_job_default_timeout_setting,
|
||||
reload_jwt_secret_setting, reload_license_key, reload_npm_config_registry_setting,
|
||||
reload_pip_index_url_setting, reload_retention_period_setting, reload_scim_token_setting,
|
||||
reload_smtp_config, reload_worker_config, MonitorIteration,
|
||||
reload_jwt_secret_setting, reload_license_key, reload_otel_tracing_proxy_setting,
|
||||
reload_npm_config_registry_setting, reload_pip_index_url_setting, reload_retention_period_setting,
|
||||
reload_scim_token_setting, reload_smtp_config, reload_worker_config, MonitorIteration,
|
||||
};
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
@@ -452,6 +452,7 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
.unwrap_or(DEFAULT_NUM_WORKERS as i32)
|
||||
};
|
||||
|
||||
// TODO: maybe gate behind debug_assertions?
|
||||
if num_workers > 1 && !std::env::var("WORKER_GROUP").is_ok_and(|x| x == "native") {
|
||||
println!(
|
||||
"We STRONGLY recommend using at most 1 worker per container, use at your own risks"
|
||||
@@ -802,6 +803,10 @@ Windmill Community Edition {GIT_VERSION}
|
||||
#[cfg(not(all(feature = "tantivy", feature = "parquet")))]
|
||||
let log_indexer_f = async { Ok(()) as anyhow::Result<()> };
|
||||
|
||||
// Resubscribe for OTEL tracing proxy before workers_f captures killpill_rx
|
||||
#[cfg(all(feature = "private", feature = "enterprise"))]
|
||||
let otel_killpill_rx = killpill_rx.resubscribe();
|
||||
|
||||
let server_f = async {
|
||||
if !is_agent {
|
||||
if let Some(db) = conn.as_sql() {
|
||||
@@ -1156,6 +1161,13 @@ Windmill Community Edition {GIT_VERSION}
|
||||
KEEP_JOB_DIR_SETTING => {
|
||||
load_keep_job_dir(&conn).await;
|
||||
},
|
||||
OTEL_TRACING_PROXY_SETTING => {
|
||||
reload_otel_tracing_proxy_setting(&conn).await;
|
||||
if worker_mode {
|
||||
tracing::info!("OTEL tracing proxy setting changed, restarting worker");
|
||||
send_delayed_killpill(&tx, 4, "OTEL tracing proxy setting change").await;
|
||||
}
|
||||
},
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING => {
|
||||
load_require_preexisting_user(&db).await;
|
||||
},
|
||||
@@ -1364,6 +1376,42 @@ Windmill Community Edition {GIT_VERSION}
|
||||
Ok(()) as anyhow::Result<()>
|
||||
};
|
||||
|
||||
let otel_tracing_proxy_f = async {
|
||||
#[cfg(all(feature = "private", feature = "enterprise"))]
|
||||
{
|
||||
// Start OTEL tracing proxy for HTTP request interception
|
||||
// Only enabled when: setting is on, worker mode (not server), and single worker (to avoid race conditions)
|
||||
if worker_mode
|
||||
&& num_workers == 1
|
||||
&& windmill_worker::OTEL_TRACING_PROXY_SETTINGS
|
||||
.read()
|
||||
.await
|
||||
.enabled
|
||||
{
|
||||
if let Some(db) = conn.as_sql() {
|
||||
tracing::info!(
|
||||
"Starting OTEL tracing proxy (port will be dynamically assigned)"
|
||||
);
|
||||
if let Err(e) =
|
||||
windmill_worker::start_otel_tracing_proxy(db.clone(), otel_killpill_rx)
|
||||
.await
|
||||
{
|
||||
tracing::error!("OTEL tracing proxy error: {}", e);
|
||||
}
|
||||
}
|
||||
} else if windmill_worker::OTEL_TRACING_PROXY_SETTINGS
|
||||
.read()
|
||||
.await
|
||||
.enabled
|
||||
&& num_workers > 1
|
||||
{
|
||||
tracing::warn!("OTEL tracing proxy is enabled but num_workers > 1. Disabling to avoid race conditions. Set NUM_WORKERS=1 to enable.");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(()) as anyhow::Result<()>
|
||||
};
|
||||
|
||||
if server_mode {
|
||||
if let Some(db) = conn.as_sql() {
|
||||
schedule_stats(&db, &HTTP_CLIENT).await;
|
||||
@@ -1378,6 +1426,7 @@ Windmill Community Edition {GIT_VERSION}
|
||||
monitor_f,
|
||||
server_f,
|
||||
metrics_f,
|
||||
otel_tracing_proxy_f,
|
||||
indexer_f,
|
||||
log_indexer_f
|
||||
)?;
|
||||
|
||||
+35
-5
@@ -53,7 +53,7 @@ use windmill_common::{
|
||||
HUB_API_SECRET_SETTING, HUB_BASE_URL_SETTING, INSTANCE_PYTHON_VERSION_SETTING,
|
||||
JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING,
|
||||
LICENSE_KEY_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NPM_CONFIG_REGISTRY_SETTING,
|
||||
NUGET_CONFIG_SETTING, OTEL_SETTING, PIP_INDEX_URL_SETTING, POWERSHELL_REPO_PAT_SETTING,
|
||||
OTEL_TRACING_PROXY_SETTING, NUGET_CONFIG_SETTING, OTEL_SETTING, PIP_INDEX_URL_SETTING, POWERSHELL_REPO_PAT_SETTING,
|
||||
POWERSHELL_REPO_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, TIMEOUT_WAIT_RESULT_SETTING,
|
||||
@@ -81,10 +81,10 @@ use windmill_common::{
|
||||
use windmill_common::{client::AuthedClient, global_settings::APP_WORKSPACED_ROUTE_SETTING};
|
||||
use windmill_queue::{cancel_job, get_queued_job_v2, SameWorkerPayload};
|
||||
use windmill_worker::{
|
||||
handle_job_error, JobCompletedSender, SameWorkerSender, BUNFIG_INSTALL_SCOPES,
|
||||
INSTANCE_PYTHON_VERSION, JOB_DEFAULT_TIMEOUT, KEEP_JOB_DIR, MAVEN_REPOS, NO_DEFAULT_MAVEN,
|
||||
NPM_CONFIG_REGISTRY, NUGET_CONFIG, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL, POWERSHELL_REPO_PAT,
|
||||
POWERSHELL_REPO_URL,
|
||||
handle_job_error, JobCompletedSender, OtelTracingProxySettings, SameWorkerSender, BUNFIG_INSTALL_SCOPES,
|
||||
INSTANCE_PYTHON_VERSION, JOB_DEFAULT_TIMEOUT, KEEP_JOB_DIR, MAVEN_REPOS,
|
||||
OTEL_TRACING_PROXY_SETTINGS, NO_DEFAULT_MAVEN, NPM_CONFIG_REGISTRY, NUGET_CONFIG, PIP_EXTRA_INDEX_URL,
|
||||
PIP_INDEX_URL, POWERSHELL_REPO_PAT, POWERSHELL_REPO_URL,
|
||||
};
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
@@ -320,6 +320,7 @@ pub async fn initial_load(
|
||||
reload_maven_repos_setting(&conn).await;
|
||||
reload_no_default_maven_setting(&conn).await;
|
||||
reload_ruby_repos_setting(&conn).await;
|
||||
reload_otel_tracing_proxy_setting(&conn).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -778,6 +779,35 @@ pub async fn load_keep_job_dir(conn: &Connection) {
|
||||
};
|
||||
}
|
||||
|
||||
pub async fn reload_otel_tracing_proxy_setting(conn: &Connection) {
|
||||
match load_value_from_global_settings_with_conn(conn, OTEL_TRACING_PROXY_SETTING, true).await {
|
||||
Ok(Some(settings)) => {
|
||||
match serde_json::from_value::<OtelTracingProxySettings>(settings) {
|
||||
Ok(new_settings) => {
|
||||
let mut current = OTEL_TRACING_PROXY_SETTINGS.write().await;
|
||||
if current.enabled != new_settings.enabled
|
||||
|| current.enabled_languages != new_settings.enabled_languages
|
||||
{
|
||||
tracing::info!(
|
||||
"OTEL tracing proxy settings changed: enabled={}, languages={:?}",
|
||||
new_settings.enabled,
|
||||
new_settings.enabled_languages
|
||||
);
|
||||
*current = new_settings;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Error parsing OTEL tracing proxy settings: {e:#}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Error loading OTEL tracing proxy setting: {e:#}");
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
||||
pub async fn load_require_preexisting_user(db: &DB) {
|
||||
let value =
|
||||
load_value_from_global_settings(db, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING).await;
|
||||
|
||||
@@ -9344,6 +9344,25 @@ paths:
|
||||
items:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/jobs/get_otel_traces/{id}:
|
||||
get:
|
||||
summary: get OpenTelemetry traces for a job
|
||||
operationId: getJobOtelTraces
|
||||
tags:
|
||||
- job
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/JobId"
|
||||
responses:
|
||||
"200":
|
||||
description: list of OTEL Span objects (compatible with OpenTelemetry Span proto)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
|
||||
/w/{workspace}/trigger/{trigger_kind}/resume_suspended_trigger_jobs/{trigger_path}:
|
||||
post:
|
||||
summary: resume all suspended jobs for a specific trigger
|
||||
@@ -17564,6 +17583,7 @@ components:
|
||||
- visible_to_owner
|
||||
- tag
|
||||
|
||||
|
||||
ExportableCompletedJob:
|
||||
type: object
|
||||
description: Completed job with full data for export/import operations
|
||||
|
||||
@@ -341,6 +341,7 @@ pub fn workspaced_service() -> Router {
|
||||
"/send_email_with_instance_smtp",
|
||||
post(send_email_with_instance_smtp),
|
||||
)
|
||||
.route("/get_otel_traces/:id", get(get_otel_traces))
|
||||
}
|
||||
|
||||
pub fn workspace_unauthed_service() -> Router {
|
||||
@@ -8835,3 +8836,63 @@ async fn delete_completed_job<'a>(
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn get_otel_traces(
|
||||
OptAuthed(opt_authed): OptAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, id)): Path<(String, Uuid)>,
|
||||
) -> error::Result<Json<Vec<serde_json::Value>>> {
|
||||
// Check job exists and user has permission to view it
|
||||
let job = sqlx::query_scalar!(
|
||||
"SELECT created_by FROM v2_job WHERE id = $1 AND workspace_id = $2",
|
||||
id,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(&db)
|
||||
.await?;
|
||||
|
||||
match job {
|
||||
Some(created_by) => {
|
||||
if opt_authed.is_none() && created_by != "anonymous" {
|
||||
return Err(Error::BadRequest(
|
||||
"As a non logged in user, you can only see jobs ran by anonymous users"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(Error::NotFound(format!("Job {} not found", id)));
|
||||
}
|
||||
}
|
||||
|
||||
let trace_id = id.as_bytes().as_slice();
|
||||
|
||||
let traces = sqlx::query_scalar!(
|
||||
r#"SELECT json_build_object(
|
||||
'trace_id', encode(trace_id, 'hex'), -- BYTEA to hex string
|
||||
'span_id', encode(span_id, 'hex'), -- BYTEA to hex string
|
||||
'parent_span_id', encode(parent_span_id, 'hex'), -- BYTEA to hex string
|
||||
'trace_state', trace_state,
|
||||
'flags', flags,
|
||||
'name', name,
|
||||
'kind', kind,
|
||||
'start_time_unix_nano', start_time_unix_nano,
|
||||
'end_time_unix_nano', end_time_unix_nano,
|
||||
'attributes', attributes,
|
||||
'dropped_attributes_count', dropped_attributes_count,
|
||||
'events', events,
|
||||
'dropped_events_count', dropped_events_count,
|
||||
'links', links,
|
||||
'dropped_links_count', dropped_links_count,
|
||||
'status', status
|
||||
) as "span!"
|
||||
FROM otel_traces
|
||||
WHERE trace_id = $1
|
||||
ORDER BY start_time_unix_nano"#,
|
||||
trace_id
|
||||
)
|
||||
.fetch_all(&db)
|
||||
.await?;
|
||||
|
||||
Ok(Json(traces))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ benchmark = []
|
||||
parquet = ["dep:object_store", "dep:aws-sdk-sts", "dep:aws-smithy-types-convert", "dep:datafusion"]
|
||||
aws_auth = ["dep:aws-sdk-sts"]
|
||||
otel = ["dep:opentelemetry-semantic-conventions", "dep:opentelemetry-otlp", "dep:opentelemetry_sdk",
|
||||
"dep:opentelemetry", "dep:tracing-opentelemetry", "dep:opentelemetry-appender-tracing", "dep:tonic"]
|
||||
"dep:tracing-opentelemetry", "dep:opentelemetry-appender-tracing", "dep:tonic", "dep:opentelemetry"]
|
||||
smtp = ["dep:mail-send"]
|
||||
scoped_cache = []
|
||||
cloud = []
|
||||
|
||||
@@ -46,6 +46,7 @@ pub const DEV_INSTANCE_SETTING: &str = "dev_instance";
|
||||
pub const JWT_SECRET_SETTING: &str = "jwt_secret";
|
||||
pub const EMAIL_DOMAIN_SETTING: &str = "email_domain";
|
||||
pub const OTEL_SETTING: &str = "otel";
|
||||
pub const OTEL_TRACING_PROXY_SETTING: &str = "otel_tracing_proxy";
|
||||
pub const APP_WORKSPACED_ROUTE_SETTING: &str = "app_workspaced_route";
|
||||
pub const SECRET_BACKEND_SETTING: &str = "secret_backend";
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ sqlx = []
|
||||
deno_core = ["dep:deno_fetch", "dep:deno_webidl", "dep:deno_web", "dep:deno_net", "dep:deno_console", "dep:deno_url", "dep:deno_core",
|
||||
"dep:deno_ast", "dep:deno_tls", "dep:deno_permissions", "dep:deno_io", "dep:deno_runtime", "dep:deno_telemetry", "dep:deno_error", "dep:winapi"]
|
||||
libffi_mac = ["dep:libffi-sys"]
|
||||
otel = ["windmill-common/otel", "dep:opentelemetry"]
|
||||
otel = ["windmill-common/otel", "dep:opentelemetry", "dep:tracing-opentelemetry"]
|
||||
dind = ["dep:bollard"]
|
||||
php = ["dep:windmill-parser-php"]
|
||||
mysql = ["dep:mysql_async"]
|
||||
@@ -76,6 +76,7 @@ futures.workspace = true
|
||||
async-recursion.workspace = true
|
||||
async-trait.workspace = true
|
||||
anyhow.workspace = true
|
||||
derive_more.workspace = true
|
||||
itertools.workspace = true
|
||||
regex.workspace = true
|
||||
prometheus = { workspace = true, optional = true }
|
||||
@@ -136,9 +137,16 @@ process-wrap.workspace = true
|
||||
async-once-cell.workspace = true
|
||||
libloading = { workspace = true, optional = true }
|
||||
|
||||
opentelemetry-proto.workspace = true
|
||||
opentelemetry = { workspace = true, optional = true }
|
||||
tracing-opentelemetry = { workspace = true, optional = true }
|
||||
bollard = { workspace = true, optional = true }
|
||||
oracle = { workspace = true, optional = true }
|
||||
hudsucker.workspace = true
|
||||
hyper-http-proxy.workspace = true
|
||||
hyper-tls.workspace = true
|
||||
hyper-util.workspace = true
|
||||
rcgen.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
deno_fetch = { workspace = true, optional = true }
|
||||
|
||||
@@ -100,3 +100,12 @@ exec_bin {
|
||||
arg: "/download_deps.sh"
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
dst: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
#{DEV}
|
||||
|
||||
|
||||
@@ -83,4 +83,11 @@ mount {
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
{DEV}
|
||||
mount {
|
||||
src: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
dst: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
#{DEV}
|
||||
|
||||
@@ -115,4 +115,11 @@ mount {
|
||||
rw: true
|
||||
}
|
||||
|
||||
{DEV}
|
||||
mount {
|
||||
src: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
dst: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
#{DEV}
|
||||
|
||||
@@ -76,4 +76,11 @@ mount {
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
{DEV}
|
||||
mount {
|
||||
src: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
dst: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
#{DEV}
|
||||
|
||||
@@ -138,3 +138,12 @@ iface_no_lo: true
|
||||
{SHARED_MOUNT}
|
||||
|
||||
envar: "HOME=/tmp"
|
||||
|
||||
mount {
|
||||
src: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
dst: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
#{DEV}
|
||||
|
||||
@@ -161,4 +161,11 @@ iface_no_lo: true
|
||||
|
||||
envar: "HOME=/tmp/{LANG}"
|
||||
|
||||
mount {
|
||||
src: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
dst: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
#{DEV}
|
||||
|
||||
@@ -112,3 +112,12 @@ mount {
|
||||
}
|
||||
|
||||
{SHARED_MOUNT}
|
||||
|
||||
mount {
|
||||
src: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
dst: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
#{DEV}
|
||||
|
||||
@@ -102,4 +102,11 @@ iface_no_lo: true
|
||||
envar: "GOPATH=/tmp/.cache/go"
|
||||
envar: "HOME=/tmp/go"
|
||||
|
||||
mount {
|
||||
src: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
dst: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
#{DEV}
|
||||
|
||||
@@ -105,3 +105,12 @@ iface_no_lo: true
|
||||
{SHARED_MOUNT}
|
||||
|
||||
envar: "HOME=/tmp"
|
||||
|
||||
mount {
|
||||
src: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
dst: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
#{DEV}
|
||||
|
||||
@@ -141,4 +141,11 @@ envar: "LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH"
|
||||
envar: "PYTHONPATH={ADDITIONAL_PYTHON_PATHS}"
|
||||
envar: "HOME=/tmp"
|
||||
|
||||
mount {
|
||||
src: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
dst: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
#{DEV}
|
||||
|
||||
@@ -109,5 +109,11 @@ iface_no_lo: true
|
||||
|
||||
{SHARED_DEPENDENCIES}
|
||||
|
||||
mount {
|
||||
src: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
dst: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
{DEV}
|
||||
#{DEV}
|
||||
|
||||
@@ -106,5 +106,12 @@ mount {
|
||||
|
||||
{SHARED_MOUNT}
|
||||
|
||||
{DEV}
|
||||
mount {
|
||||
src: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
dst: "{TRACING_PROXY_CA_CERT_PATH}"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
#{DEV}
|
||||
|
||||
|
||||
@@ -35,13 +35,15 @@ use crate::handle_child::run_future_with_polling_update_job_poller;
|
||||
|
||||
use crate::{
|
||||
common::{
|
||||
build_args_map, build_command_with_isolation, get_reserved_variables, read_file, read_file_content, start_child_process,
|
||||
OccupancyMetrics,
|
||||
build_args_map, build_command_with_isolation, get_reserved_variables, read_file,
|
||||
read_file_content, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
|
||||
},
|
||||
get_proxy_envs_for_lang,
|
||||
handle_child::handle_child,
|
||||
DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV, PROXY_ENVS,
|
||||
DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV, TRACING_PROXY_CA_CERT_PATH,
|
||||
};
|
||||
use windmill_common::client::AuthedClient;
|
||||
use windmill_common::scripts::ScriptLang;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
|
||||
@@ -165,8 +167,7 @@ exit $exit_status
|
||||
.runnable_path
|
||||
.as_ref()
|
||||
.map(|x| {
|
||||
!x.starts_with(INIT_SCRIPT_PATH_PREFIX)
|
||||
&& !x.starts_with(PERIODIC_SCRIPT_PATH_PREFIX)
|
||||
!x.starts_with(INIT_SCRIPT_PATH_PREFIX) && !x.starts_with(PERIODIC_SCRIPT_PATH_PREFIX)
|
||||
})
|
||||
.unwrap_or(true);
|
||||
|
||||
@@ -178,7 +179,9 @@ exit $exit_status
|
||||
&NSJAIL_CONFIG_RUN_BASH_CONTENT
|
||||
.replace("{JOB_DIR}", job_dir)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{SHARED_MOUNT}", shared_mount),
|
||||
.replace("{SHARED_MOUNT}", shared_mount)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL),
|
||||
)?;
|
||||
let mut cmd_args = vec![
|
||||
"--config",
|
||||
@@ -193,7 +196,7 @@ exit $exit_status
|
||||
.current_dir(job_dir)
|
||||
.env_clear()
|
||||
.envs(reserved_variables)
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Bash).await?)
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
.args(cmd_args)
|
||||
@@ -219,6 +222,7 @@ exit $exit_status
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Bash).await?)
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
.env("HOME", HOME_ENV.as_str())
|
||||
|
||||
@@ -17,16 +17,17 @@ use crate::{
|
||||
build_command_with_isolation, create_args_and_out_file, get_reserved_variables,
|
||||
parse_npm_config, read_file, read_file_content, read_result, start_child_process,
|
||||
write_file_binary, MaybeLock, OccupancyMetrics, StreamNotifier,
|
||||
DEV_CONF_NSJAIL,
|
||||
},
|
||||
handle_child::handle_child,
|
||||
BUNFIG_INSTALL_SCOPES, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR, BUN_NO_CACHE, BUN_PATH,
|
||||
DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NODE_BIN_PATH, NODE_PATH, NPM_CONFIG_REGISTRY,
|
||||
NPM_PATH, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, TZ_ENV,
|
||||
NPM_PATH, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, TRACING_PROXY_CA_CERT_PATH, TZ_ENV, get_proxy_envs_for_lang,
|
||||
};
|
||||
use windmill_common::{
|
||||
client::AuthedClient,
|
||||
s3_helpers::BundleFormat,
|
||||
scripts::{id_to_codebase_info, CodebaseInfo},
|
||||
scripts::{id_to_codebase_info, CodebaseInfo, ScriptLang},
|
||||
utils::WarnAfterExt,
|
||||
workspace_dependencies::WorkspaceDependenciesPrefetched,
|
||||
};
|
||||
@@ -41,7 +42,6 @@ use tokio::io::AsyncReadExt;
|
||||
use windmill_common::{
|
||||
error::{self, Result},
|
||||
get_latest_hash_for_path,
|
||||
scripts::ScriptLang,
|
||||
worker::{exists_in_cache, save_cache, write_file, Connection, DISABLE_BUNDLING},
|
||||
DB,
|
||||
};
|
||||
@@ -1375,7 +1375,9 @@ try {{
|
||||
"/tmp/bun/shared"
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL),
|
||||
)?;
|
||||
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
@@ -1416,6 +1418,7 @@ try {{
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Bun).await?)
|
||||
.envs(common_bun_proc_envs)
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.args(args)
|
||||
@@ -1433,6 +1436,7 @@ try {{
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Bun).await?)
|
||||
.envs(common_bun_proc_envs)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
@@ -1463,6 +1467,7 @@ try {{
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Bun).await?)
|
||||
.envs(common_bun_proc_envs)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
|
||||
@@ -50,6 +50,20 @@ use crate::agent_workers::UPDATE_PING_URL;
|
||||
use crate::{JOB_DEFAULT_TIMEOUT, MAX_RESULT_SIZE, MAX_TIMEOUT_DURATION, PATH_ENV};
|
||||
use windmill_common::client::AuthedClient;
|
||||
|
||||
/// Additional nsjail config for development. Currently used for nix flake.
|
||||
#[cfg(debug_assertions)]
|
||||
pub const DEV_CONF_NSJAIL: &str = r#"
|
||||
mount {
|
||||
src: "/nix/store"
|
||||
dst: "/nix/store"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
"#;
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub const DEV_CONF_NSJAIL: &str = "";
|
||||
|
||||
pub async fn build_args_map<'a>(
|
||||
job: &'a MiniPulledJob,
|
||||
client: &AuthedClient,
|
||||
|
||||
@@ -28,12 +28,14 @@ use windmill_queue::CanceledBy;
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, check_executor_binary_exists, create_args_and_out_file,
|
||||
get_reserved_variables, read_result, start_child_process,
|
||||
get_reserved_variables, read_result, start_child_process, DEV_CONF_NSJAIL,
|
||||
},
|
||||
handle_child::handle_child,
|
||||
handle_child::handle_child, get_proxy_envs_for_lang,
|
||||
CSHARP_CACHE_DIR, DISABLE_NSJAIL, DISABLE_NUSER, DOTNET_PATH, HOME_ENV, NSJAIL_PATH,
|
||||
NUGET_CONFIG, PATH_ENV, TZ_ENV,
|
||||
NUGET_CONFIG, PATH_ENV, TRACING_PROXY_CA_CERT_PATH, TZ_ENV,
|
||||
};
|
||||
#[cfg(feature = "csharp")]
|
||||
use windmill_common::scripts::ScriptLang;
|
||||
|
||||
use crate::common::OccupancyMetrics;
|
||||
use windmill_common::client::AuthedClient;
|
||||
@@ -562,7 +564,9 @@ pub async fn handle_csharp_job(
|
||||
.replace("{CACHE_DIR}", CSHARP_CACHE_DIR)
|
||||
.replace("{CACHE_HASH}", &hash)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{SHARED_MOUNT}", shared_mount),
|
||||
.replace("{SHARED_MOUNT}", shared_mount)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL),
|
||||
)?;
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
nsjail_cmd
|
||||
@@ -570,6 +574,7 @@ pub async fn handle_csharp_job(
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::CSharp).await?)
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("TZ", TZ_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
@@ -600,6 +605,7 @@ pub async fn handle_csharp_job(
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::CSharp).await?)
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("TZ", TZ_ENV.as_str())
|
||||
.env("DOTNET_CLI_TELEMETRY_OPTOUT", "true")
|
||||
|
||||
@@ -11,12 +11,12 @@ use crate::{
|
||||
start_child_process, OccupancyMetrics, StreamNotifier,
|
||||
},
|
||||
handle_child::handle_child,
|
||||
DENO_CACHE_DIR, DENO_PATH, DISABLE_NSJAIL, HOME_ENV, NPM_CONFIG_REGISTRY, PATH_ENV, TZ_ENV,
|
||||
get_proxy_envs_for_lang, DENO_CACHE_DIR, DENO_PATH, DISABLE_NSJAIL, HOME_ENV, NPM_CONFIG_REGISTRY, PATH_ENV, TZ_ENV,
|
||||
};
|
||||
use windmill_common::client::AuthedClient;
|
||||
|
||||
use tokio::{fs::File, io::AsyncReadExt, process::Command};
|
||||
use windmill_common::{error::Result, worker::write_file, BASE_URL};
|
||||
use windmill_common::{error::Result, scripts::ScriptLang, worker::write_file, BASE_URL};
|
||||
use windmill_common::{
|
||||
error::{self},
|
||||
worker::Connection,
|
||||
@@ -92,6 +92,12 @@ async fn get_common_deno_proc_envs(
|
||||
crate::USERPROFILE_ENV.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
// Add proxy envs (including OTEL tracing proxy if enabled for deno)
|
||||
for (k, v) in get_proxy_envs_for_lang(&ScriptLang::Deno).await.unwrap_or_default() {
|
||||
deno_envs.insert(k.to_string(), v);
|
||||
}
|
||||
|
||||
return deno_envs;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::{common::MaybeLock, PROXY_ENVS};
|
||||
use crate::{common::MaybeLock, get_proxy_envs_for_lang};
|
||||
use windmill_common::scripts::ScriptLang;
|
||||
use std::{collections::HashMap, fs::DirBuilder, process::Stdio};
|
||||
|
||||
use itertools::Itertools;
|
||||
@@ -20,11 +21,11 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob};
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, capitalize, create_args_and_out_file, get_reserved_variables,
|
||||
read_result, start_child_process, OccupancyMetrics,
|
||||
read_result, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
|
||||
},
|
||||
handle_child::handle_child,
|
||||
DISABLE_NSJAIL, DISABLE_NUSER, GOPRIVATE, GOPROXY, GO_BIN_CACHE_DIR, GO_CACHE_DIR, HOME_ENV,
|
||||
NSJAIL_PATH, PATH_ENV, TZ_ENV,
|
||||
NSJAIL_PATH, PATH_ENV, PROXY_ENVS, TRACING_PROXY_CA_CERT_PATH, TZ_ENV,
|
||||
};
|
||||
use windmill_common::client::AuthedClient;
|
||||
|
||||
@@ -344,7 +345,9 @@ func Run(req Req) (interface{{}}, error){{
|
||||
&NSJAIL_CONFIG_RUN_GO_CONTENT
|
||||
.replace("{JOB_DIR}", job_dir)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{SHARED_MOUNT}", shared_mount),
|
||||
.replace("{SHARED_MOUNT}", shared_mount)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL),
|
||||
)?;
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
nsjail_cmd
|
||||
@@ -352,6 +355,7 @@ func Run(req Req) (interface{{}}, error){{
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Go).await?)
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("TZ", TZ_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
@@ -372,6 +376,7 @@ func Run(req Req) (interface{{}}, error){{
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Go).await?)
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("TZ", TZ_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
|
||||
@@ -120,6 +120,11 @@ pub async fn handle_child(
|
||||
) -> error::Result<HandleChildResult> {
|
||||
let start = Instant::now();
|
||||
|
||||
#[cfg(all(feature = "private", feature = "enterprise"))]
|
||||
if crate::OTEL_TRACING_PROXY_SETTINGS.read().await.enabled {
|
||||
crate::otel_tracing_proxy_ee::set_current_job_context(*job_id).await;
|
||||
}
|
||||
|
||||
let pid = child.id();
|
||||
#[cfg(target_os = "linux")]
|
||||
if let Some(pid) = pid {
|
||||
|
||||
@@ -608,7 +608,6 @@ async fn run<'a>(
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.args(vec![
|
||||
"--config",
|
||||
"run.config.proto",
|
||||
|
||||
@@ -34,6 +34,9 @@ mod global_cache;
|
||||
mod go_executor;
|
||||
mod graphql_executor;
|
||||
mod handle_child;
|
||||
#[cfg(all(feature = "private", feature = "enterprise"))]
|
||||
mod otel_tracing_proxy_ee;
|
||||
mod otel_tracing_proxy_oss;
|
||||
pub mod job_logger;
|
||||
#[cfg(feature = "private")]
|
||||
pub mod job_logger_ee;
|
||||
@@ -78,6 +81,9 @@ pub use worker::*;
|
||||
pub use worker_lockfiles::{
|
||||
process_relative_imports, trigger_dependents_to_recompute_dependencies,
|
||||
};
|
||||
pub use otel_tracing_proxy_oss::start_otel_tracing_proxy;
|
||||
#[cfg(all(feature = "private", feature = "enterprise"))]
|
||||
pub use otel_tracing_proxy_oss::{set_current_job_context, TRACING_PROXY_PORT};
|
||||
|
||||
pub use result_processor::handle_job_error;
|
||||
|
||||
|
||||
@@ -14,11 +14,12 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob};
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, create_args_and_out_file, get_reserved_variables,
|
||||
read_result, start_child_process, OccupancyMetrics,
|
||||
read_result, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
|
||||
},
|
||||
handle_child, DISABLE_NSJAIL, DISABLE_NUSER, NSJAIL_PATH, PATH_ENV,
|
||||
PROXY_ENVS,
|
||||
handle_child, get_proxy_envs_for_lang, DISABLE_NSJAIL, DISABLE_NUSER, NSJAIL_PATH, PATH_ENV,
|
||||
TRACING_PROXY_CA_CERT_PATH,
|
||||
};
|
||||
use windmill_common::scripts::ScriptLang;
|
||||
use windmill_common::client::AuthedClient;
|
||||
|
||||
const NSJAIL_CONFIG_RUN_NU_CONTENT: &str = include_str!("../nsjail/run.nu.config.proto");
|
||||
@@ -251,7 +252,9 @@ async fn run<'a>(
|
||||
.replace("{JOB_DIR}", job_dir)
|
||||
.replace("{NU_PATH}", &NU_PATH)
|
||||
.replace("{SHARED_MOUNT}", &shared_mount)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()),
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL),
|
||||
)?;
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
nsjail_cmd
|
||||
@@ -261,7 +264,7 @@ async fn run<'a>(
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Nu).await?)
|
||||
.args(vec![
|
||||
"--config",
|
||||
"run.config.proto",
|
||||
@@ -300,7 +303,7 @@ async fn run<'a>(
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Nu).await?)
|
||||
// TODO(v1):
|
||||
// "--plugins",
|
||||
// &format!(
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//! OSS stubs for OTEL tracing proxy (EE feature)
|
||||
|
||||
#[cfg(all(feature = "private", feature = "enterprise"))]
|
||||
pub use crate::otel_tracing_proxy_ee::*;
|
||||
|
||||
/// Start the OTEL tracing proxy (no-op in OSS)
|
||||
#[cfg(not(all(feature = "private", feature = "enterprise")))]
|
||||
pub async fn start_otel_tracing_proxy(
|
||||
_db: windmill_common::DB,
|
||||
_killpill_rx: tokio::sync::broadcast::Receiver<()>,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -31,6 +31,7 @@ use windmill_common::{
|
||||
self,
|
||||
Error::{self},
|
||||
},
|
||||
scripts::ScriptLang,
|
||||
utils::calculate_hash,
|
||||
worker::{
|
||||
copy_dir_recursively, pad_string, split_python_requirements, write_file, Connection,
|
||||
@@ -127,12 +128,12 @@ use windmill_common::s3_helpers::OBJECT_STORE_SETTINGS;
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, create_args_and_out_file, get_reserved_variables, read_file,
|
||||
read_result, start_child_process, OccupancyMetrics, StreamNotifier,
|
||||
read_result, start_child_process, OccupancyMetrics, StreamNotifier, DEV_CONF_NSJAIL,
|
||||
},
|
||||
handle_child::handle_child,
|
||||
worker_utils::ping_job_status,
|
||||
PyV, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV, PIP_EXTRA_INDEX_URL,
|
||||
PIP_INDEX_URL, PROXY_ENVS, PY_INSTALL_DIR, TZ_ENV, UV_CACHE_DIR,
|
||||
PIP_INDEX_URL, PROXY_ENVS, PY_INSTALL_DIR, TRACING_PROXY_CA_CERT_PATH, TZ_ENV, UV_CACHE_DIR, get_proxy_envs_for_lang,
|
||||
};
|
||||
use windmill_common::client::AuthedClient;
|
||||
|
||||
@@ -803,7 +804,9 @@ mount {{
|
||||
.replace(
|
||||
"{ADDITIONAL_PYTHON_PATHS}",
|
||||
additional_python_paths_folders.as_str(),
|
||||
),
|
||||
)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL),
|
||||
)?;
|
||||
} else {
|
||||
reserved_variables.insert("PYTHONPATH".to_string(), additional_python_paths_folders);
|
||||
@@ -822,7 +825,7 @@ mount {{
|
||||
.env_clear()
|
||||
// inject PYTHONPATH here - for some reason I had to do it in nsjail conf
|
||||
.envs(reserved_variables)
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Python3).await?)
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("TZ", TZ_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
@@ -848,6 +851,7 @@ mount {{
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Python3).await?)
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("TZ", TZ_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
@@ -1372,6 +1376,8 @@ async fn spawn_uv_install(
|
||||
.replace("{PY_INSTALL_DIR}", &PY_INSTALL_DIR)
|
||||
.replace("{TARGET_DIR}", &venv_p)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL)
|
||||
.as_str(),
|
||||
)?;
|
||||
|
||||
|
||||
@@ -24,12 +24,14 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob};
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, create_args_and_out_file, get_reserved_variables,
|
||||
read_result, start_child_process, OccupancyMetrics,
|
||||
read_result, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
|
||||
},
|
||||
handle_child::{self},
|
||||
handle_child::{self}, get_proxy_envs_for_lang,
|
||||
universal_pkg_installer::{par_install_language_dependencies_seq, RequiredDependency},
|
||||
DISABLE_NSJAIL, DISABLE_NUSER, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, RUBY_CACHE_DIR, RUBY_REPOS,
|
||||
TRACING_PROXY_CA_CERT_PATH,
|
||||
};
|
||||
use windmill_common::scripts::ScriptLang;
|
||||
lazy_static::lazy_static! {
|
||||
static ref RUBY_CONCURRENT_DOWNLOADS: usize = std::env::var("RUBY_CONCURRENT_DOWNLOADS").ok().map(|flag| flag.parse().unwrap_or(20)).unwrap_or(20);
|
||||
static ref RUBY_PATH: String = std::env::var("RUBY_PATH").unwrap_or_else(|_| "/usr/bin/ruby".to_string());
|
||||
@@ -49,19 +51,6 @@ const NSJAIL_CONFIG_DOWNLOAD_RUBY_CONTENT: &str =
|
||||
include_str!("../nsjail/download.ruby.config.proto");
|
||||
const NSJAIL_CONFIG_LOCK_RUBY_CONTENT: &str = include_str!("../nsjail/lock.ruby.config.proto");
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
const DEV_CONF_NSJAIL: &'static str = r#"
|
||||
# Mount nix store for nixos to work properly
|
||||
mount {
|
||||
src: "/nix/store"
|
||||
dst: "/nix/store"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
"#;
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
const DEV_CONF_NSJAIL: &'static str = "";
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct JobHandlerInput<'a> {
|
||||
@@ -349,7 +338,8 @@ Your Gemfile syntax will continue to work as-is."
|
||||
&NSJAIL_CONFIG_LOCK_RUBY_CONTENT
|
||||
.replace("{JOB_DIR}", job_dir)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{DEV}", DEV_CONF_NSJAIL), // .replace("{BUILD}", &build_dir),
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL), // .replace("{BUILD}", &build_dir),
|
||||
)?;
|
||||
let mut cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
cmd.args(vec!["--config", &nsjail_proto, "--", BUNDLE_PATH.as_str()]);
|
||||
@@ -624,7 +614,8 @@ async fn install<'a>(
|
||||
&NSJAIL_CONFIG_DOWNLOAD_RUBY_CONTENT
|
||||
.replace("{TARGET}", &dependency.path)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{DEV}", DEV_CONF_NSJAIL), // .replace("{BUILD}", &build_dir),
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL), // .replace("{BUILD}", &build_dir),
|
||||
)?;
|
||||
let mut cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
cmd.args(vec!["--config", &nsjail_proto, "--", GEM_PATH.as_str()]);
|
||||
@@ -791,7 +782,8 @@ mount {{
|
||||
.replace("{JOB_DIR}", job_dir)
|
||||
.replace("{SHARED_MOUNT}", &shared_mount)
|
||||
.replace("{SHARED_DEPENDENCIES}", &shared_deps)
|
||||
.replace("{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()),
|
||||
)?;
|
||||
let mut cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
@@ -803,7 +795,7 @@ mount {{
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(RUBY_PROXY_ENVS.clone())
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Ruby).await?)
|
||||
.args(vec![
|
||||
"--config",
|
||||
"run.config.proto",
|
||||
@@ -842,7 +834,7 @@ mount {{
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
.envs(reserved_variables)
|
||||
.envs(RUBY_PROXY_ENVS.clone())
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Ruby).await?)
|
||||
.envs(envs);
|
||||
|
||||
cmd.stdin(Stdio::null())
|
||||
|
||||
@@ -20,12 +20,13 @@ use windmill_queue::{append_logs, CanceledBy};
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, check_executor_binary_exists, create_args_and_out_file, get_reserved_variables,
|
||||
read_result, start_child_process, OccupancyMetrics,
|
||||
read_result, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
|
||||
},
|
||||
handle_child::handle_child,
|
||||
DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, RUST_CACHE_DIR,
|
||||
TZ_ENV,
|
||||
get_proxy_envs_for_lang, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV,
|
||||
PROXY_ENVS, RUST_CACHE_DIR, TRACING_PROXY_CA_CERT_PATH, TZ_ENV,
|
||||
};
|
||||
use windmill_common::scripts::ScriptLang;
|
||||
use windmill_common::client::AuthedClient;
|
||||
|
||||
#[cfg(windows)]
|
||||
@@ -52,19 +53,6 @@ lazy_static::lazy_static! {
|
||||
static ref RUSTUP_HOME_DEFAULT: String = format!("{}\\.rustup", *HOME_DIR);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
const DEV_CONF_NSJAIL: &'static str = r#"
|
||||
# Mount nix store for nixos to work properly
|
||||
mount {
|
||||
src: "/nix/store"
|
||||
dst: "/nix/store"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
"#;
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
const DEV_CONF_NSJAIL: &'static str = "";
|
||||
|
||||
#[cfg(not(windows))]
|
||||
lazy_static::lazy_static! {
|
||||
@@ -341,7 +329,8 @@ pub async fn build_rust_crate(
|
||||
.replace("{JOB_DIR}", job_dir)
|
||||
.replace("{CACHE_DIR}", RUST_CACHE_DIR)
|
||||
.replace("{CARGO_HOME}", CARGO_HOME.as_str())
|
||||
.replace("{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{BUILD}", &build_dir),
|
||||
)?;
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
@@ -549,7 +538,8 @@ pub async fn handle_rust_job(
|
||||
.replace("{CACHE_DIR}", RUST_CACHE_DIR)
|
||||
.replace("{CACHE_HASH}", &hash)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{SHARED_MOUNT}", shared_mount),
|
||||
)?;
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
@@ -558,6 +548,7 @@ pub async fn handle_rust_job(
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Rust).await?)
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("TZ", TZ_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
@@ -573,6 +564,7 @@ pub async fn handle_rust_job(
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.envs(get_proxy_envs_for_lang(&ScriptLang::Rust).await?)
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("TZ", TZ_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
|
||||
@@ -55,7 +55,7 @@ use windmill_common::METRICS_ENABLED;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::types::Json;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{HashMap, HashSet},
|
||||
fmt::Display,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU16, Ordering},
|
||||
@@ -228,6 +228,9 @@ pub const GO_BIN_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "gobin");
|
||||
pub const POWERSHELL_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "powershell");
|
||||
pub const COMPOSER_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "composer");
|
||||
|
||||
pub const TRACING_PROXY_CA_CERT_PATH: &str =
|
||||
concatcp!(ROOT_CACHE_NOMOUNT_DIR, "tracing_proxy_ca.pem");
|
||||
|
||||
const NUM_SECS_PING: u64 = 5;
|
||||
const NUM_SECS_READINGS: u64 = 60;
|
||||
|
||||
@@ -248,6 +251,16 @@ const VACUUM_PERIOD: u32 = 50000;
|
||||
|
||||
pub const MAX_BUFFERED_DEDICATED_JOBS: usize = 3;
|
||||
|
||||
/// Per-language OTEL tracing proxy configuration.
|
||||
/// Default languages are configured in frontend instanceSettings.ts
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct OtelTracingProxySettings {
|
||||
#[serde(default)]
|
||||
pub enabled: bool,
|
||||
#[serde(default)]
|
||||
pub enabled_languages: HashSet<ScriptLang>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "prometheus")]
|
||||
lazy_static::lazy_static! {
|
||||
|
||||
@@ -505,6 +518,7 @@ lazy_static::lazy_static! {
|
||||
pub static ref HTTP_PROXY: Option<String> = std::env::var("http_proxy").ok().or(std::env::var("HTTP_PROXY").ok());
|
||||
pub static ref HTTPS_PROXY: Option<String> = std::env::var("https_proxy").ok().or(std::env::var("HTTPS_PROXY").ok());
|
||||
|
||||
/// Static proxy environment variables from env vars (for languages not using dynamic OTEL tracing proxy config)
|
||||
pub static ref PROXY_ENVS: Vec<(&'static str, String)> = {
|
||||
let mut proxy_env = Vec::new();
|
||||
if let Some(no_proxy) = NO_PROXY.as_ref() {
|
||||
@@ -520,6 +534,9 @@ lazy_static::lazy_static! {
|
||||
}
|
||||
proxy_env
|
||||
};
|
||||
|
||||
/// Per-language OTEL tracing proxy settings (configured via instance settings)
|
||||
pub static ref OTEL_TRACING_PROXY_SETTINGS: Arc<RwLock<OtelTracingProxySettings>> = Arc::new(RwLock::new(OtelTracingProxySettings::default()));
|
||||
pub static ref WHITELIST_ENVS: HashMap<String, String> = {
|
||||
windmill_common::worker::load_env_vars(
|
||||
windmill_common::worker::load_whitelist_env_vars_from_env(),
|
||||
@@ -609,6 +626,50 @@ lazy_static::lazy_static! {
|
||||
|
||||
type Envs = Vec<(String, String)>;
|
||||
|
||||
/// Check if OTEL tracing proxy is enabled for a specific language (EE only)
|
||||
pub async fn is_otel_tracing_proxy_enabled_for_lang(lang: &ScriptLang) -> bool {
|
||||
cfg!(all(feature = "private", feature = "enterprise")) && {
|
||||
let settings = OTEL_TRACING_PROXY_SETTINGS.read().await;
|
||||
settings.enabled && settings.enabled_languages.contains(lang)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get proxy environment variables for job execution for a specific language.
|
||||
/// When OTEL tracing proxy is enabled for this language, routes all traffic through the proxy.
|
||||
/// Otherwise, uses the standard HTTP_PROXY/HTTPS_PROXY from environment.
|
||||
pub async fn get_proxy_envs_for_lang(lang: &ScriptLang) -> anyhow::Result<Vec<(&'static str, String)>> {
|
||||
#[cfg(all(feature = "private", feature = "enterprise"))]
|
||||
if is_otel_tracing_proxy_enabled_for_lang(lang).await {
|
||||
return get_otel_tracing_proxy_envs().await;
|
||||
}
|
||||
let _ = lang;
|
||||
Ok(PROXY_ENVS.clone())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "private", feature = "enterprise"))]
|
||||
async fn get_otel_tracing_proxy_envs() -> anyhow::Result<Vec<(&'static str, String)>> {
|
||||
let port = crate::otel_tracing_proxy_ee::TRACING_PROXY_PORT
|
||||
.read()
|
||||
.await
|
||||
.ok_or_else(|| anyhow::anyhow!("OTEL tracing proxy port not initialized"))?;
|
||||
let proxy_url = format!("http://127.0.0.1:{}", port);
|
||||
Ok(vec![
|
||||
("HTTP_PROXY", proxy_url.clone()),
|
||||
("HTTPS_PROXY", proxy_url.clone()),
|
||||
// Lowercase variants for Ruby and other runtimes that check lowercase first
|
||||
("http_proxy", proxy_url.clone()),
|
||||
("https_proxy", proxy_url),
|
||||
("NO_PROXY", "".to_string()),
|
||||
("no_proxy", "".to_string()),
|
||||
// CA cert for various runtimes to trust the tracing proxy
|
||||
("SSL_CERT_FILE", TRACING_PROXY_CA_CERT_PATH.to_string()),
|
||||
("REQUESTS_CA_BUNDLE", TRACING_PROXY_CA_CERT_PATH.to_string()),
|
||||
("NODE_EXTRA_CA_CERTS", TRACING_PROXY_CA_CERT_PATH.to_string()),
|
||||
("CURL_CA_BUNDLE", TRACING_PROXY_CA_CERT_PATH.to_string()),
|
||||
("DENO_CERT", TRACING_PROXY_CA_CERT_PATH.to_string()),
|
||||
])
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref SYSTEM_ROOT: String = std::env::var("SystemRoot").unwrap_or_else(|_| "C:\\Windows".to_string());
|
||||
@@ -2689,8 +2750,6 @@ pub async fn handle_queued_job(
|
||||
flow_runners: Option<Arc<FlowRunners>>,
|
||||
#[cfg(feature = "benchmark")] _bench: &mut BenchmarkIter,
|
||||
) -> windmill_common::error::Result<bool> {
|
||||
// Extract the active span from the context
|
||||
|
||||
if job.canceled_by.is_some() {
|
||||
return Err(Error::JsonErr(canceled_job_to_result(&job)));
|
||||
}
|
||||
@@ -3136,7 +3195,7 @@ pub fn build_envs(
|
||||
};
|
||||
|
||||
for (k, v) in PROXY_ENVS.iter() {
|
||||
envs.insert(k.to_string(), v.to_string());
|
||||
envs.insert(k.to_string(), v.clone());
|
||||
}
|
||||
|
||||
Ok(envs)
|
||||
|
||||
@@ -287,6 +287,7 @@
|
||||
JAVA_PATH = "${pkgs.jdk21}/bin/java";
|
||||
JAVAC_PATH = "${pkgs.jdk21}/bin/javac";
|
||||
COURSIER_PATH = "${coursier}/coursier";
|
||||
BASH_PATH = "bash";
|
||||
RUBY_PATH = "${pkgs.ruby}/bin/ruby";
|
||||
RUBY_BUNDLE_PATH = "${pkgs.ruby}/bin/bundle";
|
||||
RUBY_GEM_PATH = "${pkgs.ruby}/bin/gem";
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
} from './graph/renderers/nodes/AIToolNode.svelte'
|
||||
import JobAssetsViewer from './assets/JobAssetsViewer.svelte'
|
||||
import McpToolCallDetails from './McpToolCallDetails.svelte'
|
||||
import JobOtelTraces from './JobOtelTraces.svelte'
|
||||
import { SelectionManager } from './graph/selectionUtils.svelte'
|
||||
import { useThrottle } from 'runed'
|
||||
|
||||
@@ -103,7 +104,7 @@
|
||||
refreshGlobal: (moduleId: string, clear: boolean, root: string) => Promise<void>
|
||||
updateGlobalRefresh: (moduleId: string, updateFn: (clear, root) => Promise<void>) => void
|
||||
job?: (Job & { result_stream?: string }) | undefined
|
||||
rightColumnSelect?: 'timeline' | 'node_status' | 'node_definition' | 'user_states'
|
||||
rightColumnSelect?: 'timeline' | 'node_status' | 'node_definition' | 'user_states' | 'tracing'
|
||||
localModuleStates?: Record<string, GraphModuleState>
|
||||
localDurationStatuses?: Record<string, DurationStatus>
|
||||
onResultStreamUpdate?: ({
|
||||
@@ -1756,7 +1757,7 @@
|
||||
bind:expandedSubflows
|
||||
onSelect={(e) => {
|
||||
console.log('onSelect', e)
|
||||
if (rightColumnSelect != 'node_definition') {
|
||||
if (rightColumnSelect != 'node_definition' && rightColumnSelect != 'tracing') {
|
||||
rightColumnSelect = 'node_status'
|
||||
}
|
||||
if (typeof e == 'string') {
|
||||
@@ -1811,6 +1812,7 @@
|
||||
{#if Object.keys(job?.flow_status?.user_states ?? {}).length > 0}
|
||||
<Tab value="user_states" label="User States" />
|
||||
{/if}
|
||||
<Tab value="tracing" label="Tracing" />
|
||||
</Tabs>
|
||||
{#if rightColumnSelect == 'timeline'}
|
||||
<FlowTimeline
|
||||
@@ -2009,6 +2011,13 @@
|
||||
<div class="p-2">
|
||||
<JobArgs argLabel="Key" args={job?.flow_status?.user_states ?? {}} />
|
||||
</div>
|
||||
{:else if rightColumnSelect == 'tracing'}
|
||||
{@const node = selectedNode ? localModuleStates[selectedNode] : undefined}
|
||||
{#if node?.job_id}
|
||||
<JobOtelTraces jobId={node.job_id} />
|
||||
{:else}
|
||||
<div class="p-4 text-secondary">Select a node with a job to see HTTP request traces</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
import { enterpriseLicense, isCriticalAlertsUIOpen } from '$lib/stores'
|
||||
import { AlertCircle, BadgeCheck, BadgeX, Info } from 'lucide-svelte'
|
||||
import type { Setting } from './instanceSettings'
|
||||
import { OTEL_TRACING_PROXY_LANGUAGES } from './instanceSettings'
|
||||
import { LanguageIcon } from './common/languageIcons'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import ObjectStoreConfigSettings from './ObjectStoreConfigSettings.svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
@@ -831,6 +833,39 @@
|
||||
</div> -->
|
||||
{/if}
|
||||
</div>
|
||||
{:else if setting.fieldType == 'otel_tracing_proxy'}
|
||||
{@const tracingProxyVal = $values[setting.key] ?? { enabled: false, enabled_languages: [...OTEL_TRACING_PROXY_LANGUAGES] }}
|
||||
<div class="flex flex-col gap-4">
|
||||
<Toggle
|
||||
id="otel_tracing_proxy_enabled"
|
||||
checked={tracingProxyVal.enabled ?? false}
|
||||
on:change={(e) => {
|
||||
$values[setting.key] = { ...tracingProxyVal, enabled: e.detail }
|
||||
}}
|
||||
options={{ right: 'Enabled' }}
|
||||
/>
|
||||
{#if tracingProxyVal.enabled}
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#each OTEL_TRACING_PROXY_LANGUAGES as lang (lang)}
|
||||
{@const isEnabled = (tracingProxyVal.enabled_languages ?? []).includes(lang)}
|
||||
<button
|
||||
class="flex flex-col items-center gap-1 p-2 rounded border transition-all {isEnabled
|
||||
? 'border-blue-500 bg-blue-500/10'
|
||||
: 'border-gray-300 opacity-40 hover:opacity-70'}"
|
||||
onclick={() => {
|
||||
const current = tracingProxyVal.enabled_languages ?? []
|
||||
const newLangs = isEnabled
|
||||
? current.filter((l) => l !== lang)
|
||||
: [...current, lang]
|
||||
$values[setting.key] = { ...tracingProxyVal, enabled_languages: newLangs }
|
||||
}}
|
||||
>
|
||||
<LanguageIcon {lang} size={24} />
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if setting.fieldType == 'object_store_config'}
|
||||
<ObjectStoreConfigSettings bind:bucket_config={$values[setting.key]} />
|
||||
<div class="mb-6"></div>
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
<script lang="ts">
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { Alert, Skeleton } from './common'
|
||||
import { Activity } from 'lucide-svelte'
|
||||
import { JobService } from '$lib/gen'
|
||||
import { msToReadableTime } from '$lib/utils'
|
||||
|
||||
// OTEL SpanKind enum values from opentelemetry-proto/opentelemetry/proto/trace/v1/trace.proto
|
||||
const SpanKind = {
|
||||
UNSPECIFIED: 0,
|
||||
INTERNAL: 1,
|
||||
SERVER: 2,
|
||||
CLIENT: 3,
|
||||
PRODUCER: 4,
|
||||
CONSUMER: 5
|
||||
} as const
|
||||
|
||||
// OTEL StatusCode enum values from opentelemetry-proto/opentelemetry/proto/trace/v1/trace.proto
|
||||
const StatusCode = {
|
||||
UNSET: 0,
|
||||
OK: 1,
|
||||
ERROR: 2
|
||||
} as const
|
||||
|
||||
// Matches OTEL Span proto spec. If expanding usage, consider generating types from
|
||||
// opentelemetry-proto .proto files using ts-proto: npx protoc --ts_proto_out=./src/generated
|
||||
// See: https://github.com/open-telemetry/opentelemetry-proto
|
||||
interface OtelSpan {
|
||||
trace_id: string
|
||||
span_id: string
|
||||
parent_span_id: string | null
|
||||
name: string
|
||||
kind: number
|
||||
start_time_unix_nano: number
|
||||
end_time_unix_nano: number
|
||||
status: { code: number; message: string } | null
|
||||
attributes: Record<string, any>
|
||||
}
|
||||
|
||||
interface Props {
|
||||
jobId: string
|
||||
}
|
||||
|
||||
let { jobId }: Props = $props()
|
||||
let traces: OtelSpan[] = $state([])
|
||||
let loading = $state(true)
|
||||
let error: string | null = $state(null)
|
||||
let expandedSpans: Set<string> = $state(new Set())
|
||||
|
||||
$effect(() => {
|
||||
if (jobId) {
|
||||
loadTraces()
|
||||
}
|
||||
})
|
||||
|
||||
async function loadTraces() {
|
||||
if (!$workspaceStore || !jobId) return
|
||||
|
||||
loading = true
|
||||
error = null
|
||||
|
||||
try {
|
||||
const response = await JobService.getJobOtelTraces({
|
||||
workspace: $workspaceStore,
|
||||
id: jobId
|
||||
})
|
||||
traces = response as unknown as OtelSpan[]
|
||||
} catch (e: any) {
|
||||
if (e?.status === 404) {
|
||||
traces = []
|
||||
} else {
|
||||
error = `Error loading traces: ${e?.message ?? e}`
|
||||
}
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
function getDurationMs(span: OtelSpan): number {
|
||||
return (span.end_time_unix_nano - span.start_time_unix_nano) / 1_000_000
|
||||
}
|
||||
|
||||
function formatTimestamp(ns: number): string {
|
||||
const date = new Date(ns / 1000000)
|
||||
return date.toISOString()
|
||||
}
|
||||
|
||||
function getStatusColor(statusCode: number): string {
|
||||
switch (statusCode) {
|
||||
case StatusCode.UNSET:
|
||||
return 'text-secondary'
|
||||
case StatusCode.OK:
|
||||
return 'text-green-600'
|
||||
case StatusCode.ERROR:
|
||||
return 'text-red-600'
|
||||
default:
|
||||
return 'text-secondary'
|
||||
}
|
||||
}
|
||||
|
||||
function getStatusLabel(statusCode: number): string {
|
||||
switch (statusCode) {
|
||||
case StatusCode.UNSET:
|
||||
return 'Unset'
|
||||
case StatusCode.OK:
|
||||
return 'OK'
|
||||
case StatusCode.ERROR:
|
||||
return 'Error'
|
||||
default:
|
||||
return 'Unknown'
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSpan(spanId: string) {
|
||||
const newSet = new Set(expandedSpans)
|
||||
if (newSet.has(spanId)) {
|
||||
newSet.delete(spanId)
|
||||
} else {
|
||||
newSet.add(spanId)
|
||||
}
|
||||
expandedSpans = newSet
|
||||
}
|
||||
|
||||
function getKindLabel(kind: number): string {
|
||||
switch (kind) {
|
||||
case SpanKind.INTERNAL: return 'Internal'
|
||||
case SpanKind.SERVER: return 'Server'
|
||||
case SpanKind.CLIENT: return 'Client'
|
||||
case SpanKind.PRODUCER: return 'Producer'
|
||||
case SpanKind.CONSUMER: return 'Consumer'
|
||||
default: return 'Unknown'
|
||||
}
|
||||
}
|
||||
|
||||
// Parse OTEL proto AnyValue to a simple value
|
||||
function parseAnyValue(anyValue: any): any {
|
||||
if (!anyValue) return null
|
||||
if (anyValue.stringValue !== undefined) return anyValue.stringValue
|
||||
if (anyValue.intValue !== undefined) return anyValue.intValue
|
||||
if (anyValue.boolValue !== undefined) return anyValue.boolValue
|
||||
if (anyValue.doubleValue !== undefined) return anyValue.doubleValue
|
||||
if (anyValue.arrayValue?.values) {
|
||||
return anyValue.arrayValue.values.map(parseAnyValue)
|
||||
}
|
||||
if (anyValue.kvlistValue?.values) {
|
||||
return parseAttributes(anyValue.kvlistValue.values)
|
||||
}
|
||||
return JSON.stringify(anyValue)
|
||||
}
|
||||
|
||||
// Parse OTEL proto attributes array to a simple key-value object
|
||||
function parseAttributes(attributes: any): Record<string, any> {
|
||||
if (!attributes) return {}
|
||||
if (!Array.isArray(attributes)) return attributes // already parsed
|
||||
const result: Record<string, any> = {}
|
||||
for (const attr of attributes) {
|
||||
if (attr.key && attr.value !== undefined) {
|
||||
result[attr.key] = parseAnyValue(attr.value)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Calculate timeline metrics
|
||||
function getTimelineMetrics(spans: OtelSpan[]) {
|
||||
if (spans.length === 0) return { minTime: 0, maxTime: 0, totalDuration: 0 }
|
||||
const minTime = Math.min(...spans.map((s) => s.start_time_unix_nano))
|
||||
const maxTime = Math.max(...spans.map((s) => s.end_time_unix_nano))
|
||||
return { minTime, maxTime, totalDuration: maxTime - minTime }
|
||||
}
|
||||
|
||||
let timelineMetrics = $derived(getTimelineMetrics(traces))
|
||||
</script>
|
||||
|
||||
<div class="p-4">
|
||||
{#if loading}
|
||||
<Skeleton layout={[[4], [8], [6], [10]]} />
|
||||
{:else if error}
|
||||
<Alert type="error" title="Error">{error}</Alert>
|
||||
{:else if traces.length === 0}
|
||||
<div class="flex flex-col items-center justify-center py-8 text-secondary">
|
||||
<Activity size={48} class="mb-4 opacity-50" />
|
||||
<p class="text-lg font-medium">No HTTP requests captured</p>
|
||||
<p class="text-sm mt-2">
|
||||
This job did not make any HTTP/HTTPS requests, or HTTP Request Tracing is not enabled in instance settings.
|
||||
</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-lg font-semibold">Traces ({traces.length} spans)</h3>
|
||||
<button
|
||||
class="text-sm text-blue-600 hover:underline"
|
||||
onclick={loadTraces}
|
||||
>
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Timeline view -->
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<div class="bg-surface-secondary px-4 py-2 border-b">
|
||||
<div class="grid grid-cols-12 gap-2 text-xs font-medium text-secondary">
|
||||
<div class="col-span-4">Operation</div>
|
||||
<div class="col-span-1">Status</div>
|
||||
<div class="col-span-5">Timeline</div>
|
||||
<div class="col-span-2 text-right">Duration</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divide-y">
|
||||
{#each traces as span (span.span_id)}
|
||||
{@const durationNs = span.end_time_unix_nano - span.start_time_unix_nano}
|
||||
{@const startOffset =
|
||||
((span.start_time_unix_nano - timelineMetrics.minTime) /
|
||||
timelineMetrics.totalDuration) *
|
||||
100}
|
||||
{@const width = (durationNs / timelineMetrics.totalDuration) * 100}
|
||||
{@const statusCode = span.status?.code ?? 0}
|
||||
|
||||
<div class="hover:bg-surface-hover">
|
||||
<button
|
||||
class="w-full px-4 py-2 text-left"
|
||||
onclick={() => toggleSpan(span.span_id)}
|
||||
>
|
||||
<div class="grid grid-cols-12 gap-2 items-center">
|
||||
<div class="col-span-4 flex items-center gap-2">
|
||||
<span class="text-xs text-secondary">
|
||||
{expandedSpans.has(span.span_id) ? '▼' : '▶'}
|
||||
</span>
|
||||
<span class="font-medium truncate" title={span.name}>
|
||||
{span.name}
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<span class={`text-xs font-medium ${getStatusColor(statusCode)}`}>
|
||||
{getStatusLabel(statusCode)}
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-span-5 relative h-4">
|
||||
<div class="absolute inset-0 bg-surface-secondary rounded"></div>
|
||||
<div
|
||||
class="absolute h-full bg-blue-500 rounded opacity-75"
|
||||
style="left: {startOffset}%; width: {Math.max(width, 0.5)}%;"
|
||||
></div>
|
||||
</div>
|
||||
<div class="col-span-2 text-right text-sm font-mono">
|
||||
{msToReadableTime(getDurationMs(span))}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{#if expandedSpans.has(span.span_id)}
|
||||
{@const parsedAttrs = parseAttributes(span.attributes)}
|
||||
<div class="px-4 pb-4 bg-surface-secondary/50">
|
||||
<div class="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<p class="text-xs text-secondary mb-1">Trace ID</p>
|
||||
<p class="font-mono text-xs break-all">{span.trace_id}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs text-secondary mb-1">Span ID</p>
|
||||
<p class="font-mono text-xs">{span.span_id}</p>
|
||||
</div>
|
||||
{#if span.parent_span_id}
|
||||
<div>
|
||||
<p class="text-xs text-secondary mb-1">Parent Span ID</p>
|
||||
<p class="font-mono text-xs">{span.parent_span_id}</p>
|
||||
</div>
|
||||
{/if}
|
||||
<div>
|
||||
<p class="text-xs text-secondary mb-1">Kind</p>
|
||||
<p class="font-mono text-xs">{getKindLabel(span.kind)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs text-secondary mb-1">Start Time</p>
|
||||
<p class="font-mono text-xs">{formatTimestamp(span.start_time_unix_nano)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs text-secondary mb-1">End Time</p>
|
||||
<p class="font-mono text-xs">{formatTimestamp(span.end_time_unix_nano)}</p>
|
||||
</div>
|
||||
{#if span.status?.message}
|
||||
<div class="col-span-2">
|
||||
<p class="text-xs text-secondary mb-1">Status Message</p>
|
||||
<p class="text-xs">{span.status.message}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if Object.keys(parsedAttrs).length > 0}
|
||||
<div class="col-span-2">
|
||||
<p class="text-xs text-secondary mb-1">Attributes</p>
|
||||
<div class="bg-surface-secondary p-2 rounded text-xs space-y-1">
|
||||
{#each Object.entries(parsedAttrs) as [key, value]}
|
||||
<div class="flex gap-2">
|
||||
<span class="text-secondary font-medium shrink-0">{key}:</span>
|
||||
<span class="font-mono break-all">{typeof value === 'object' ? JSON.stringify(value) : value}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,5 +1,18 @@
|
||||
import type { ButtonType } from './common/button/model'
|
||||
|
||||
// Languages that support HTTP request tracing via OTEL proxy
|
||||
export const OTEL_TRACING_PROXY_LANGUAGES = [
|
||||
'python3',
|
||||
'deno',
|
||||
'bun',
|
||||
'go',
|
||||
'bash',
|
||||
'rust',
|
||||
'csharp',
|
||||
'nu',
|
||||
'ruby'
|
||||
] as const
|
||||
|
||||
export interface Setting {
|
||||
label: string
|
||||
description?: string
|
||||
@@ -34,6 +47,7 @@ export interface Setting {
|
||||
| 'smtp_connect'
|
||||
| 'indexer_rates'
|
||||
| 'otel'
|
||||
| 'otel_tracing_proxy'
|
||||
| 'secret_backend'
|
||||
storage: SettingStorage
|
||||
advancedToggle?: {
|
||||
@@ -446,7 +460,16 @@ export const settings: Record<string, Setting[]> = {
|
||||
storage: 'setting',
|
||||
ee_only: ''
|
||||
},
|
||||
|
||||
{
|
||||
label: 'HTTP Request Tracing',
|
||||
description:
|
||||
'Capture HTTP/HTTPS requests from job scripts as OpenTelemetry spans. Visible in job details and exported to your OTEL collector if configured. Toggling restarts workers.',
|
||||
key: 'otel_tracing_proxy',
|
||||
fieldType: 'otel_tracing_proxy',
|
||||
storage: 'setting',
|
||||
ee_only: 'HTTP Request Tracing is an EE feature',
|
||||
defaultValue: () => ({ enabled: false, enabled_languages: [...OTEL_TRACING_PROXY_LANGUAGES] })
|
||||
},
|
||||
{
|
||||
label: 'Prometheus',
|
||||
description:
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
import type Editor from '../Editor.svelte'
|
||||
import type DiffEditor from '../DiffEditor.svelte'
|
||||
import ScriptFix from '../copilot/ScriptFix.svelte'
|
||||
import JobOtelTraces from '../JobOtelTraces.svelte'
|
||||
import Cell from '../table/Cell.svelte'
|
||||
import DataTable from '../table/DataTable.svelte'
|
||||
import Head from '../table/Head.svelte'
|
||||
@@ -127,6 +128,7 @@
|
||||
{#if showCaptures && customUi?.disableTriggerCaptures !== true}
|
||||
<Tab value="captures" label="Trigger captures" />
|
||||
{/if}
|
||||
<Tab value="tracing" label="Tracing" />
|
||||
|
||||
{#snippet content()}
|
||||
<div class="grow min-h-0">
|
||||
@@ -298,6 +300,15 @@
|
||||
{#if selectedTab === 'captures'}
|
||||
{@render capturesTab?.()}
|
||||
{/if}
|
||||
{#if selectedTab === 'tracing'}
|
||||
{#if previewJob?.id}
|
||||
<JobOtelTraces jobId={previewJob.id} />
|
||||
{:else}
|
||||
<div class="p-4 text-secondary">
|
||||
Run a preview to see HTTP request traces
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
</Tabs>
|
||||
|
||||
@@ -91,13 +91,14 @@
|
||||
import RunBadges from '$lib/components/runs/RunBadges.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import FlowRestartButton from '$lib/components/FlowRestartButton.svelte'
|
||||
import JobOtelTraces from '$lib/components/JobOtelTraces.svelte'
|
||||
let job: (Job & { result?: any; result_stream?: string }) | undefined = $state()
|
||||
let jobUpdateLastFetch: Date | undefined = $state()
|
||||
|
||||
let scriptProgress: number | undefined = $state(undefined)
|
||||
let currentJobIsLongRunning: boolean = $state(false)
|
||||
|
||||
let viewTab: 'result' | 'logs' | 'code' | 'stats' | 'assets' = $state('result')
|
||||
let viewTab: 'result' | 'logs' | 'code' | 'stats' | 'assets' | 'traces' = $state('result')
|
||||
let selectedJobStep: string | undefined = $state(undefined)
|
||||
|
||||
let selectedJobStepIsTopLevel: boolean | undefined = $state(undefined)
|
||||
@@ -770,6 +771,7 @@
|
||||
<Tab value="result" label="Result" />
|
||||
<Tab value="logs" label="Logs" />
|
||||
<Tab value="stats" label="Metrics" />
|
||||
<Tab value="traces" label="Traces" />
|
||||
<Tab value="assets" label="Assets" />
|
||||
{#if isScriptPreview(job?.job_kind)}
|
||||
<Tab value="code" label="Code" />
|
||||
@@ -799,6 +801,10 @@
|
||||
<div class="w-full">
|
||||
<JobAssetsViewer {job} />
|
||||
</div>
|
||||
{:else if viewTab == 'traces'}
|
||||
<div class="w-full">
|
||||
<JobOtelTraces jobId={job.id} />
|
||||
</div>
|
||||
{:else if viewTab == 'code'}
|
||||
{#if job && 'raw_code' in job && job.raw_code}
|
||||
<div class="text-xs">
|
||||
|
||||
Reference in New Issue
Block a user