]> git.puffer.fish Git - matthieu/nova.git/commitdiff
base for tracing 35/head
authorMatthieuCoder <matthieu@matthieu-dev.xyz>
Thu, 5 Jan 2023 14:33:53 +0000 (18:33 +0400)
committerMatthieuCoder <matthieu@matthieu-dev.xyz>
Thu, 5 Jan 2023 14:33:53 +0000 (18:33 +0400)
61 files changed:
.env [new file with mode: 0644]
.gitignore
Cargo.lock
docker-compose.yaml
exes/all/Cargo.toml
exes/all/build.rs
exes/all/src/lib.rs
exes/cache/Cargo.toml
exes/cache/src/config.rs
exes/cache/src/main.rs
exes/cache/src/managers/channels.rs
exes/cache/src/managers/guild_schedules.rs
exes/cache/src/managers/guilds.rs
exes/cache/src/managers/integrations.rs
exes/cache/src/managers/invites.rs
exes/cache/src/managers/members.rs
exes/cache/src/managers/messages.rs
exes/cache/src/managers/mod.rs
exes/cache/src/managers/reactions.rs
exes/cache/src/managers/roles.rs
exes/cache/src/managers/stage_instances.rs
exes/cache/src/managers/threads.rs
exes/gateway/Cargo.toml
exes/gateway/src/config.rs
exes/gateway/src/lib.rs
exes/gateway/src/main.rs
exes/ratelimit/Cargo.toml
exes/ratelimit/src/grpc.rs
exes/ratelimit/src/lib.rs
exes/ratelimit/src/redis_global_local_bucket_ratelimiter/bucket.rs
exes/ratelimit/src/redis_global_local_bucket_ratelimiter/mod.rs
exes/rest/Cargo.toml
exes/rest/src/config.rs
exes/rest/src/handler.rs
exes/rest/src/lib.rs
exes/rest/src/ratelimit_client/mod.rs
exes/rest/src/ratelimit_client/remote_hashring.rs
exes/webhook/Cargo.toml
exes/webhook/src/config.rs
exes/webhook/src/handler/mod.rs
exes/webhook/src/handler/signature.rs
exes/webhook/src/handler/tests/handler.rs
exes/webhook/src/handler/tests/mod.rs
exes/webhook/src/lib.rs
libs/leash/Cargo.toml
libs/leash/src/lib.rs
libs/shared/Cargo.toml
libs/shared/src/config.rs
libs/shared/src/error.rs
libs/shared/src/lib.rs
libs/shared/src/monitoring.rs [deleted file]
libs/shared/src/payloads.rs
otel/grafana/grafana.ini [new file with mode: 0644]
otel/grafana/provisioning/dashboards/general.yaml [new file with mode: 0644]
otel/grafana/provisioning/dashboards/general/demo-dashboard.json [new file with mode: 0644]
otel/grafana/provisioning/dashboards/general/opentelemetry-collector.json [new file with mode: 0644]
otel/grafana/provisioning/datasources/default.yaml [new file with mode: 0644]
otel/grafana/provisioning/datasources/jaeger.yaml [new file with mode: 0644]
otel/otelcollector/otelcol-config-extras.yml [new file with mode: 0644]
otel/otelcollector/otelcol-config.yml [new file with mode: 0644]
otel/prometheus/prometheus-config.yaml [new file with mode: 0644]

diff --git a/.env b/.env
new file mode 100644 (file)
index 0000000..156276e
--- /dev/null
+++ b/.env
@@ -0,0 +1,11 @@
+GRAFANA_SERVICE_PORT=3000
+GRAFANA_SERVICE_HOST=grafana
+
+# Jaeger
+JAEGER_SERVICE_PORT=16686
+JAEGER_SERVICE_HOST=jaeger
+
+# Prometheus
+PROMETHEUS_SERVICE_PORT=9090
+PROMETHEUS_SERVICE_HOST=prometheus
+PROMETHEUS_ADDR=${PROMETHEUS_SERVICE_HOST}:${PROMETHEUS_SERVICE_PORT}
\ No newline at end of file
index cc5c15c7e7a097a91fb6a510b7785c81abab2b11..6c9334c78b0864528bd02b04ca78a5d24a0502f0 100644 (file)
@@ -4,3 +4,5 @@ target/
 .ijwb\r
 .idea\r
 config.yml\r
+\r
+config/
\ No newline at end of file
index 025545f63a62eda7cc107895d237e84428251a55..3d47207552b652ea3b758faf564bfdd0dcbe481d 100644 (file)
@@ -39,25 +39,20 @@ dependencies = [
  "gateway",
  "leash",
  "libc",
- "pretty_env_logger",
+ "opentelemetry",
+ "opentelemetry-otlp",
  "ratelimit",
  "rest",
  "serde",
  "serde_json",
  "shared",
  "tokio",
+ "tracing",
+ "tracing-opentelemetry",
+ "tracing-subscriber",
  "webhook",
 ]
 
-[[package]]
-name = "android_system_properties"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
-dependencies = [
- "libc",
-]
-
 [[package]]
 name = "anyhow"
 version = "1.0.68"
@@ -96,7 +91,7 @@ dependencies = [
  "serde_nanos",
  "serde_repr",
  "subslice",
- "time 0.3.17",
+ "time",
  "tokio",
  "tokio-retry",
  "tokio-rustls",
@@ -244,17 +239,6 @@ dependencies = [
  "generic-array",
 ]
 
-[[package]]
-name = "bollard-stubs"
-version = "1.41.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed2f2e73fffe9455141e170fb9c1feb0ac521ec7e7dcd47a7cab72a658490fb8"
-dependencies = [
- "chrono",
- "serde",
- "serde_with",
-]
-
 [[package]]
 name = "bumpalo"
 version = "3.11.1"
@@ -279,14 +263,13 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "async-nats",
- "futures-util",
- "log",
  "proto",
- "redis",
  "serde",
  "serde_json",
  "shared",
  "tokio",
+ "tokio-stream",
+ "tracing",
  "twilight-model",
 ]
 
@@ -321,22 +304,6 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
-[[package]]
-name = "chrono"
-version = "0.4.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
-dependencies = [
- "iana-time-zone",
- "js-sys",
- "num-integer",
- "num-traits",
- "serde",
- "time 0.1.45",
- "wasm-bindgen",
- "winapi",
-]
-
 [[package]]
 name = "clap"
 version = "3.2.23"
@@ -361,16 +328,6 @@ dependencies = [
  "os_str_bytes",
 ]
 
-[[package]]
-name = "codespan-reporting"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
-dependencies = [
- "termcolor",
- "unicode-width",
-]
-
 [[package]]
 name = "combine"
 version = "4.6.6"
@@ -450,6 +407,25 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
+dependencies = [
+ "cfg-if",
+]
+
 [[package]]
 name = "crypto-common"
 version = "0.1.6"
@@ -474,82 +450,16 @@ dependencies = [
 ]
 
 [[package]]
-name = "cxx"
-version = "1.0.85"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd"
-dependencies = [
- "cc",
- "cxxbridge-flags",
- "cxxbridge-macro",
- "link-cplusplus",
-]
-
-[[package]]
-name = "cxx-build"
-version = "1.0.85"
+name = "dashmap"
+version = "5.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0"
+checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
 dependencies = [
- "cc",
- "codespan-reporting",
+ "cfg-if",
+ "hashbrown",
+ "lock_api",
  "once_cell",
- "proc-macro2",
- "quote",
- "scratch",
- "syn",
-]
-
-[[package]]
-name = "cxxbridge-flags"
-version = "1.0.85"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59"
-
-[[package]]
-name = "cxxbridge-macro"
-version = "1.0.85"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "darling"
-version = "0.13.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
-dependencies = [
- "darling_core",
- "darling_macro",
-]
-
-[[package]]
-name = "darling_core"
-version = "0.13.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
-dependencies = [
- "fnv",
- "ident_case",
- "proc-macro2",
- "quote",
- "strsim",
- "syn",
-]
-
-[[package]]
-name = "darling_macro"
-version = "0.13.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
-dependencies = [
- "darling_core",
- "quote",
- "syn",
+ "parking_lot_core",
 ]
 
 [[package]]
@@ -584,7 +494,6 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
 dependencies = [
  "block-buffer 0.10.3",
  "crypto-common",
- "subtle",
 ]
 
 [[package]]
@@ -624,7 +533,7 @@ dependencies = [
  "ed25519",
  "rand 0.7.3",
  "serde",
- "sha2 0.9.9",
+ "sha2",
  "zeroize",
 ]
 
@@ -635,34 +544,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
 
 [[package]]
-name = "enumflags2"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb"
-dependencies = [
- "enumflags2_derive",
- "serde",
-]
-
-[[package]]
-name = "enumflags2_derive"
-version = "0.7.4"
+name = "env_logger"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae"
+checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "atty",
+ "humantime 1.3.0",
+ "log",
+ "regex",
+ "termcolor",
 ]
 
 [[package]]
 name = "env_logger"
-version = "0.7.1"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
+checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
 dependencies = [
- "atty",
- "humantime",
+ "humantime 2.1.0",
+ "is-terminal",
  "log",
  "regex",
  "termcolor",
@@ -839,14 +740,20 @@ name = "gateway"
 version = "0.1.0"
 dependencies = [
  "anyhow",
+ "async-nats",
  "bytes",
- "futures",
  "leash",
+ "opentelemetry",
+ "opentelemetry-http",
  "proto",
  "serde",
  "serde_json",
  "shared",
  "tokio",
+ "tokio-stream",
+ "tracing",
+ "tracing-futures",
+ "tracing-opentelemetry",
  "twilight-gateway",
  "twilight-model",
 ]
@@ -956,15 +863,6 @@ version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
 
-[[package]]
-name = "hmac"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
-dependencies = [
- "digest 0.10.6",
-]
-
 [[package]]
 name = "http"
 version = "0.2.8"
@@ -1014,6 +912,12 @@ dependencies = [
  "quick-error",
 ]
 
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
 [[package]]
 name = "hyper"
 version = "0.14.23"
@@ -1077,36 +981,6 @@ dependencies = [
  "tokio-native-tls",
 ]
 
-[[package]]
-name = "iana-time-zone"
-version = "0.1.53"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
-dependencies = [
- "android_system_properties",
- "core-foundation-sys",
- "iana-time-zone-haiku",
- "js-sys",
- "wasm-bindgen",
- "winapi",
-]
-
-[[package]]
-name = "iana-time-zone-haiku"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
-dependencies = [
- "cxx",
- "cxx-build",
-]
-
-[[package]]
-name = "ident_case"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
-
 [[package]]
 name = "idna"
 version = "0.3.0"
@@ -1127,12 +1001,6 @@ dependencies = [
  "hashbrown",
 ]
 
-[[package]]
-name = "inner"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9368e93322f271c5ca078ed2ddcfad3511f1a40f564e522ade34e6e5c8e6680"
-
 [[package]]
 name = "instant"
 version = "0.1.12"
@@ -1152,6 +1020,18 @@ dependencies = [
  "windows-sys 0.42.0",
 ]
 
+[[package]]
+name = "is-terminal"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
+dependencies = [
+ "hermit-abi 0.2.6",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys 0.42.0",
+]
+
 [[package]]
 name = "itertools"
 version = "0.10.5"
@@ -1207,10 +1087,16 @@ name = "leash"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "pretty_env_logger",
+ "env_logger 0.10.0",
+ "opentelemetry",
+ "opentelemetry-otlp",
  "serde",
  "shared",
  "tokio",
+ "tracing",
+ "tracing-log",
+ "tracing-opentelemetry",
+ "tracing-subscriber",
 ]
 
 [[package]]
@@ -1230,15 +1116,6 @@ dependencies = [
  "vcpkg",
 ]
 
-[[package]]
-name = "link-cplusplus"
-version = "1.0.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
-dependencies = [
- "cc",
-]
-
 [[package]]
 name = "linked-hash-map"
 version = "0.5.6"
@@ -1270,6 +1147,15 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata",
+]
+
 [[package]]
 name = "matchit"
 version = "0.7.0"
@@ -1365,23 +1251,23 @@ dependencies = [
 ]
 
 [[package]]
-name = "nuid"
-version = "0.3.2"
+name = "nu-ansi-term"
+version = "0.46.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20c1bb65186718d348306bf1afdeb20d9ab45b2ab80fb793c0fdcf59ffbb4f38"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
 dependencies = [
- "lazy_static",
- "rand 0.8.5",
+ "overload",
+ "winapi",
 ]
 
 [[package]]
-name = "num-integer"
-version = "0.1.45"
+name = "nuid"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+checksum = "20c1bb65186718d348306bf1afdeb20d9ab45b2ab80fb793c0fdcf59ffbb4f38"
 dependencies = [
- "autocfg",
- "num-traits",
+ "lazy_static",
+ "rand 0.8.5",
 ]
 
 [[package]]
@@ -1460,6 +1346,98 @@ dependencies = [
  "vcpkg",
 ]
 
+[[package]]
+name = "opentelemetry"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69d6c3d7288a106c0a363e4b0e8d308058d56902adefb16f4936f417ffef086e"
+dependencies = [
+ "opentelemetry_api",
+ "opentelemetry_sdk",
+]
+
+[[package]]
+name = "opentelemetry-http"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1edc79add46364183ece1a4542592ca593e6421c60807232f5b8f7a31703825d"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "http",
+ "opentelemetry_api",
+]
+
+[[package]]
+name = "opentelemetry-otlp"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1c928609d087790fc936a1067bdc310ae702bdf3b090c3f281b713622c8bbde"
+dependencies = [
+ "async-trait",
+ "futures",
+ "futures-util",
+ "http",
+ "opentelemetry",
+ "opentelemetry-proto",
+ "prost",
+ "thiserror",
+ "tokio",
+ "tonic",
+]
+
+[[package]]
+name = "opentelemetry-proto"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d61a2f56df5574508dd86aaca016c917489e589ece4141df1b5e349af8d66c28"
+dependencies = [
+ "futures",
+ "futures-util",
+ "opentelemetry",
+ "prost",
+ "tonic",
+ "tonic-build",
+]
+
+[[package]]
+name = "opentelemetry_api"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c24f96e21e7acc813c7a8394ee94978929db2bcc46cf6b5014fc612bf7760c22"
+dependencies = [
+ "fnv",
+ "futures-channel",
+ "futures-util",
+ "indexmap",
+ "js-sys",
+ "once_cell",
+ "pin-project-lite",
+ "thiserror",
+]
+
+[[package]]
+name = "opentelemetry_sdk"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ca41c4933371b61c2a2f214bf16931499af4ec90543604ec828f7a625c09113"
+dependencies = [
+ "async-trait",
+ "crossbeam-channel",
+ "dashmap",
+ "fnv",
+ "futures-channel",
+ "futures-executor",
+ "futures-util",
+ "once_cell",
+ "opentelemetry_api",
+ "percent-encoding",
+ "rand 0.8.5",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+]
+
 [[package]]
 name = "ordered-float"
 version = "2.10.0"
@@ -1485,6 +1463,12 @@ version = "6.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
 
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
 [[package]]
 name = "parking_lot"
 version = "0.12.1"
@@ -1639,16 +1623,6 @@ version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
-[[package]]
-name = "pretty_env_logger"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
-dependencies = [
- "env_logger",
- "log",
-]
-
 [[package]]
 name = "prettyplease"
 version = "0.1.22"
@@ -1668,36 +1642,6 @@ dependencies = [
  "unicode-ident",
 ]
 
-[[package]]
-name = "procfs"
-version = "0.14.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1de8dacb0873f77e6aefc6d71e044761fcc68060290f5b1089fcdf84626bb69"
-dependencies = [
- "bitflags",
- "byteorder",
- "hex",
- "lazy_static",
- "rustix",
-]
-
-[[package]]
-name = "prometheus"
-version = "0.13.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c"
-dependencies = [
- "cfg-if",
- "fnv",
- "lazy_static",
- "libc",
- "memchr",
- "parking_lot",
- "procfs",
- "protobuf",
- "thiserror",
-]
-
 [[package]]
 name = "prost"
 version = "0.11.5"
@@ -1763,12 +1707,6 @@ dependencies = [
  "tonic-build",
 ]
 
-[[package]]
-name = "protobuf"
-version = "2.28.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94"
-
 [[package]]
 name = "quick-error"
 version = "1.2.3"
@@ -1860,17 +1798,19 @@ name = "ratelimit"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "futures-util",
  "hyper",
  "leash",
+ "opentelemetry",
+ "opentelemetry-http",
  "proto",
+ "redis",
  "serde",
- "serde_json",
  "shared",
  "tokio",
  "tokio-stream",
  "tonic",
  "tracing",
+ "tracing-opentelemetry",
  "twilight-http-ratelimiting 0.14.0 (git+https://github.com/MatthieuCoder/twilight.git)",
 ]
 
@@ -1918,6 +1858,15 @@ dependencies = [
  "regex-syntax",
 ]
 
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax",
+]
+
 [[package]]
 name = "regex-syntax"
 version = "0.6.28"
@@ -1939,24 +1888,22 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "dns-lookup",
- "futures-util",
  "hashring",
  "http",
  "hyper",
  "hyper-tls",
- "lazy_static",
  "leash",
+ "opentelemetry",
+ "opentelemetry-http",
  "proto",
  "serde",
- "serde_json",
  "shared",
  "tokio",
- "tokio-scoped",
  "tokio-stream",
  "tonic",
  "tracing",
+ "tracing-opentelemetry",
  "twilight-http-ratelimiting 0.14.0 (git+https://github.com/MatthieuCoder/twilight.git)",
- "xxhash-rust",
 ]
 
 [[package]]
@@ -2070,12 +2017,6 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
-[[package]]
-name = "scratch"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
-
 [[package]]
 name = "sct"
 version = "0.7.0"
@@ -2171,25 +2112,12 @@ dependencies = [
 ]
 
 [[package]]
-name = "serde_with"
-version = "1.14.0"
+name = "serde_test"
+version = "1.0.152"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff"
+checksum = "3611210d2d67e3513204742004d6ac6f589e521861dabb0f649b070eea8bed9e"
 dependencies = [
  "serde",
- "serde_with_macros",
-]
-
-[[package]]
-name = "serde_with_macros"
-version = "1.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
-dependencies = [
- "darling",
- "proc-macro2",
- "quote",
- "syn",
 ]
 
 [[package]]
@@ -2234,14 +2162,12 @@ dependencies = [
 ]
 
 [[package]]
-name = "sha2"
-version = "0.10.6"
+name = "sharded-slab"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
+checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
 dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest 0.10.6",
+ "lazy_static",
 ]
 
 [[package]]
@@ -2251,18 +2177,14 @@ dependencies = [
  "anyhow",
  "async-nats",
  "config",
- "enumflags2",
- "hyper",
- "inner",
- "log",
- "prometheus",
  "redis",
  "serde",
  "serde_json",
  "serde_repr",
- "testcontainers",
+ "serde_test",
  "thiserror",
  "tokio",
+ "tracing",
  "twilight-model",
 ]
 
@@ -2412,23 +2334,6 @@ dependencies = [
  "winapi-util",
 ]
 
-[[package]]
-name = "testcontainers"
-version = "0.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e2b1567ca8a2b819ea7b28c92be35d9f76fb9edb214321dcc86eb96023d1f87"
-dependencies = [
- "bollard-stubs",
- "futures",
- "hex",
- "hmac",
- "log",
- "rand 0.8.5",
- "serde",
- "serde_json",
- "sha2 0.10.6",
-]
-
 [[package]]
 name = "textwrap"
 version = "0.16.0"
@@ -2456,14 +2361,12 @@ dependencies = [
 ]
 
 [[package]]
-name = "time"
-version = "0.1.45"
+name = "thread_local"
+version = "1.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
+checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
 dependencies = [
- "libc",
- "wasi 0.10.0+wasi-snapshot-preview1",
- "winapi",
+ "once_cell",
 ]
 
 [[package]]
@@ -2581,16 +2484,6 @@ dependencies = [
  "webpki",
 ]
 
-[[package]]
-name = "tokio-scoped"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4beb8ba13bc53ac53ce1d52b42f02e5d8060f0f42138862869beb769722b256"
-dependencies = [
- "tokio",
- "tokio-stream",
-]
-
 [[package]]
 name = "tokio-stream"
 version = "0.1.11"
@@ -2768,6 +2661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
 dependencies = [
  "once_cell",
+ "valuable",
 ]
 
 [[package]]
@@ -2780,6 +2674,50 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "tracing-log"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
+dependencies = [
+ "env_logger 0.7.1",
+ "lazy_static",
+ "log",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-opentelemetry"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21ebb87a95ea13271332df069020513ab70bdb5637ca42d6e492dc3bbbad48de"
+dependencies = [
+ "once_cell",
+ "opentelemetry",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
 [[package]]
 name = "try-lock"
 version = "0.2.3"
@@ -2892,7 +2830,7 @@ dependencies = [
  "serde",
  "serde-value",
  "serde_repr",
- "time 0.3.17",
+ "time",
  "tracing",
 ]
 
@@ -2938,12 +2876,6 @@ dependencies = [
  "tinyvec",
 ]
 
-[[package]]
-name = "unicode-width"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
-
 [[package]]
 name = "unicode-xid"
 version = "0.2.4"
@@ -2973,6 +2905,12 @@ version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
 
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
 [[package]]
 name = "vcpkg"
 version = "0.2.15"
@@ -3001,12 +2939,6 @@ version = "0.9.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
 
-[[package]]
-name = "wasi"
-version = "0.10.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
-
 [[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
@@ -3082,17 +3014,17 @@ name = "webhook"
 version = "0.1.0"
 dependencies = [
  "anyhow",
+ "async-nats",
  "ed25519-dalek",
- "futures-util",
  "hex",
  "hyper",
- "lazy_static",
  "leash",
  "proto",
  "serde",
  "serde_json",
  "shared",
  "tokio",
+ "tracing",
  "twilight-model",
 ]
 
@@ -3248,12 +3180,6 @@ version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
 
-[[package]]
-name = "xxhash-rust"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "735a71d46c4d68d71d4b24d03fdc2b98e38cea81730595801db779c04fe80d70"
-
 [[package]]
 name = "yaml-rust"
 version = "0.4.5"
index 2472e45e8b4dd3888137690fbd8a083f03039685..6d5ccdb89b4dab557956565c79786b7ee1ce85ea 100644 (file)
@@ -6,6 +6,7 @@ services:
     ports:
       - 4222:4222
       - 8222:8222
+  
   redis:
     image: redis
 
@@ -17,12 +18,14 @@ services:
       args:
         - COMPONENT=cache
     volumes:
-      - ./config.yml:/config/default.yml
+      - ./config/default.yml:/config/default.yml
     environment:
-      - RUST_LOG=info
+      - RUST_LOG=debug
+      - OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4317
     depends_on:
       - nats
       - redis
+      - otelcol
   
   gateway:
     image: ghcr.io/discordnova/nova/gateway
@@ -32,13 +35,13 @@ services:
       args:
         - COMPONENT=gateway
     volumes:
-      - ./config.yml:/config/default.yml
+      - ./config/default.yml:/config/default.yml
     environment:
-      - RUST_LOG=info
+      - RUST_LOG=debug
+      - OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4317
     depends_on:
       - nats
-    ports:
-      - 9000:9000
+      - otelcol
 
   rest:
     image: ghcr.io/discordnova/nova/rest
@@ -48,14 +51,16 @@ services:
       args:
         - COMPONENT=rest
     volumes:
-      - ./config.yml:/config/default.yml
+      - ./config/default.yml:/config/default.yml
     environment:
-      - RUST_LOG=info
+      - RUST_LOG=debug
+      - OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4317
     depends_on:
       - ratelimit
+      - otelcol
     ports:
       - 9001:9000
-      - 8080:8080
+      - 8090:8090
 
   webhook:
     image: ghcr.io/discordnova/nova/webhook
@@ -63,16 +68,19 @@ services:
     build: 
       context: .
       args:
+        - RUST_LOG=debug
         - COMPONENT=webhook
     volumes:
-      - ./config.yml:/config/default.yml
+      - ./config/default.yml:/config/default.yml
     environment:
-      - RUST_LOG=info
+      - RUST_LOG=debug
+      - OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4317
     depends_on:
       - nats
+      - otelcol
     ports:
       - 9002:9000
-      - 8081:8080
+      - 8091:8091
   ratelimit:
     image: ghcr.io/discordnova/nova/ratelimit
     restart: always
@@ -81,12 +89,84 @@ services:
       args:
         - COMPONENT=ratelimit
     volumes:
-      - ./config.yml:/config/default.yml
+      - ./config/default.yml:/config/default.yml
     environment:
-      - RUST_LOG=info
+      - RUST_LOG=debug
+      - OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4317
     depends_on:
       - nats
       - redis
+      - otelcol
+
+  # ********************
+  # Telemetry Components
+  # ********************
+  # Jaeger
+  jaeger:
+    image: jaegertracing/all-in-one
+    container_name: jaeger
+    command:
+      - "--memory.max-debugs"
+      - "10000"
+      - "--query.base-path"
+      - "/jaeger/ui"
+      - "--prometheus.server-url"
+      - "http://${PROMETHEUS_ADDR}"
+    deploy:
+      resources:
+        limits:
+          memory: 275M
+    restart: always
+    ports:
+      - "${JAEGER_SERVICE_PORT}:${JAEGER_SERVICE_PORT}"                    # Jaeger UI
+      - "4317"                           # OTLP gRPC default port
+    environment:
+      - COLLECTOR_OTLP_ENABLED=true
+      - METRICS_STORAGE_TYPE=prometheus
+
+  # Grafana
+  grafana:
+    image: grafana/grafana:9.1.0
+    container_name: grafana
+    volumes:
+      - ./otel/grafana/grafana.ini:/etc/grafana/grafana.ini
+      - ./otel/grafana/provisioning/:/etc/grafana/provisioning/
+    ports:
+      - "${GRAFANA_SERVICE_PORT}:${GRAFANA_SERVICE_PORT}"
+
+  # OpenTelemetry Collector
+  otelcol:
+    image: otel/opentelemetry-collector-contrib:0.61.0
+    deploy:
+      resources:
+        limits:
+          memory: 100M
+    restart: always
+    command: [ "--config=/etc/otelcol-config.yml", "--config=/etc/otelcol-config-extras.yml" ]
+    volumes:
+      - ./otel/otelcollector/otelcol-config.yml:/etc/otelcol-config.yml
+      - ./otel/otelcollector/otelcol-config-extras.yml:/etc/otelcol-config-extras.yml
+    ports:
+      - "4317:4317"          # OTLP over gRPC receiver
+      - "4318:4318"     # OTLP over HTTP receiver
+      - "9464"          # Prometheus exporter
+      - "8888"          # metrics endpoint
+    depends_on:
+      - jaeger
+
+  # Prometheus
+  prometheus:
+    image: quay.io/prometheus/prometheus:v2.34.0
+    container_name: prometheus
+    command:
+      - --web.console.templates=/etc/prometheus/consoles
+      - --web.console.libraries=/etc/prometheus/console_libraries
+      - --storage.tsdb.retention.time=1h
+      - --config.file=/etc/prometheus/prometheus-config.yaml
+      - --storage.tsdb.path=/prometheus
+      - --web.enable-lifecycle
+      - --web.route-prefix=/
+    volumes:
+      - ./otel/prometheus/prometheus-config.yaml:/etc/prometheus/prometheus-config.yaml
     ports:
-      - 9003:9000
-      - 8082:8080
\ No newline at end of file
+      - "${PROMETHEUS_SERVICE_PORT}:${PROMETHEUS_SERVICE_PORT}"
index 41396fcf2e59b6f6bd9bc1b52aac4c08ef934885..26e8d044ccfcbcd230b5fa3b76b923c09541299d 100644 (file)
@@ -16,13 +16,19 @@ ratelimit = { path = "../ratelimit" }
 rest = { path = "../rest" }
 webhook = { path = "../webhook" }
 
-tokio = { version = "1.23.0", features = ["full"] }
+tokio = { version = "1.23.0", features = ["rt"] }
 serde = "1.0.152"
 serde_json = "1.0.91"
 anyhow = "1.0.68"
 
+tracing = "0.1.37"
+
 config = "0.13.3"
-pretty_env_logger = "0.4.0"
+
+tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
+tracing-opentelemetry = "0.18.0"
+opentelemetry = { version ="0.18.0", features = ["rt-tokio"] }
+opentelemetry-otlp = { version = "0.11.0" }
 
 [lib]
 crate-type = ["staticlib"]
index 192dffd960189411d28077e9b3f9327cdc71d588..00d7afcb08d2a51ef37ebd91cfc9aeb17dfea513 100644 (file)
@@ -1,9 +1,8 @@
 extern crate cbindgen;
 
+use cbindgen::{Config, Language};
 use std::env;
 use std::path::PathBuf;
-use cbindgen::{Config, Language};
-
 
 fn main() {
     let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
@@ -20,6 +19,6 @@ fn main() {
     };
 
     cbindgen::generate_with_config(crate_dir, config)
-      .unwrap()
-      .write_to_file(output_file);
+        .unwrap()
+        .write_to_file(output_file);
 }
index de83243b5863176670a731d5059abfb2272f62d3..1c991095b7234a61c29433893ea4bd3cc6609287 100644 (file)
@@ -5,14 +5,21 @@ use anyhow::Result;
 use config::{Config, Environment, File};
 use gateway::GatewayServer;
 use leash::Component;
+use opentelemetry::{
+    global,
+    sdk::{propagation::TraceContextPropagator, trace, Resource},
+    KeyValue,
+};
+use opentelemetry_otlp::WithExportConfig;
 use ratelimit::RatelimiterServerComponent;
 use rest::ReverseProxyServer;
 use serde::de::DeserializeOwned;
 use serde_json::Value;
-use shared::{config::Settings, log::info};
+use shared::config::Settings;
 use std::{
     env,
     ffi::{CStr, CString},
+    str::FromStr,
     time::Duration,
 };
 use tokio::{
@@ -20,6 +27,11 @@ use tokio::{
     sync::oneshot::{self, Sender},
     task::JoinHandle,
 };
+use tracing::info;
+use tracing_subscriber::{
+    filter::Directive, fmt, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt,
+    EnvFilter,
+};
 use webhook::WebhookServer;
 
 pub struct AllInOneInstance {
@@ -83,9 +95,7 @@ pub extern "C" fn load_config() -> *const libc::c_char {
 #[no_mangle]
 /// Initialise les logs des composants de nova
 /// Utilise la crate `pretty_log_env`
-pub extern "C" fn init_logs() {
-    pretty_env_logger::init();
-}
+pub extern "C" fn init_logs() {}
 
 #[no_mangle]
 /// Stops a nova instance
@@ -108,6 +118,29 @@ pub unsafe extern "C" fn start_instance(config: *const libc::c_char) -> *mut All
     // Initialize a tokio runtime
     let rt = Runtime::new().unwrap();
     rt.block_on(async move {
+        global::set_text_map_propagator(TraceContextPropagator::new());
+        let tracer =
+            opentelemetry_otlp::new_pipeline()
+                .tracing()
+                .with_trace_config(trace::config().with_resource(Resource::new(vec![
+                    KeyValue::new("service.name", "all-in-one"),
+                ])))
+                .with_exporter(opentelemetry_otlp::new_exporter().tonic().with_env())
+                .install_batch(opentelemetry::runtime::Tokio)
+                .unwrap();
+
+        let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
+
+        tracing_subscriber::registry()
+            .with(fmt::layer())
+            .with(telemetry)
+            .with(
+                EnvFilter::builder()
+                    .with_default_directive(Directive::from_str("info").unwrap())
+                    .from_env()
+                    .unwrap(),
+            )
+            .init();
         // Start the gateway server
 
         let mut aio = vec![];
index 9a7f9ee2e93328c1bf6f24d7d4614cc298d1fb85..349deaa633c13e553266ac1a0c9ad07e571e3570 100644 (file)
@@ -8,12 +8,15 @@ edition = "2018"
 [dependencies]
 shared = { path = "../../libs/shared" }
 proto = { path = "../../libs/proto" }
-async-nats = "0.25.1"
-tokio = { version = "1", features = ["full"] }
+
+tokio = { version = "1", features = ["rt"] }
+tokio-stream = "0.1.11"
+
 serde = { version = "1.0.8", features = ["derive"] }
-log = { version = "0.4", features = ["std"] }
 serde_json = { version = "1.0" }
-redis = "*"
-futures-util = "*"
+
+async-nats = "0.25.1"
 twilight-model = "0.14"
-anyhow = "1.0.68"
\ No newline at end of file
+anyhow = "1.0.68"
+
+tracing = "0.1.37"
\ No newline at end of file
index 3d9f5e24e61f9194de9d52114c00b2bee42341bb..3136c1ab20eb627a919470f576bca8267bc08f75 100644 (file)
@@ -1,5 +1,5 @@
 use serde::Deserialize;
 #[derive(Debug, Deserialize, Clone, Default)]
 pub struct CacheConfiguration {
-    pub toggles: Vec<String>
+    pub toggles: Vec<String>,
 }
index 5240a6a32d8507585825ec34a46fa082a67b2c17..bc13cd56a200d37e7829a716fc9dd32820c4f94d 100644 (file)
@@ -1,8 +1,6 @@
-use std::{error::Error, pin::Pin};
+use std::{error::Error, future::Future, pin::Pin};
 
 use async_nats::{Client, Subscriber};
-use futures_util::{stream::StreamExt, Future};
-use log::info;
 use managers::{
     automoderation::Automoderation, bans::Bans, channels::Channels,
     guild_schedules::GuildSchedules, guilds::Guilds, integrations::Integrations, invites::Invites,
@@ -10,6 +8,8 @@ use managers::{
     stage_instances::StageInstances, threads::Threads, CacheManager,
 };
 use shared::{config::Settings, payloads::CachePayload};
+use tokio_stream::StreamExt;
+use tracing::info;
 use twilight_model::gateway::event::DispatchEvent;
 
 use crate::config::CacheConfiguration;
@@ -43,8 +43,8 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
     let settings: Settings<CacheConfiguration> = Settings::new("cache").unwrap();
     info!("loaded configuration: {:?}", settings);
     let nats =
-        Into::<Pin<Box<dyn Future<Output = anyhow::Result<Client>> + Send>>>::into(settings.nats).await?;
-    // let redis: redis::Client = settings.redis.into();
+        Into::<Pin<Box<dyn Future<Output = anyhow::Result<Client>> + Send>>>::into(settings.nats)
+            .await?;
 
     let mut cache = Cache::default();
 
index fe34accef0e6f3479d20a831fcb9dc1f9f6b0248..c645420d4320cbed3c5a90f5c7da3aa955b1fd8e 100644 (file)
@@ -5,7 +5,6 @@ use crate::CacheSourcedEvents;
 use super::CacheManager;
 use std::future::Future;
 
-
 #[derive(Default)]
 pub struct Channels {}
 impl CacheManager for Channels {
index bcc79c59f7c0dea397e9773dae7ad60994d4a308..0c565f17ee46d01d55055c1aaa294b3d8fd7c9e5 100644 (file)
@@ -5,7 +5,6 @@ use crate::CacheSourcedEvents;
 use super::CacheManager;
 use std::future::Future;
 
-
 #[derive(Default)]
 pub struct GuildSchedules {}
 impl CacheManager for GuildSchedules {
index 3f5f4c4829fc84e22f92e0cad056e35773e34bfb..6e142c829bc922969f33060557098b3fb2d3b8a7 100644 (file)
@@ -5,7 +5,6 @@ use crate::CacheSourcedEvents;
 use super::CacheManager;
 use std::future::Future;
 
-
 #[derive(Default)]
 pub struct Guilds {}
 impl CacheManager for Guilds {
@@ -15,16 +14,16 @@ impl CacheManager for Guilds {
     ) -> std::pin::Pin<Box<dyn Future<Output = crate::CacheSourcedEvents>>> {
         Box::pin(async move {
             match event {
-                DispatchEvent::GuildCreate(_) => {},
-                DispatchEvent::GuildDelete(_) => {},
-                DispatchEvent::UnavailableGuild(_) => {},
-                DispatchEvent::GuildUpdate(_) => {},
-                DispatchEvent::WebhooksUpdate(_) => {},
-                DispatchEvent::GuildStickersUpdate(_) => {},
-                DispatchEvent::GuildEmojisUpdate(_) => {},
-                DispatchEvent::VoiceServerUpdate(_) => {},
-                DispatchEvent::GuildIntegrationsUpdate(_) => {},
-                DispatchEvent::CommandPermissionsUpdate(_) => {},
+                DispatchEvent::GuildCreate(_) => {}
+                DispatchEvent::GuildDelete(_) => {}
+                DispatchEvent::UnavailableGuild(_) => {}
+                DispatchEvent::GuildUpdate(_) => {}
+                DispatchEvent::WebhooksUpdate(_) => {}
+                DispatchEvent::GuildStickersUpdate(_) => {}
+                DispatchEvent::GuildEmojisUpdate(_) => {}
+                DispatchEvent::VoiceServerUpdate(_) => {}
+                DispatchEvent::GuildIntegrationsUpdate(_) => {}
+                DispatchEvent::CommandPermissionsUpdate(_) => {}
                 _ => unreachable!(),
             };
 
index 99d292ea3d935b8431a0ffbdaa5597709576e225..8e717241e0b9d9c40f4931ee19f0ef25db6314cb 100644 (file)
@@ -5,7 +5,6 @@ use crate::CacheSourcedEvents;
 use super::CacheManager;
 use std::future::Future;
 
-
 #[derive(Default)]
 pub struct Integrations {}
 impl CacheManager for Integrations {
index 21da64f481d9fdee3b58507a5ef4d5ac161168a0..6168e82532f99ed2c5d0d2f0547ddb0cdca1fc01 100644 (file)
@@ -5,7 +5,6 @@ use crate::CacheSourcedEvents;
 use super::CacheManager;
 use std::future::Future;
 
-
 #[derive(Default)]
 pub struct Invites {}
 impl CacheManager for Invites {
index 3a483f119e05605504eca4d12ce29815b5a16014..21e6cde7399eaeb262e4a183dca4e72ea909a65d 100644 (file)
@@ -5,7 +5,6 @@ use crate::CacheSourcedEvents;
 use super::CacheManager;
 use std::future::Future;
 
-
 #[derive(Default)]
 pub struct Members {}
 impl CacheManager for Members {
@@ -15,11 +14,11 @@ impl CacheManager for Members {
     ) -> std::pin::Pin<Box<dyn Future<Output = crate::CacheSourcedEvents>>> {
         Box::pin(async move {
             match event {
-                DispatchEvent::MemberAdd(_) => {},
-                DispatchEvent::MemberRemove(_) => {},
-                DispatchEvent::MemberUpdate(_) => {},
-                DispatchEvent::MemberChunk(_) => {},
-                DispatchEvent::UserUpdate(_) => {},
+                DispatchEvent::MemberAdd(_) => {}
+                DispatchEvent::MemberRemove(_) => {}
+                DispatchEvent::MemberUpdate(_) => {}
+                DispatchEvent::MemberChunk(_) => {}
+                DispatchEvent::UserUpdate(_) => {}
                 _ => unreachable!(),
             };
 
index 7b06ae70c175a044c578e3c25658cede6ce944d5..1725781321f78c99795ffb0d549f7250e01c82ed 100644 (file)
@@ -5,7 +5,6 @@ use crate::CacheSourcedEvents;
 use super::CacheManager;
 use std::future::Future;
 
-
 #[derive(Default)]
 pub struct Messages {}
 impl CacheManager for Messages {
@@ -15,10 +14,10 @@ impl CacheManager for Messages {
     ) -> std::pin::Pin<Box<dyn Future<Output = crate::CacheSourcedEvents>>> {
         Box::pin(async move {
             match event {
-                DispatchEvent::MessageCreate(_) => {},
-                DispatchEvent::MessageDelete(_) => {},
-                DispatchEvent::MessageDeleteBulk(_) => {},
-                DispatchEvent::MessageUpdate(_) => {},
+                DispatchEvent::MessageCreate(_) => {}
+                DispatchEvent::MessageDelete(_) => {}
+                DispatchEvent::MessageDeleteBulk(_) => {}
+                DispatchEvent::MessageUpdate(_) => {}
                 _ => unreachable!(),
             };
 
index 370cda6e41c9d1c26b70a1abbdd725cd808565b6..9768a86715364d51ccb3cc1a9daa53dcc0f73b5a 100644 (file)
@@ -1,22 +1,22 @@
+use std::future::Future;
 use std::pin::Pin;
 use twilight_model::gateway::event::DispatchEvent;
-use std::future::Future;
 
 use crate::CacheSourcedEvents;
 
+pub mod automoderation;
+pub mod bans;
 pub mod channels;
-pub mod guilds;
 pub mod guild_schedules;
-pub mod stage_instances;
+pub mod guilds;
 pub mod integrations;
+pub mod invites;
 pub mod members;
-pub mod bans;
-pub mod reactions;
 pub mod messages;
-pub mod threads;
-pub mod invites;
+pub mod reactions;
 pub mod roles;
-pub mod automoderation;
+pub mod stage_instances;
+pub mod threads;
 
 pub trait CacheManager {
     fn handle(&self, event: DispatchEvent) -> Pin<Box<dyn Future<Output = CacheSourcedEvents>>>;
index 5d21e0b522183e5f7458ed854b5fa62dc7ea14fc..b154638bd5ff4cb06b82518d266eed8d74519f47 100644 (file)
@@ -5,7 +5,6 @@ use crate::CacheSourcedEvents;
 use super::CacheManager;
 use std::future::Future;
 
-
 #[derive(Default)]
 pub struct Reactions {}
 impl CacheManager for Reactions {
@@ -15,10 +14,10 @@ impl CacheManager for Reactions {
     ) -> std::pin::Pin<Box<dyn Future<Output = crate::CacheSourcedEvents>>> {
         Box::pin(async move {
             match event {
-                DispatchEvent::ReactionAdd(_) => {},
-                DispatchEvent::ReactionRemove(_) => {},
-                DispatchEvent::ReactionRemoveAll(_) => {},
-                DispatchEvent::ReactionRemoveEmoji(_) => {},
+                DispatchEvent::ReactionAdd(_) => {}
+                DispatchEvent::ReactionRemove(_) => {}
+                DispatchEvent::ReactionRemoveAll(_) => {}
+                DispatchEvent::ReactionRemoveEmoji(_) => {}
                 _ => unreachable!(),
             };
 
index 5fa0f22f221e2c45babf6966f3eab2c92ef29e76..c69526fbdc18323e806b4ed4ee61385e37e2880a 100644 (file)
@@ -5,7 +5,6 @@ use crate::CacheSourcedEvents;
 use super::CacheManager;
 use std::future::Future;
 
-
 #[derive(Default)]
 pub struct Roles {}
 impl CacheManager for Roles {
@@ -15,9 +14,9 @@ impl CacheManager for Roles {
     ) -> std::pin::Pin<Box<dyn Future<Output = crate::CacheSourcedEvents>>> {
         Box::pin(async move {
             match event {
-                DispatchEvent::RoleCreate(_) => {},
-                DispatchEvent::RoleDelete(_) => {},
-                DispatchEvent::RoleUpdate(_) => {},
+                DispatchEvent::RoleCreate(_) => {}
+                DispatchEvent::RoleDelete(_) => {}
+                DispatchEvent::RoleUpdate(_) => {}
                 _ => unreachable!(),
             };
 
index 314d08998494ce4f9ba38d0b98c9a833624f2352..baeabc834540a5f65d255c1a38fd33941ca6fbbf 100644 (file)
@@ -5,7 +5,6 @@ use crate::CacheSourcedEvents;
 use super::CacheManager;
 use std::future::Future;
 
-
 #[derive(Default)]
 pub struct StageInstances {}
 impl CacheManager for StageInstances {
@@ -15,9 +14,9 @@ impl CacheManager for StageInstances {
     ) -> std::pin::Pin<Box<dyn Future<Output = crate::CacheSourcedEvents>>> {
         Box::pin(async move {
             match event {
-                DispatchEvent::StageInstanceCreate(_) => {},
-                DispatchEvent::StageInstanceDelete(_) => {},
-                DispatchEvent::StageInstanceUpdate(_) => {},
+                DispatchEvent::StageInstanceCreate(_) => {}
+                DispatchEvent::StageInstanceDelete(_) => {}
+                DispatchEvent::StageInstanceUpdate(_) => {}
                 _ => unreachable!(),
             };
 
index d4efc2ee4e96bad8f6405ff3b409d03145c31d26..8956616b09f54ad0df5ebdb4f0faf9011153e366 100644 (file)
@@ -14,12 +14,12 @@ impl CacheManager for Threads {
     ) -> std::pin::Pin<Box<dyn Future<Output = crate::CacheSourcedEvents>>> {
         Box::pin(async move {
             match event {
-                DispatchEvent::ThreadCreate(_) => {},
-                DispatchEvent::ThreadDelete(_) => {},
-                DispatchEvent::ThreadListSync(_) => {},
-                DispatchEvent::ThreadMemberUpdate(_) => {},
-                DispatchEvent::ThreadMembersUpdate(_) => {},
-                DispatchEvent::ThreadUpdate(_) => {},
+                DispatchEvent::ThreadCreate(_) => {}
+                DispatchEvent::ThreadDelete(_) => {}
+                DispatchEvent::ThreadListSync(_) => {}
+                DispatchEvent::ThreadMemberUpdate(_) => {}
+                DispatchEvent::ThreadMembersUpdate(_) => {}
+                DispatchEvent::ThreadUpdate(_) => {}
                 _ => unreachable!(),
             };
 
index d71ed4ac07cb2e3b146539592523eba9c4af65f2..a17471013f01d9376a1ae5772219cc78937e2f49 100644 (file)
@@ -7,11 +7,24 @@ edition = "2018"
 shared = { path = "../../libs/shared" }
 proto = { path = "../../libs/proto" }
 leash = { path = "../../libs/leash" }
-tokio = { version = "1", features = ["full"] }
+
+tokio = { version = "1", features = ["rt", "signal"] }
+tokio-stream = "0.1.11"
+
 twilight-gateway = { version = "0.14" }
 twilight-model = "0.14"
+
+bytes = "1.3.0"
+anyhow = "1.0.68"
+
 serde = { version = "1.0.8", features = ["derive"] }
-futures = "0.3"
 serde_json = { version = "1.0" }
-bytes = "*"
-anyhow = "*"
\ No newline at end of file
+
+tracing = "0.1.37"
+tracing-futures = "0.2.5"
+
+async-nats = "0.25.1"
+
+tracing-opentelemetry = "0.18.0"
+opentelemetry = "0.18.0"
+opentelemetry-http = "0.7.0"
\ No newline at end of file
index 923ab30cebc2f03c6790bc8a2475811439f9b770..7b12bfeccc826cb9828ee9c0dac01ad588b9fd33 100644 (file)
@@ -1,4 +1,4 @@
-use shared::serde::{Deserialize, Serialize};
+use serde::{Deserialize, Serialize};
 use twilight_gateway::Intents;
 
 #[derive(Serialize, Deserialize, Clone)]
index d7a4cee5b912240ed92c52f0c251db9493badffd..014b72addc039e24960a2e17b117fe59e7615ce7 100644 (file)
@@ -1,19 +1,28 @@
+use async_nats::{Client, HeaderMap, HeaderValue};
 use config::GatewayConfig;
 use leash::{AnyhowResultFuture, Component};
+use opentelemetry::{global, propagation::Injector};
 use shared::{
     config::Settings,
-    log::{debug, info},
-    nats_crate::Client,
-    payloads::{CachePayload, DispatchEventTagged, Tracing},
+    payloads::{CachePayload, DispatchEventTagged},
 };
-use std::{convert::TryFrom, pin::Pin};
-use tokio::sync::oneshot;
+use std::{convert::TryFrom, future::Future, pin::Pin, str::FromStr};
+use tokio::{select, sync::oneshot};
+use tokio_stream::StreamExt;
+use tracing_opentelemetry::OpenTelemetrySpanExt;
 use twilight_gateway::{Event, Shard};
 pub mod config;
-use futures::FutureExt;
-use futures::{select, Future, StreamExt};
+use tracing::{debug, info, trace_span};
 use twilight_model::gateway::event::DispatchEvent;
 
+struct MetadataMap<'a>(&'a mut HeaderMap);
+
+impl<'a> Injector for MetadataMap<'a> {
+    fn set(&mut self, key: &str, value: String) {
+        self.0.insert(key, HeaderValue::from_str(&value).unwrap())
+    }
+}
+
 pub struct GatewayServer {}
 impl Component for GatewayServer {
     type Config = GatewayConfig;
@@ -22,7 +31,7 @@ impl Component for GatewayServer {
     fn start(
         &self,
         settings: Settings<Self::Config>,
-        stop: oneshot::Receiver<()>,
+        mut stop: oneshot::Receiver<()>,
     ) -> AnyhowResultFuture<()> {
         Box::pin(async move {
             let (shard, mut events) = Shard::builder(settings.token.to_owned(), settings.intents)
@@ -33,35 +42,41 @@ impl Component for GatewayServer {
                 settings.nats,
             )
             .await?;
-
             shard.start().await?;
 
-            let mut stop = stop.fuse();
             loop {
                 select! {
-                    event = events.next().fuse() => {
+                    event = events.next() => {
+
                         if let Some(event) = event {
                             match event {
                                 Event::Ready(ready) => {
                                     info!("Logged in as {}", ready.user.name);
-                                }
+                                },
 
                                 _ => {
+
                                     let name = event.kind().name();
                                     if let Ok(dispatch_event) = DispatchEvent::try_from(event) {
+                                        debug!("handling event {}", name.unwrap());
+
                                         let data = CachePayload {
-                                            tracing: Tracing {
-                                                node_id: "".to_string(),
-                                                span: None,
-                                            },
                                             data: DispatchEventTagged {
                                                 data: dispatch_event,
                                             },
                                         };
                                         let value = serde_json::to_string(&data)?;
-                                        debug!("nats send: {}", value);
                                         let bytes = bytes::Bytes::from(value);
-                                        nats.publish(format!("nova.cache.dispatch.{}", name.unwrap()), bytes)
+
+                                        let span = trace_span!("nats send");
+
+                                        let mut header_map = HeaderMap::new();
+                                        let context = span.context();
+                                        global::get_text_map_propagator(|propagator| {
+                                            propagator.inject_context(&context, &mut MetadataMap(&mut header_map))
+                                        });
+
+                                        nats.publish_with_headers(format!("nova.cache.dispatch.{}", name.unwrap()), header_map, bytes)
                                             .await?;
                                     }
                                 }
@@ -70,7 +85,7 @@ impl Component for GatewayServer {
                             break
                         }
                     },
-                    _ = stop => break
+                    _ = (&mut stop) => break
                 };
             }
 
index 2e18f9c2051ae34bb633c1b9c95586141b306403..f1b029891f77ca4d838971c4692c0c72cfe3f778 100644 (file)
@@ -1,4 +1,4 @@
-use leash::ignite;
 use gateway::GatewayServer;
+use leash::ignite;
 
 ignite!(GatewayServer);
index 82ca9f609ca82767a89146d38d53cc64a242a036..d82d8c95f07a76224a53c958f1a84df398fb51aa 100644 (file)
@@ -9,13 +9,21 @@ edition = "2021"
 shared = { path = "../../libs/shared" }
 proto = { path = "../../libs/proto" }
 leash = { path = "../../libs/leash" }
-hyper = { version = "0.14", features = ["full"] }
-tokio = { version = "1", features = ["full"] }
+
+hyper = "0.14"
+tokio = { version = "1", features = ["rt"] }
+
 serde = { version = "1.0.8", features = ["derive"] }
+
 twilight-http-ratelimiting = { git = "https://github.com/MatthieuCoder/twilight.git" }
 anyhow = "*"
-futures-util = "0.3.17"
 tracing = "*"
-serde_json = { version = "1.0" }
+tracing-opentelemetry = "0.18.0"
+opentelemetry = "0.18.0"
+opentelemetry-http = "0.7.0"
+
 tonic = "0.8.3"
 tokio-stream = "0.1.11"
+
+
+redis = { version = "0.22.1", features = ["cluster", "connection-manager", "tokio-comp"] }
\ No newline at end of file
index a75c32998e9ed8b6cc55c2bcc06c07b767c937bf..fbcf3b746d8337df8bee5d6fdcc3c318ddc52ffa 100644 (file)
@@ -1,11 +1,13 @@
-
+use opentelemetry::{global, propagation::Extractor};
+use proto::nova::ratelimit::ratelimiter::{
+    ratelimiter_server::Ratelimiter, BucketSubmitTicketRequest, BucketSubmitTicketResponse,
+};
 use std::pin::Pin;
-
-use futures_util::Stream;
-use proto::nova::ratelimit::ratelimiter::{ratelimiter_server::Ratelimiter, BucketSubmitTicketResponse, BucketSubmitTicketRequest};
 use tokio::sync::mpsc;
-use tokio_stream::{wrappers::ReceiverStream, StreamExt};
+use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt};
 use tonic::{Request, Response, Status, Streaming};
+use tracing::{debug, debug_span, info, Instrument};
+use tracing_opentelemetry::OpenTelemetrySpanExt;
 use twilight_http_ratelimiting::{ticket::TicketReceiver, RatelimitHeaders};
 
 use crate::redis_global_local_bucket_ratelimiter::RedisGlobalLocalBucketRatelimiter;
@@ -20,9 +22,28 @@ impl RLServer {
     }
 }
 
+struct MetadataMap<'a>(&'a tonic::metadata::MetadataMap);
+
+impl<'a> Extractor for MetadataMap<'a> {
+    /// Get a value for a key from the MetadataMap.  If the value can't be converted to &str, returns None
+    fn get(&self, key: &str) -> Option<&str> {
+        self.0.get(key).and_then(|metadata| metadata.to_str().ok())
+    }
+
+    /// Collect all the keys from the MetadataMap.
+    fn keys(&self) -> Vec<&str> {
+        self.0
+            .keys()
+            .map(|key| match key {
+                tonic::metadata::KeyRef::Ascii(v) => v.as_str(),
+                tonic::metadata::KeyRef::Binary(v) => v.as_str(),
+            })
+            .collect::<Vec<_>>()
+    }
+}
+
 #[tonic::async_trait]
 impl Ratelimiter for RLServer {
-
     type SubmitTicketStream =
         Pin<Box<dyn Stream<Item = Result<BucketSubmitTicketResponse, Status>> + Send>>;
 
@@ -30,6 +51,14 @@ impl Ratelimiter for RLServer {
         &self,
         req: Request<Streaming<BucketSubmitTicketRequest>>,
     ) -> Result<Response<Self::SubmitTicketStream>, Status> {
+        let parent_cx =
+            global::get_text_map_propagator(|prop| prop.extract(&MetadataMap(req.metadata())));
+        // Generate a tracing span as usual
+        let span = tracing::span!(tracing::Level::INFO, "request process");
+
+        // Assign parent trace from external context
+        span.set_parent(parent_cx);
+
         let mut in_stream = req.into_inner();
         let (tx, rx) = mpsc::channel(128);
         let imrl = self.ratelimiter.clone();
@@ -45,29 +74,30 @@ impl Ratelimiter for RLServer {
 
                 match result.data.unwrap() {
                     proto::nova::ratelimit::ratelimiter::bucket_submit_ticket_request::Data::Path(path) => {
-                        let a = imrl.ticket(path).await.unwrap();
+                        let span = debug_span!("requesting ticket");
+                        let a = imrl.ticket(path).instrument(span).await.unwrap();
                         receiver = Some(a);
-                        
 
                         tx.send(Ok(BucketSubmitTicketResponse {
                             accepted: 1
                         })).await.unwrap();
-
                     },
                     proto::nova::ratelimit::ratelimiter::bucket_submit_ticket_request::Data::Headers(b) => {
                         if let Some(recv) = receiver {
-                            let recv = recv.await.unwrap();
+                            let span = debug_span!("waiting for headers data");
+                            let recv = recv.instrument(span).await.unwrap();
                             let rheaders = RatelimitHeaders::from_pairs(b.headers.iter().map(|f| (f.0.as_str(), f.1.as_bytes()))).unwrap();
-                            
-                            recv.headers(Some(rheaders)).unwrap();
 
+                            recv.headers(Some(rheaders)).unwrap();
                             break;
                         }
                     },
                 }
             }
-            println!("\tstream ended");
-        });
+
+            debug!("\tstream ended");
+            info!("request terminated");
+        }.instrument(span));
 
         // echo just write the same data that was received
         let out_stream = ReceiverStream::new(rx);
@@ -76,4 +106,4 @@ impl Ratelimiter for RLServer {
             Box::pin(out_stream) as Self::SubmitTicketStream
         ))
     }
-}
\ No newline at end of file
+}
index 345c37a456cd11f5a45ce68ac70554f5b7bd2ab6..7a1f98c29a92b660364d848991b273f050c9c9ee 100644 (file)
@@ -1,9 +1,9 @@
-use futures_util::FutureExt;
 use grpc::RLServer;
 use leash::{AnyhowResultFuture, Component};
 use proto::nova::ratelimit::ratelimiter::ratelimiter_server::RatelimiterServer;
+use redis::aio::MultiplexedConnection;
 use redis_global_local_bucket_ratelimiter::RedisGlobalLocalBucketRatelimiter;
-use shared::{config::Settings, redis_crate::aio::MultiplexedConnection};
+use shared::config::Settings;
 use std::future::Future;
 use std::{net::ToSocketAddrs, pin::Pin};
 use tokio::sync::oneshot;
@@ -34,7 +34,9 @@ impl Component for RatelimiterServerComponent {
                 .add_service(RatelimiterServer::new(server))
                 .serve_with_shutdown(
                     "0.0.0.0:8093".to_socket_addrs().unwrap().next().unwrap(),
-                    stop.map(|_| ()),
+                    async move {
+                        let _ = stop.await;
+                    },
                 )
                 .await?;
 
index d739acf47775c6873bf2311fd9418c6a83a90b8d..b35dc45b78bd22579c6cd4783b672ccebb526a81 100644 (file)
@@ -4,7 +4,6 @@
 //! and respects the global ratelimit.
 
 use super::RedisLockPair;
-use twilight_http_ratelimiting::{headers::RatelimitHeaders, ticket::TicketNotifier};
 use std::{
     collections::HashMap,
     mem,
@@ -21,6 +20,7 @@ use tokio::{
     },
     time::{sleep, timeout},
 };
+use twilight_http_ratelimiting::{headers::RatelimitHeaders, ticket::TicketNotifier};
 
 /// Time remaining until a bucket will reset.
 #[derive(Clone, Debug)]
@@ -265,8 +265,8 @@ impl BucketQueueTask {
             RatelimitHeaders::None => return,
             RatelimitHeaders::Present(present) => {
                 Some((present.limit(), present.remaining(), present.reset_after()))
-            },
-            _=> unreachable!()
+            }
+            _ => unreachable!(),
         };
 
         tracing::debug!(path=?self.path, "updating bucket");
index a97d5a3d2b5d5e1361c8234cf1eb5bb91448ad70..a055b043c27d5caaf9181e17180c9b20993b3a91 100644 (file)
@@ -1,12 +1,11 @@
 use self::bucket::{Bucket, BucketQueueTask};
-use shared::redis_crate::aio::MultiplexedConnection;
-use shared::redis_crate::{AsyncCommands};
+use redis::aio::MultiplexedConnection;
+use redis::AsyncCommands;
 use tokio::sync::Mutex;
 use twilight_http_ratelimiting::ticket::{self, TicketNotifier};
 use twilight_http_ratelimiting::GetTicketFuture;
 mod bucket;
-
-use futures_util::future;
+use std::future;
 use std::{
     collections::hash_map::{Entry, HashMap},
     sync::Arc,
@@ -97,6 +96,6 @@ impl RedisGlobalLocalBucketRatelimiter {
             );
         }
 
-        Box::pin(future::ok(rx))
+        Box::pin(future::ready(Ok(rx)))
     }
 }
index f4c5eccc5c2c6a67e37b09b5867a70ed2a4babca..39e9798997299b91e9eb71b94b2bdc1ca5f594f2 100644 (file)
@@ -10,20 +10,23 @@ shared = { path = "../../libs/shared" }
 proto = { path = "../../libs/proto" }
 leash = { path = "../../libs/leash" }
 
-hyper = { version = "0.14", features = ["full"] }
-tokio = { version = "1", features = ["full"] }
+hyper= "0.14"
+http = "0.2.8"
+
+tokio = { version = "1", features = ["rt"] }
 serde = { version = "1.0.8", features = ["derive"] }
-futures-util = "0.3.17"
+
 hyper-tls = "0.5.0"
-lazy_static = "1.4.0"
-xxhash-rust = { version = "0.8.2", features = ["xxh32"] }
+
+# todo(MatthieuCoder): Move to the real twilight when patch is merged
 twilight-http-ratelimiting = { git = "https://github.com/MatthieuCoder/twilight.git" }
+
 tracing = "0.1.37"
+anyhow = "1.0.68"
 hashring = "0.3.0"
-anyhow = "*"
 tonic = "0.8.3"
-serde_json = { version = "1.0" }
-http = "0.2.8"
 tokio-stream = "0.1.11"
 dns-lookup = "1.0.8"
-tokio-scoped = "0.2.0"
\ No newline at end of file
+opentelemetry = "0.18.0"
+opentelemetry-http = "0.7.0"
+tracing-opentelemetry = "0.18.0"
\ No newline at end of file
index 4e27a308aca36ffa270e4d35649bcc85e1182928..3bfe8db952d7f839fe989bb6799f65a1bf2b1154 100644 (file)
@@ -1,5 +1,5 @@
-use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
 use serde::Deserialize;
+use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
 
 fn default_listening_address() -> SocketAddr {
     SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 8090))
@@ -7,8 +7,7 @@ fn default_listening_address() -> SocketAddr {
 
 #[derive(Debug, Deserialize, Clone)]
 pub struct ServerSettings {
-    #[serde(default = "default_listening_address")]
-    pub listening_adress: SocketAddr
+    pub listening_adress: SocketAddr,
 }
 impl Default for ServerSettings {
     fn default() -> Self {
@@ -20,7 +19,7 @@ impl Default for ServerSettings {
 
 #[derive(Debug, Deserialize, Clone, Default)]
 pub struct Discord {
-    pub token: String
+    pub token: String,
 }
 
 #[derive(Debug, Deserialize, Clone, Default)]
index 004f763e385ad7d50cb2f12402c8b67ce922d88a..3ad4cea2328e2f929cc889e4f183c6b69534bf25 100644 (file)
@@ -1,11 +1,3 @@
-use std::{
-    collections::hash_map::DefaultHasher,
-    convert::TryFrom,
-    hash::{Hash, Hasher},
-    str::FromStr,
-    time::Instant,
-};
-
 use anyhow::bail;
 use http::{
     header::{AUTHORIZATION, CONNECTION, HOST, TRANSFER_ENCODING, UPGRADE},
@@ -13,7 +5,13 @@ use http::{
 };
 use hyper::{client::HttpConnector, Body, Client};
 use hyper_tls::HttpsConnector;
-use shared::log::error;
+use std::{
+    collections::hash_map::DefaultHasher,
+    convert::TryFrom,
+    hash::{Hash, Hasher},
+    str::FromStr,
+};
+use tracing::{debug_span, error, instrument, Instrument};
 use twilight_http_ratelimiting::{Method, Path};
 
 use crate::ratelimit_client::RemoteRatelimiter;
@@ -36,6 +34,7 @@ fn normalize_path(request_path: &str) -> (&str, &str) {
     }
 }
 
+#[instrument]
 pub async fn handle_request(
     client: Client<HttpsConnector<HttpConnector>, Body>,
     ratelimiter: RemoteRatelimiter,
@@ -72,7 +71,7 @@ pub async fn handle_request(
                     "Failed to parse path for {:?} {}: {:?}",
                     method, trimmed_path, e
                 );
-                bail!("failed o parse");
+                bail!("failed to parse");
             }
         }
         .hash(&mut hash);
@@ -80,21 +79,18 @@ pub async fn handle_request(
         (hash.finish().to_string(), uri_string)
     };
 
-    let start_ticket_request = Instant::now();
-    let header_sender = match ratelimiter.ticket(hash).await {
+    let span = debug_span!("ticket validation request");
+    let header_sender = match span
+        .in_scope(|| ratelimiter.ticket(hash))
+        .await
+    {
         Ok(sender) => sender,
         Err(e) => {
             error!("Failed to receive ticket for ratelimiting: {:?}", e);
             bail!("failed to reteive ticket");
         }
     };
-    let time_took_ticket = Instant::now() - start_ticket_request;
-
-    request.headers_mut().insert(
-        AUTHORIZATION,
-        HeaderValue::from_bytes(token.as_bytes())
-            .expect("strings are guaranteed to be valid utf-8"),
-    );
+    
     request
         .headers_mut()
         .insert(HOST, HeaderValue::from_static("discord.com"));
@@ -106,7 +102,7 @@ pub async fn handle_request(
     request.headers_mut().remove("proxy-connection");
     request.headers_mut().remove(TRANSFER_ENCODING);
     request.headers_mut().remove(UPGRADE);
-    
+
     if let Some(auth) = request.headers_mut().get_mut(AUTHORIZATION) {
         if auth
             .to_str()
@@ -130,25 +126,14 @@ pub async fn handle_request(
         }
     };
     *request.uri_mut() = uri;
-
-    let start_upstream_req = Instant::now();
-    let mut resp = match client.request(request).await {
+    let span = debug_span!("upstream request to discord");
+    let resp = match client.request(request).instrument(span).await {
         Ok(response) => response,
         Err(e) => {
             error!("Error when requesting the Discord API: {:?}", e);
             bail!("failed to request the discord api");
         }
     };
-    let upstream_time_took = Instant::now() - start_upstream_req;
-
-    resp.headers_mut().append(
-        "X-TicketRequest-Ms",
-        HeaderValue::from_str(&time_took_ticket.as_millis().to_string()).unwrap(),
-    );
-    resp.headers_mut().append(
-        "X-Upstream-Ms",
-        HeaderValue::from_str(&upstream_time_took.as_millis().to_string()).unwrap(),
-    );
 
     let ratelimit_headers = resp
         .headers()
index 02721cc3d0fefc547141365feb1cc26c1fe162cc..158fc97609eed15901baec52f2ad75851bb11c56 100644 (file)
@@ -8,6 +8,8 @@ use hyper::{
 };
 use hyper_tls::HttpsConnector;
 use leash::{AnyhowResultFuture, Component};
+use opentelemetry::{global, trace::{Tracer}};
+use opentelemetry_http::HeaderExtractor;
 use shared::config::Settings;
 use std::{convert::Infallible, sync::Arc};
 use tokio::sync::oneshot;
@@ -38,6 +40,12 @@ impl Component for ReverseProxyServer {
                 let token = token.clone();
                 async move {
                     Ok::<_, Infallible>(service_fn(move |request: Request<Body>| {
+                        let parent_cx = global::get_text_map_propagator(|propagator| {
+                            propagator.extract(&HeaderExtractor(request.headers()))
+                        });
+                        let _span = global::tracer("")
+                            .start_with_context("handle_request", &parent_cx);
+
                         let client = client.clone();
                         let ratelimiter = ratelimiter.clone();
                         let token = token.clone();
@@ -64,4 +72,4 @@ impl Component for ReverseProxyServer {
     fn new() -> Self {
         Self {}
     }
-}
\ No newline at end of file
+}
index ea34ad96e42f92774c74ca8ffe3f1bc9c86d5e8a..7493af97a003b0e363f374a53906ebaeeac4b709 100644 (file)
@@ -1,10 +1,10 @@
-use self::remote_hashring::{HashRingWrapper, VNode};
-use futures_util::Future;
+use self::remote_hashring::{HashRingWrapper, MetadataMap, VNode};
+use opentelemetry::global;
 use proto::nova::ratelimit::ratelimiter::bucket_submit_ticket_request::{Data, Headers};
 use proto::nova::ratelimit::ratelimiter::BucketSubmitTicketRequest;
-use shared::log::debug;
 use std::collections::HashMap;
 use std::fmt::Debug;
+use std::future::Future;
 use std::pin::Pin;
 use std::sync::Arc;
 use std::time::UNIX_EPOCH;
@@ -12,6 +12,9 @@ use std::time::{Duration, SystemTime};
 use tokio::sync::oneshot::{self};
 use tokio::sync::{broadcast, mpsc, RwLock};
 use tokio_stream::wrappers::ReceiverStream;
+use tonic::Request;
+use tracing::{debug, debug_span, Instrument, Span, instrument};
+use tracing_opentelemetry::OpenTelemetrySpanExt;
 
 mod remote_hashring;
 
@@ -45,7 +48,7 @@ impl RemoteRatelimiter {
 
         let mut write = self.remotes.write().await;
 
-        for ip in ["localhost"] {
+        for ip in ["ratelimit"] {
             let a = VNode::new(ip.into()).await?;
             write.add(a.clone());
         }
@@ -82,55 +85,80 @@ impl RemoteRatelimiter {
         obj
     }
 
+    #[instrument(name = "ticket task")]
     pub fn ticket(&self, path: String) -> IssueTicket {
         let remotes = self.remotes.clone();
         let (tx, rx) = oneshot::channel::<HashMap<String, String>>();
-
-        Box::pin(async move {
-            // Get node managing this path
-            let mut node = (*remotes.read().await.get(&path).unwrap()).clone();
-
-            // Buffers for the gRPC streaming channel.
-            let (send, remote) = mpsc::channel(5);
-            let (do_request, wait) = oneshot::channel();
-            // Tonic requires a stream to be used; Since we use a mpsc channel, we can create a stream from it
-            let stream = ReceiverStream::new(remote);
-
-            // Start the grpc streaming
-            let ticket = node.submit_ticket(stream).await?;
-
-            // First, send the request
-            send.send(BucketSubmitTicketRequest {
-                data: Some(Data::Path(path)),
-            })
-            .await?;
-
-            // We continuously listen for events in the channel.
-            tokio::spawn(async move {
-                let message = ticket.into_inner().message().await.unwrap().unwrap();
-
-                if message.accepted == 1 {
-                    do_request.send(()).unwrap();
-                    let headers = rx.await.unwrap();
-
-                    send.send(BucketSubmitTicketRequest {
-                        data: Some(Data::Headers(Headers {
-                            precise_time: SystemTime::now()
-                                .duration_since(UNIX_EPOCH)
-                                .expect("time went backwards")
-                                .as_millis() as u64,
-                            headers,
-                        })),
-                    })
-                    .await
-                    .unwrap();
-                }
-            });
-
-            // Wait for the message to be sent
-            wait.await?;
-
-            Ok(tx)
-        })
+        Box::pin(
+            async move {
+                // Get node managing this path
+                let mut node = (*remotes.read().await.get(&path).unwrap()).clone();
+
+                // Buffers for the gRPC streaming channel.
+                let (send, remote) = mpsc::channel(5);
+                let (do_request, wait) = oneshot::channel();
+                // Tonic requires a stream to be used; Since we use a mpsc channel, we can create a stream from it
+                let stream = ReceiverStream::new(remote);
+
+                let mut request = Request::new(stream);
+
+                let span = debug_span!("remote request");
+                let context = span.context();
+                global::get_text_map_propagator(|propagator| {
+                    propagator.inject_context(&context, &mut MetadataMap(request.metadata_mut()))
+                });
+
+                // Start the grpc streaming
+                let ticket = node.submit_ticket(request).await?;
+
+                // First, send the request
+                send.send(BucketSubmitTicketRequest {
+                    data: Some(Data::Path(path)),
+                })
+                .await?;
+
+                // We continuously listen for events in the channel.
+                let span = debug_span!("stream worker");
+                tokio::spawn(
+                    async move {
+                        let span = debug_span!("waiting for ticket upstream");
+                        let message = ticket
+                            .into_inner()
+                            .message()
+                            .instrument(span)
+                            .await
+                            .unwrap()
+                            .unwrap();
+
+                        if message.accepted == 1 {
+                            debug!("request ticket was accepted");
+                            do_request.send(()).unwrap();
+                            let span = debug_span!("waiting for response headers");
+                            let headers = rx.instrument(span).await.unwrap();
+
+                            send.send(BucketSubmitTicketRequest {
+                                data: Some(Data::Headers(Headers {
+                                    precise_time: SystemTime::now()
+                                        .duration_since(UNIX_EPOCH)
+                                        .expect("time went backwards")
+                                        .as_millis()
+                                        as u64,
+                                    headers,
+                                })),
+                            })
+                            .await
+                            .unwrap();
+                        }
+                    }
+                    .instrument(span),
+                );
+
+                // Wait for the message to be sent
+                wait.await?;
+
+                Ok(tx)
+            }
+            .instrument(Span::current()),
+        )
     }
 }
index 4e3fa06d994bc099665f863bc0f2fe70712518a5..d1c1702596be43e6baa4ae317763a33104713b0f 100644 (file)
@@ -1,4 +1,6 @@
 use core::fmt::Debug;
+use std::convert::TryFrom;
+use opentelemetry::propagation::Injector;
 use proto::nova::ratelimit::ratelimiter::ratelimiter_client::RatelimiterClient;
 use std::hash::Hash;
 use std::ops::Deref;
@@ -32,6 +34,20 @@ impl Hash for VNode {
     }
 }
 
+pub struct MetadataMap<'a>(pub &'a mut tonic::metadata::MetadataMap);
+
+impl<'a> Injector for MetadataMap<'a> {
+    /// Set a key and value in the MetadataMap.  Does nothing if the key or value are not valid inputs
+    fn set(&mut self, key: &str, value: String) {
+        if let Ok(key) = tonic::metadata::MetadataKey::from_bytes(key.as_bytes()) {
+            if let Ok(val) = tonic::metadata::MetadataValue::try_from(&value) {
+                self.0.insert(key, val);
+            }
+        }
+    }
+}
+
+
 impl VNode {
     pub async fn new(address: String) -> Result<Self, tonic::transport::Error> {
         let client = RatelimiterClient::connect(format!("http://{}:8093", address.clone())).await?;
index 589b5bd772fb2f187f4994432d52aa89acfb402e..0c50009fbbbf194f56acdbde2e9252a7f621798e 100644 (file)
@@ -4,21 +4,19 @@ version = "0.1.0"
 edition = "2018"
 
 [dependencies]
-hyper = { version = "0.14", features = ["full"] }
-tokio = { version = "1", features = ["full"] }
+hyper = "0.14"
+tokio = { version = "1", features = ["rt"] }
 shared = { path = "../../libs/shared" }
 proto = { path = "../../libs/proto" }
 leash = { path = "../../libs/leash" }
+tracing = "0.1.37"
 
 serde = { version = "1.0.8", features = ["derive"] }
-hex = "0.4.3"
 serde_json = { version = "1.0" }
-lazy_static = "1.4.0"
+
+hex = "0.4.3"
 ed25519-dalek = "1"
 twilight-model = { version = "0.14" }
 anyhow = "1.0.68"
-futures-util = "0.3.25"
 
-[[bin]]
-name = "webhook"
-path = "src/main.rs"
+async-nats = "0.25.1"
index d1b3fb616c7ebf8abe68b5a78262aadef8117bc5..02543e65fa3adfc4c129739a61725ff5dabe95e0 100644 (file)
@@ -9,7 +9,6 @@ fn default_listening_address() -> SocketAddr {
 
 #[derive(Debug, Deserialize, Clone, Copy)]
 pub struct ServerSettings {
-    #[serde(default = "default_listening_address")]
     pub listening_adress: SocketAddr,
 }
 impl Default for ServerSettings {
index 3ef859e63fa0d1e751998e9daa7e40c41e289477..594919b1c0ad867b7dda9914293d056e614e53e6 100644 (file)
@@ -1,4 +1,5 @@
 use crate::config::WebhookConfig;
+use async_nats::Client;
 use ed25519_dalek::PublicKey;
 use error::WebhookError;
 use hyper::{
@@ -6,11 +7,7 @@ use hyper::{
     service::Service,
     Body, Method, Request, Response, StatusCode,
 };
-use shared::nats_crate::Client;
-use shared::{
-    log::{debug, error},
-    payloads::{CachePayload, DispatchEventTagged, Tracing},
-};
+use shared::payloads::{CachePayload, DispatchEventTagged};
 use signature::validate_signature;
 use std::{
     future::Future,
@@ -18,6 +15,7 @@ use std::{
     str::from_utf8,
     task::{Context, Poll},
 };
+use tracing::{debug, error};
 use twilight_model::gateway::event::DispatchEvent;
 use twilight_model::{
     application::interaction::{Interaction, InteractionType},
@@ -98,10 +96,6 @@ impl WebhookService {
                                     // this should hopefully not fail ?
 
                                     let data = CachePayload {
-                                        tracing: Tracing {
-                                            node_id: "".to_string(),
-                                            span: None,
-                                        },
                                         data: DispatchEventTagged {
                                             data: DispatchEvent::InteractionCreate(Box::new(
                                                 InteractionCreate(value),
index fc5555fc6ecf48e3bcf301a55967062bdf4c2585..ece7b85c8951ca48a24c53d4007586939fda88b4 100644 (file)
@@ -1,41 +1,13 @@
-use shared::prometheus::{Counter, HistogramVec, labels, opts, register_counter, register_histogram_vec};
-use ed25519_dalek::PublicKey;
-use ed25519_dalek::Verifier;
-use ed25519_dalek::Signature;
-use std::convert::TryInto;
-
-lazy_static::lazy_static! {
-    static ref SIGNATURE_TIME_HISTOGRAM: HistogramVec = register_histogram_vec!(
-        "nova_webhook_signature_time",
-        "The time taken by the signature verification",
-        &["signature"]
-    ).unwrap();
-
-    static ref SIGNATURE_COUNTER: Counter = register_counter!(opts!(
-        "nova_webhook_signatures_verify",
-        "number of signatures verification issued by the service",
-        labels! {"handler" => "webhook_main"}
-    )).unwrap();
-}
-
-fn demo<T, const N: usize>(v: Vec<T>) -> [T; N] {
-    v.try_into()
-        .unwrap_or_else(|v: Vec<T>| panic!("Expected a Vec of length {} but it was {}", N, v.len()))
-}
+use ed25519_dalek::{PublicKey, Signature, Verifier};
 
 pub fn validate_signature(public_key: &PublicKey, data: &[u8], hex_signature: &str) -> bool {
-    SIGNATURE_COUNTER.inc();
-    let timer = SIGNATURE_TIME_HISTOGRAM.with_label_values(&["webhook_main"]).start_timer();
-
-    let signature_result = hex::decode(hex_signature);
+    let mut slice: [u8; Signature::BYTE_SIZE] = [0; Signature::BYTE_SIZE];
+    let signature_result = hex::decode_to_slice(hex_signature, &mut slice);
 
     let mut result = false;
-    if let Ok(signature) = signature_result {
-        let sig = Signature::from(demo(signature));
-
-        result = public_key.verify(data, &sig).is_ok();
+    if signature_result.is_ok() {
+        result = public_key.verify(data, &Signature::from(slice)).is_ok();
     }
 
-    timer.observe_duration();
     result
 }
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8b137891791fe96927ad78e64b0aad7bded08bdc 100644 (file)
@@ -0,0 +1 @@
+
index cf7f55843aff108b6ff680831d8563f6ff16449e..60ae6d3ef6f7d900e1d5f0421dd2296791cb2a8c 100644 (file)
@@ -1,2 +1,2 @@
-pub mod signature;
 pub mod handler;
+pub mod signature;
index 43ab9c4d85bb7ba64dfa574e0abbc121dc8585fa..057e70f72b7cc2f4d519b68d6b8255dabf816241 100644 (file)
@@ -6,11 +6,12 @@ use crate::{
     config::WebhookConfig,
     handler::{make_service::MakeSvc, WebhookService},
 };
+use async_nats::Client;
 use hyper::Server;
 use leash::{AnyhowResultFuture, Component};
-use shared::{config::Settings, log::info, nats_crate::Client};
+use shared::config::Settings;
 use tokio::sync::oneshot;
-
+use tracing::info;
 #[derive(Clone, Copy)]
 pub struct WebhookServer {}
 
@@ -27,7 +28,7 @@ impl Component for WebhookServer {
             info!("Starting server on {}", settings.server.listening_adress);
 
             let bind = settings.server.listening_adress;
-            info!("NAts connected!");
+            info!("Nats connected!");
             let nats = Into::<Pin<Box<dyn Future<Output = anyhow::Result<Client>> + Send>>>::into(
                 settings.nats,
             )
index 32f385c5720b76fa6648c5b8f87be71a564feea6..364bc3a5876a452dd227fce36531240cfe010255 100644 (file)
@@ -8,6 +8,12 @@ edition = "2021"
 [dependencies]
 shared = { path = "../shared" }
 anyhow = "1.0.68"
-tokio = { version = "1.23.0", features = ["full"] }
-pretty_env_logger = "0.4"
-serde = "1.0.152"
\ No newline at end of file
+tokio = { version = "1.23.0", features = ["rt", "signal"] }
+serde = "1.0.152"
+tracing-log = { version = "0.1.3", features = ["env_logger"] }
+tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
+tracing = "0.1.37"
+env_logger = "0.10.0"
+tracing-opentelemetry = "0.18.0"
+opentelemetry = { version ="0.18.0", features = ["rt-tokio"] }
+opentelemetry-otlp = { version = "0.11.0" }
\ No newline at end of file
index a73a0b5f4f32728ebf58996676d7bc8141eae03e..cd6707572ac82d0170cb1ead2829af749ed835bd 100644 (file)
@@ -1,8 +1,17 @@
 use anyhow::Result;
+use opentelemetry::sdk::propagation::TraceContextPropagator;
+use opentelemetry::sdk::trace::{self};
+use opentelemetry::sdk::Resource;
+use opentelemetry::{global, KeyValue};
+use opentelemetry_otlp::WithExportConfig;
 use serde::de::DeserializeOwned;
 use shared::config::Settings;
+use std::str::FromStr;
 use std::{future::Future, pin::Pin};
 use tokio::sync::oneshot;
+use tracing::{info, log::trace};
+use tracing_subscriber::filter::Directive;
+use tracing_subscriber::{fmt, prelude::*, EnvFilter};
 
 pub type AnyhowResultFuture<T> = Pin<Box<dyn Future<Output = Result<T>> + Send>>;
 pub trait Component: Send + Sync + 'static + Sized {
@@ -18,27 +27,48 @@ pub trait Component: Send + Sync + 'static + Sized {
 
     fn _internal_start(self) -> AnyhowResultFuture<()> {
         Box::pin(async move {
-            pretty_env_logger::init();
+            global::set_text_map_propagator(TraceContextPropagator::new());
+            let tracer = opentelemetry_otlp::new_pipeline()
+                .tracing()
+                .with_trace_config(trace::config().with_resource(Resource::new(vec![
+                    KeyValue::new("service.name", Self::SERVICE_NAME),
+                ])))
+                .with_exporter(opentelemetry_otlp::new_exporter().tonic().with_env())
+                .install_batch(opentelemetry::runtime::Tokio)?;
+
+            let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
+
+            tracing_subscriber::registry()
+                .with(fmt::layer())
+                .with(telemetry)
+                .with(
+                    EnvFilter::builder()
+                        .with_default_directive(Directive::from_str("info").unwrap())
+                        .from_env()?,
+                )
+                .init();
+
+            info!("Starting nova");
             let settings = Settings::<Self::Config>::new(Self::SERVICE_NAME);
             let (stop, stop_channel) = oneshot::channel();
 
-            // Start the grpc healthcheck
-            tokio::spawn(async move {});
-
-            // Start the prometheus monitoring job
-            tokio::spawn(async move {});
-
             tokio::spawn(async move {
+                trace!("started signal watching");
                 #[cfg(unix)]
                 tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
                     .unwrap()
                     .recv()
                     .await;
                 #[cfg(not(unix))]
-                tokio::signal::ctrl_c().await;
+                return tokio::signal::ctrl_c().await.unwrap();
 
                 stop.send(()).unwrap();
             });
+
+            trace!(
+                "Starting component {component}",
+                component = Self::SERVICE_NAME
+            );
             self.start(settings?, stop_channel).await
         })
     }
index ce08fbc3f1792bc799657a247845f62ee3596706..419ad0cb2519463e550ddc74e0e5a2c740b9c249 100644 (file)
@@ -4,22 +4,21 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-log = { version = "0.4", features = ["std"] }
 serde = { version = "1.0.8", features = ["derive"] }
+serde_json = { version = "1.0" }
 serde_repr = "0.1"
-config = "0.13"
-hyper = { version = "0.14", features = ["full"] }
-tokio = { version = "1", features = ["full"] }
-enumflags2 = { version = "0.7.1", features = ["serde"] }
-prometheus = { version = "0.13", features = ["process"] }
+
+config = { version = "0.13", default-features = false, features = ["json", "yaml-rust", "ini"] }
+
 async-nats = "0.25.1"
-testcontainers = "0.14"
+redis = { version = "0.22.1", features = ["cluster", "connection-manager", "tokio-comp"] }
+
+tokio = { version = "1", features = ["signal", "rt"] }
+
 twilight-model = "0.14"
-serde_json = { version = "1.0" }
 thiserror = "1.0.38"
-inner = "0.1.1"
 anyhow = "1.0.68"
 
-[dependencies.redis]
-version = "*"
-features = ["cluster", "connection-manager", "tokio-comp"]
+serde_test = "1.0.152"
+
+tracing = "0.1.37"
\ No newline at end of file
index ab584a2b420b00f8990eaea2503cfade957fea48..73bc4493781408a516de20d1f6b38f27c4b68ff6 100644 (file)
@@ -1,23 +1,21 @@
-use std::{env, ops::Deref};
 use config::{Config, Environment, File};
-use log::info;
-use serde::{Deserialize, de::DeserializeOwned};
+use serde::{de::DeserializeOwned, Deserialize};
+use std::{env, ops::Deref};
+use tracing::info;
 
 use crate::error::GenericError;
 #[derive(Debug, Deserialize, Clone)]
 pub struct Settings<T: Clone + DeserializeOwned + Default> {
     #[serde(skip_deserializing)]
     pub config: T,
-    pub monitoring: crate::monitoring::MonitoringConfiguration,
     pub nats: crate::nats::NatsConfiguration,
     pub redis: crate::redis::RedisConfiguration,
 }
 
-impl<T: Clone + DeserializeOwned + Default> Settings<T>
-{
+impl<T: Clone + DeserializeOwned + Default> Settings<T> {
     pub fn new(service_name: &str) -> Result<Settings<T>, GenericError> {
         let mut builder = Config::builder();
-        
+
         builder = builder.add_source(File::with_name("config/default"));
         let mode = env::var("ENV").unwrap_or_else(|_| "development".into());
         info!("Configuration Environment: {}", mode);
@@ -28,7 +26,7 @@ impl<T: Clone + DeserializeOwned + Default> Settings<T>
         let env = Environment::with_prefix("NOVA").separator("__");
         // we can configure each component using environment variables
         builder = builder.add_source(env);
-        
+
         let config = builder.build()?;
         let mut settings: Settings<T> = config.clone().try_deserialize()?;
 
@@ -39,10 +37,10 @@ impl<T: Clone + DeserializeOwned + Default> Settings<T>
     }
 }
 
-impl<T: Clone + DeserializeOwned + Default>  Deref for Settings<T> {
+impl<T: Clone + DeserializeOwned + Default> Deref for Settings<T> {
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
         &self.config
     }
-}
\ No newline at end of file
+}
index 31b1dcdc23695d0a7709b91fb369a70728bcaab4..990dd1c5fb37e3f3b58403d5b650df265c39653a 100644 (file)
@@ -14,5 +14,5 @@ pub enum GenericError {
     StepFailed(String),
 
     #[error("io error")]
-    Io(#[from] io::Error)
+    Io(#[from] io::Error),
 }
index 62d8689d059a9fcc15d9d74dd7ecfbf898a2cd75..68ff335f408079d881681b698c63b6b4b3a5d0ae 100644 (file)
@@ -1,16 +1,7 @@
-pub use ::config as config_crate;
-pub use ::async_nats as nats_crate;
-pub use ::redis as redis_crate;
-pub use log;
-pub use prometheus;
-pub use serde;
-pub use testcontainers;
-
 /// This crate is all the utilities shared by the nova rust projects
 /// It includes logging, config and protocols.
 pub mod config;
 pub mod error;
-pub mod monitoring;
 pub mod nats;
 pub mod payloads;
 pub mod redis;
diff --git a/libs/shared/src/monitoring.rs b/libs/shared/src/monitoring.rs
deleted file mode 100644 (file)
index 4bff043..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-use hyper::{
-    header::CONTENT_TYPE,
-    service::{make_service_fn, service_fn},
-    Body, Request, Response, Server,
-};
-use log::{error, info};
-use prometheus::{Encoder, TextEncoder};
-use serde::Deserialize;
-use std::net::ToSocketAddrs;
-
-#[derive(Clone, Debug, Deserialize)]
-/// Options for the monitoring service
-pub struct MonitoringConfiguration {
-    pub enabled: bool,
-    pub address: Option<String>,
-    pub port: Option<i32>,
-}
-
-/// Handler for the hyper http server
-async fn serve_metrics(_request: Request<Body>) -> Result<Response<Body>, hyper::Error> {
-    let encoder = TextEncoder::new();
-    let metrics = prometheus::gather();
-
-    let mut buffer = vec![];
-    encoder.encode(&metrics, &mut buffer).unwrap();
-
-    let response = Response::builder()
-        .status(200)
-        .header(CONTENT_TYPE, encoder.format_type())
-        .body(Body::from(buffer))
-        .unwrap();
-    Ok(response)
-}
-
-/// Starts a monitoring server on the requested port
-pub fn start_monitoring(configuration: &MonitoringConfiguration) {
-    let config = configuration.clone();
-    tokio::task::spawn(async move {
-        if config.enabled {
-            let address = format!(
-                "{}:{}",
-                config
-                    .address
-                    .expect("a listening address must be specified for the metrics server"),
-                config
-                    .port
-                    .expect("a listening port must be specified for the metrics server")
-            );
-            info!("Starting monitoring server on {}", address);
-
-            let listen_address = address.to_socket_addrs().unwrap().next().unwrap();
-            let server = Server::bind(&listen_address).serve(make_service_fn(|_| async {
-                Ok::<_, hyper::Error>(service_fn(serve_metrics))
-            }));
-
-            if let Err(e) = server.await {
-                error!("failed to start the monitoring server {}", e);
-            }
-        }
-    });
-}
index 7b56dfb9f23da05eecdd203e49b2e9967b4460d2..6e51b2d602b4a4a54e35db037db52aef39c1873b 100644 (file)
@@ -1,9 +1,10 @@
 use std::fmt::Debug;
 
-use crate::serde::Deserializer;
 use serde::de::DeserializeSeed;
+use serde::Deserializer;
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
+use tracing::trace_span;
 use twilight_model::gateway::event::{DispatchEvent, DispatchEventWithTypeDeserializer};
 
 #[derive(Debug, Clone)]
@@ -20,15 +21,19 @@ struct DispatchEventTaggedSerialized {
     pub kind: String,
 }
 
+// todo(MatthieuCoder): Remove the use of the Value
 impl<'de> Deserialize<'de> for DispatchEventTagged {
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
         D: Deserializer<'de>,
     {
+        let _s = trace_span!("deserializing DispatchEventTagged");
         let tagged = DispatchEventTaggedSerialized::deserialize(deserializer)?;
         let deserializer_seed = DispatchEventWithTypeDeserializer::new(&tagged.kind);
         let dispatch_event = deserializer_seed.deserialize(tagged.data).unwrap();
-        Ok(DispatchEventTagged { data: dispatch_event })
+        Ok(DispatchEventTagged {
+            data: dispatch_event,
+        })
     }
 }
 
@@ -37,28 +42,19 @@ impl Serialize for DispatchEventTagged {
     where
         S: serde::Serializer,
     {
-        let kind = self.data.kind().name().unwrap().to_string();
-
-        let s = DispatchEventTaggedSerialized {
-            kind,
+        let _s = trace_span!("serializing DispatchEventTagged");
+        let kind = self.data.kind().name().unwrap();
+        DispatchEventTaggedSerialized {
             data: serde_json::to_value(&self.data).unwrap(),
-        };
-
-        s.serialize(serializer)
+            kind: kind.to_string(),
+        }
+        .serialize(serializer)
     }
 }
 
 /// Payload send to the nova cache queues
 #[derive(Serialize, Deserialize, Debug, Clone)]
-// #[serde(bound(deserialize = "T: Deserialize<'de> + std::default::Default + Clone"))]
 pub struct CachePayload {
-    pub tracing: Tracing,
     #[serde(flatten)]
     pub data: DispatchEventTagged,
 }
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct Tracing {
-    pub node_id: String,
-    pub span: Option<String>,
-}
diff --git a/otel/grafana/grafana.ini b/otel/grafana/grafana.ini
new file mode 100644 (file)
index 0000000..e9c5b16
--- /dev/null
@@ -0,0 +1,1170 @@
+##################### Grafana Configuration Example #####################
+#
+# Everything has defaults so you only need to uncomment things you want to
+# change
+
+# possible values : production, development
+;app_mode = production
+
+# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty
+;instance_name = ${HOSTNAME}
+
+# force migration will run migrations that might cause dataloss
+;force_migration = false
+
+#################################### Paths ####################################
+[paths]
+# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)
+;data = /var/lib/grafana
+
+# Temporary files in `data` directory older than given duration will be removed
+;temp_data_lifetime = 24h
+
+# Directory where grafana can store logs
+;logs = /var/log/grafana
+
+# Directory where grafana will automatically scan and look for plugins
+;plugins = /var/lib/grafana/plugins
+
+# folder that contains provisioning config files that grafana will apply on startup and while running.
+provisioning = /etc/grafana/provisioning
+
+#################################### Server ####################################
+[server]
+# Protocol (http, https, h2, socket)
+protocol = http
+
+# The ip address to bind to, empty will bind to all interfaces
+;http_addr =
+
+# The http port  to use
+http_port = 3000
+
+# The public facing domain name used to access grafana from a browser
+domain = localhost
+
+# Redirect to correct domain if host header does not match domain
+# Prevents DNS rebinding attacks
+;enforce_domain = false
+
+# The full public facing url you use in browser, used for redirects and emails
+# If you use reverse proxy and sub path specify full url (with sub path)
+root_url = %(protocol)s://%(domain)s/grafana/
+
+# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons.
+serve_from_sub_path = true
+
+# Log web requests
+;router_logging = false
+
+# the path relative working path
+;static_root_path = public
+
+# enable gzip
+;enable_gzip = false
+
+# https certs & key file
+;cert_file =
+;cert_key =
+
+# Unix socket path
+;socket =
+
+# CDN Url
+;cdn_url =
+
+# Sets the maximum time using a duration format (5s/5m/5ms) before timing out read of an incoming request and closing idle connections.
+# `0` means there is no timeout for reading the request.
+;read_timeout = 0
+
+#################################### Database ####################################
+[database]
+# You can configure the database connection by specifying type, host, name, user and password
+# as separate properties or as on string using the url properties.
+
+# Either "mysql", "postgres" or "sqlite3", it's your choice
+;type = sqlite3
+;host = 127.0.0.1:3306
+;name = grafana
+;user = root
+# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;"""
+;password =
+
+# Use either URL or the previous fields to configure the database
+# Example: mysql://user:secret@host:port/database
+;url =
+
+# For "postgres" only, either "disable", "require" or "verify-full"
+;ssl_mode = disable
+
+# Database drivers may support different transaction isolation levels.
+# Currently, only "mysql" driver supports isolation levels.
+# If the value is empty - driver's default isolation level is applied.
+# For "mysql" use "READ-UNCOMMITTED", "READ-COMMITTED", "REPEATABLE-READ" or "SERIALIZABLE".
+;isolation_level =
+
+;ca_cert_path =
+;client_key_path =
+;client_cert_path =
+;server_cert_name =
+
+# For "sqlite3" only, path relative to data_path setting
+;path = grafana.db
+
+# Max idle conn setting default is 2
+;max_idle_conn = 2
+
+# Max conn setting default is 0 (mean not set)
+;max_open_conn =
+
+# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours)
+;conn_max_lifetime = 14400
+
+# Set to true to log the sql calls and execution times.
+;log_queries =
+
+# For "sqlite3" only. cache mode setting used for connecting to the database. (private, shared)
+;cache_mode = private
+
+# For "mysql" only if lockingMigration feature toggle is set. How many seconds to wait before failing to lock the database for the migrations, default is 0.
+;locking_attempt_timeout_sec = 0
+
+################################### Data sources #########################
+[datasources]
+# Upper limit of data sources that Grafana will return. This limit is a temporary configuration and it will be deprecated when pagination will be introduced on the list data sources API.
+;datasource_limit = 5000
+
+#################################### Cache server #############################
+[remote_cache]
+# Either "redis", "memcached" or "database" default is "database"
+;type = database
+
+# cache connectionstring options
+# database: will use Grafana primary database.
+# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'.
+# memcache: 127.0.0.1:11211
+;connstr =
+
+#################################### Data proxy ###########################
+[dataproxy]
+
+# This enables data proxy logging, default is false
+;logging = false
+
+# How long the data proxy waits to read the headers of the response before timing out, default is 30 seconds.
+# This setting also applies to core backend HTTP data sources where query requests use an HTTP client with timeout set.
+;timeout = 30
+
+# How long the data proxy waits to establish a TCP connection before timing out, default is 10 seconds.
+;dialTimeout = 10
+
+# How many seconds the data proxy waits before sending a keepalive probe request.
+;keep_alive_seconds = 30
+
+# How many seconds the data proxy waits for a successful TLS Handshake before timing out.
+;tls_handshake_timeout_seconds = 10
+
+# How many seconds the data proxy will wait for a server's first response headers after
+# fully writing the request headers if the request has an "Expect: 100-continue"
+# header. A value of 0 will result in the body being sent immediately, without
+# waiting for the server to approve.
+;expect_continue_timeout_seconds = 1
+
+# Optionally limits the total number of connections per host, including connections in the dialing,
+# active, and idle states. On limit violation, dials will block.
+# A value of zero (0) means no limit.
+;max_conns_per_host = 0
+
+# The maximum number of idle connections that Grafana will keep alive.
+;max_idle_connections = 100
+
+# How many seconds the data proxy keeps an idle connection open before timing out.
+;idle_conn_timeout_seconds = 90
+
+# If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request, default is false.
+;send_user_header = false
+
+# Limit the amount of bytes that will be read/accepted from responses of outgoing HTTP requests.
+;response_limit = 0
+
+# Limits the number of rows that Grafana will process from SQL data sources.
+;row_limit = 1000000
+
+#################################### Analytics ####################################
+[analytics]
+# Server reporting, sends usage counters to stats.grafana.org every 24 hours.
+# No ip addresses are being tracked, only simple counters to track
+# running instances, dashboard and error counts. It is very helpful to us.
+# Change this option to false to disable reporting.
+;reporting_enabled = true
+
+# The name of the distributor of the Grafana instance. Ex hosted-grafana, grafana-labs
+;reporting_distributor = grafana-labs
+
+# Set to false to disable all checks to https://grafana.com
+# for new versions of grafana. The check is used
+# in some UI views to notify that a grafana update exists.
+# This option does not cause any auto updates, nor send any information
+# only a GET request to https://raw.githubusercontent.com/grafana/grafana/main/latest.json to get the latest version.
+;check_for_updates = true
+
+# Set to false to disable all checks to https://grafana.com
+# for new versions of plugins. The check is used
+# in some UI views to notify that a plugin update exists.
+# This option does not cause any auto updates, nor send any information
+# only a GET request to https://grafana.com to get the latest versions.
+;check_for_plugin_updates = true
+
+# Google Analytics universal tracking code, only enabled if you specify an id here
+;google_analytics_ua_id =
+
+# Google Tag Manager ID, only enabled if you specify an id here
+;google_tag_manager_id =
+
+# Rudderstack write key, enabled only if rudderstack_data_plane_url is also set
+;rudderstack_write_key =
+
+# Rudderstack data plane url, enabled only if rudderstack_write_key is also set
+;rudderstack_data_plane_url =
+
+# Rudderstack SDK url, optional, only valid if rudderstack_write_key and rudderstack_data_plane_url is also set
+;rudderstack_sdk_url =
+
+# Rudderstack Config url, optional, used by Rudderstack SDK to fetch source config
+;rudderstack_config_url =
+
+# Controls if the UI contains any links to user feedback forms
+;feedback_links_enabled = true
+
+#################################### Security ####################################
+[security]
+# disable creation of admin user on first start of grafana
+;disable_initial_admin_creation = false
+
+# default admin user, created on startup
+;admin_user = admin
+
+# default admin password, can be changed before first start of grafana,  or in profile settings
+;admin_password = admin
+
+# used for signing
+;secret_key = SW2YcwTIb9zpOOhoPsMm
+
+# current key provider used for envelope encryption, default to static value specified by secret_key
+;encryption_provider = secretKey.v1
+
+# list of configured key providers, space separated (Enterprise only): e.g., awskms.v1 azurekv.v1
+;available_encryption_providers =
+
+# disable gravatar profile images
+;disable_gravatar = false
+
+# data source proxy whitelist (ip_or_domain:port separated by spaces)
+;data_source_proxy_whitelist =
+
+# disable protection against brute force login attempts
+;disable_brute_force_login_protection = false
+
+# set to true if you host Grafana behind HTTPS. default is false.
+;cookie_secure = false
+
+# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled"
+;cookie_samesite = lax
+
+# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false.
+;allow_embedding = false
+
+# Set to true if you want to enable http strict transport security (HSTS) response header.
+# HSTS tells browsers that the site should only be accessed using HTTPS.
+;strict_transport_security = false
+
+# Sets how long a browser should cache HSTS. Only applied if strict_transport_security is enabled.
+;strict_transport_security_max_age_seconds = 86400
+
+# Set to true if to enable HSTS preloading option. Only applied if strict_transport_security is enabled.
+;strict_transport_security_preload = false
+
+# Set to true if to enable the HSTS includeSubDomains option. Only applied if strict_transport_security is enabled.
+;strict_transport_security_subdomains = false
+
+# Set to true to enable the X-Content-Type-Options response header.
+# The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised
+# in the Content-Type headers should not be changed and be followed.
+;x_content_type_options = true
+
+# Set to true to enable the X-XSS-Protection header, which tells browsers to stop pages from loading
+# when they detect reflected cross-site scripting (XSS) attacks.
+;x_xss_protection = true
+
+# Enable adding the Content-Security-Policy header to your requests.
+# CSP allows to control resources the user agent is allowed to load and helps prevent XSS attacks.
+;content_security_policy = false
+
+# Set Content Security Policy template used when adding the Content-Security-Policy header to your requests.
+# $NONCE in the template includes a random nonce.
+# $ROOT_PATH is server.root_url without the protocol.
+;content_security_policy_template = """script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';"""
+
+# Controls if old angular plugins are supported or not. This will be disabled by default in future release
+;angular_support_enabled = true
+
+[security.encryption]
+# Defines the time-to-live (TTL) for decrypted data encryption keys stored in memory (cache).
+# Please note that small values may cause performance issues due to a high frequency decryption operations.
+;data_keys_cache_ttl = 15m
+
+# Defines the frequency of data encryption keys cache cleanup interval.
+# On every interval, decrypted data encryption keys that reached the TTL are removed from the cache.
+;data_keys_cache_cleanup_interval = 1m
+
+#################################### Snapshots ###########################
+[snapshots]
+# snapshot sharing options
+;external_enabled = true
+;external_snapshot_url = https://snapshots.raintank.io
+;external_snapshot_name = Publish to snapshots.raintank.io
+
+# Set to true to enable this Grafana instance act as an external snapshot server and allow unauthenticated requests for
+# creating and deleting snapshots.
+;public_mode = false
+
+# remove expired snapshot
+;snapshot_remove_expired = true
+
+#################################### Dashboards History ##################
+[dashboards]
+# Number dashboard versions to keep (per dashboard). Default: 20, Minimum: 1
+;versions_to_keep = 20
+
+# Minimum dashboard refresh interval. When set, this will restrict users to set the refresh interval of a dashboard lower than given interval. Per default this is 5 seconds.
+# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
+;min_refresh_interval = 5s
+
+# Path to the default home dashboard. If this value is empty, then Grafana uses StaticRootPath + "dashboards/home.json"
+;default_home_dashboard_path =
+
+#################################### Users ###############################
+[users]
+# disable user signup / registration
+;allow_sign_up = true
+
+# Allow non admin users to create organizations
+;allow_org_create = true
+
+# Set to true to automatically assign new users to the default organization (id 1)
+;auto_assign_org = true
+
+# Set this value to automatically add new users to the provided organization (if auto_assign_org above is set to true)
+;auto_assign_org_id = 1
+
+# Default role new users will be automatically assigned (if disabled above is set to true)
+;auto_assign_org_role = Viewer
+
+# Require email validation before sign up completes
+;verify_email_enabled = false
+
+# Background text for the user field on the login page
+;login_hint = email or username
+;password_hint = password
+
+# Default UI theme ("dark" or "light")
+;default_theme = dark
+
+# Path to a custom home page. Users are only redirected to this if the default home dashboard is used. It should match a frontend route and contain a leading slash.
+; home_page =
+
+# External user management, these options affect the organization users view
+;external_manage_link_url =
+;external_manage_link_name =
+;external_manage_info =
+
+# Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard.
+;viewers_can_edit = false
+
+# Editors can administrate dashboard, folders and teams they create
+;editors_can_admin = false
+
+# The duration in time a user invitation remains valid before expiring. This setting should be expressed as a duration. Examples: 6h (hours), 2d (days), 1w (week). Default is 24h (24 hours). The minimum supported duration is 15m (15 minutes).
+;user_invite_max_lifetime_duration = 24h
+
+# Enter a comma-separated list of users login to hide them in the Grafana UI. These users are shown to Grafana admins and themselves.
+; hidden_users =
+
+[auth]
+# Login cookie name
+;login_cookie_name = grafana_session
+
+# The maximum lifetime (duration) an authenticated user can be inactive before being required to login at next visit. Default is 7 days (7d). This setting should be expressed as a duration, e.g. 5m (minutes), 6h (hours), 10d (days), 2w (weeks), 1M (month). The lifetime resets at each successful token rotation.
+;login_maximum_inactive_lifetime_duration =
+
+# The maximum lifetime (duration) an authenticated user can be logged in since login time before being required to login. Default is 30 days (30d). This setting should be expressed as a duration, e.g. 5m (minutes), 6h (hours), 10d (days), 2w (weeks), 1M (month).
+;login_maximum_lifetime_duration =
+
+# How often should auth tokens be rotated for authenticated users when being active. The default is each 10 minutes.
+;token_rotation_interval_minutes = 10
+
+# Set to true to disable (hide) the login form, useful if you use OAuth, defaults to false
+disable_login_form = true 
+
+# Set to true to disable the sign out link in the side menu. Useful if you use auth.proxy or auth.jwt, defaults to false
+;disable_signout_menu = false
+
+# URL to redirect the user to after sign out
+;signout_redirect_url =
+
+# Set to true to attempt login with OAuth automatically, skipping the login screen.
+# This setting is ignored if multiple OAuth providers are configured.
+;oauth_auto_login = false
+
+# OAuth state max age cookie duration in seconds. Defaults to 600 seconds.
+;oauth_state_cookie_max_age = 600
+
+# Skip forced assignment of OrgID 1 or 'auto_assign_org_id' for social logins
+;oauth_skip_org_role_update_sync = false
+
+# limit of api_key seconds to live before expiration
+;api_key_max_seconds_to_live = -1
+
+# Set to true to enable SigV4 authentication option for HTTP-based datasources.
+;sigv4_auth_enabled = false
+
+# Set to true to enable verbose logging of SigV4 request signing
+;sigv4_verbose_logging = false
+
+#################################### Anonymous Auth ######################
+[auth.anonymous]
+# enable anonymous access
+enabled = true 
+
+# specify organization name that should be used for unauthenticated users
+org_name = Main Org.
+
+# specify role for unauthenticated users
+org_role = Admin 
+
+# mask the Grafana version number for unauthenticated users
+;hide_version = false
+
+#################################### GitHub Auth ##########################
+[auth.github]
+;enabled = false
+;allow_sign_up = true
+;client_id = some_id
+;client_secret = some_secret
+;scopes = user:email,read:org
+;auth_url = https://github.com/login/oauth/authorize
+;token_url = https://github.com/login/oauth/access_token
+;api_url = https://api.github.com/user
+;allowed_domains =
+;team_ids =
+;allowed_organizations =
+
+#################################### GitLab Auth #########################
+[auth.gitlab]
+;enabled = false
+;allow_sign_up = true
+;client_id = some_id
+;client_secret = some_secret
+;scopes = api
+;auth_url = https://gitlab.com/oauth/authorize
+;token_url = https://gitlab.com/oauth/token
+;api_url = https://gitlab.com/api/v4
+;allowed_domains =
+;allowed_groups =
+
+#################################### Google Auth ##########################
+[auth.google]
+;enabled = false
+;allow_sign_up = true
+;client_id = some_client_id
+;client_secret = some_client_secret
+;scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
+;auth_url = https://accounts.google.com/o/oauth2/auth
+;token_url = https://accounts.google.com/o/oauth2/token
+;api_url = https://www.googleapis.com/oauth2/v1/userinfo
+;allowed_domains =
+;hosted_domain =
+
+#################################### Grafana.com Auth ####################
+[auth.grafana_com]
+;enabled = false
+;allow_sign_up = true
+;client_id = some_id
+;client_secret = some_secret
+;scopes = user:email
+;allowed_organizations =
+
+#################################### Azure AD OAuth #######################
+[auth.azuread]
+;name = Azure AD
+;enabled = false
+;allow_sign_up = true
+;client_id = some_client_id
+;client_secret = some_client_secret
+;scopes = openid email profile
+;auth_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize
+;token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
+;allowed_domains =
+;allowed_groups =
+;role_attribute_strict = false
+
+#################################### Okta OAuth #######################
+[auth.okta]
+;name = Okta
+;enabled = false
+;allow_sign_up = true
+;client_id = some_id
+;client_secret = some_secret
+;scopes = openid profile email groups
+;auth_url = https://<tenant-id>.okta.com/oauth2/v1/authorize
+;token_url = https://<tenant-id>.okta.com/oauth2/v1/token
+;api_url = https://<tenant-id>.okta.com/oauth2/v1/userinfo
+;allowed_domains =
+;allowed_groups =
+;role_attribute_path =
+;role_attribute_strict = false
+
+#################################### Generic OAuth ##########################
+[auth.generic_oauth]
+;enabled = false
+;name = OAuth
+;allow_sign_up = true
+;client_id = some_id
+;client_secret = some_secret
+;scopes = user:email,read:org
+;empty_scopes = false
+;email_attribute_name = email:primary
+;email_attribute_path =
+;login_attribute_path =
+;name_attribute_path =
+;id_token_attribute_name =
+;auth_url = https://foo.bar/login/oauth/authorize
+;token_url = https://foo.bar/login/oauth/access_token
+;api_url = https://foo.bar/user
+;teams_url =
+;allowed_domains =
+;team_ids =
+;allowed_organizations =
+;role_attribute_path =
+;role_attribute_strict = false
+;groups_attribute_path =
+;team_ids_attribute_path =
+;tls_skip_verify_insecure = false
+;tls_client_cert =
+;tls_client_key =
+;tls_client_ca =
+;use_pkce = false
+
+#################################### Basic Auth ##########################
+[auth.basic]
+;enabled = true
+
+#################################### Auth Proxy ##########################
+[auth.proxy]
+;enabled = false
+;header_name = X-WEBAUTH-USER
+;header_property = username
+;auto_sign_up = true
+;sync_ttl = 60
+;whitelist = 192.168.1.1, 192.168.2.1
+;headers = Email:X-User-Email, Name:X-User-Name
+# Non-ASCII strings in header values are encoded using quoted-printable encoding
+;headers_encoded = false
+# Read the auth proxy docs for details on what the setting below enables
+;enable_login_token = false
+
+#################################### Auth JWT ##########################
+[auth.jwt]
+;enabled = true
+;header_name = X-JWT-Assertion
+;email_claim = sub
+;username_claim = sub
+;jwk_set_url = https://foo.bar/.well-known/jwks.json
+;jwk_set_file = /path/to/jwks.json
+;cache_ttl = 60m
+;expected_claims = {"aud": ["foo", "bar"]}
+;key_file = /path/to/key/file
+;auto_sign_up = false
+
+#################################### Auth LDAP ##########################
+[auth.ldap]
+;enabled = false
+;config_file = /etc/grafana/ldap.toml
+;allow_sign_up = true
+
+# LDAP background sync (Enterprise only)
+# At 1 am every day
+;sync_cron = "0 1 * * *"
+;active_sync_enabled = true
+
+#################################### AWS ###########################
+[aws]
+# Enter a comma-separated list of allowed AWS authentication providers.
+# Options are: default (AWS SDK Default), keys (Access && secret key), credentials (Credentials field), ec2_iam_role (EC2 IAM Role)
+; allowed_auth_providers = default,keys,credentials
+
+# Allow AWS users to assume a role using temporary security credentials.
+# If true, assume role will be enabled for all AWS authentication providers that are specified in aws_auth_providers
+; assume_role_enabled = true
+
+#################################### Azure ###############################
+[azure]
+# Azure cloud environment where Grafana is hosted
+# Possible values are AzureCloud, AzureChinaCloud, AzureUSGovernment and AzureGermanCloud
+# Default value is AzureCloud (i.e. public cloud)
+;cloud = AzureCloud
+
+# Specifies whether Grafana hosted in Azure service with Managed Identity configured (e.g. Azure Virtual Machines instance)
+# If enabled, the managed identity can be used for authentication of Grafana in Azure services
+# Disabled by default, needs to be explicitly enabled
+;managed_identity_enabled = false
+
+# Client ID to use for user-assigned managed identity
+# Should be set for user-assigned identity and should be empty for system-assigned identity
+;managed_identity_client_id =
+
+#################################### Role-based Access Control ###########
+[rbac]
+;enabled = true
+# If enabled, cache permissions in a in memory cache (Enterprise only)
+;permission_cache = true
+#################################### SMTP / Emailing ##########################
+[smtp]
+;enabled = false
+;host = localhost:25
+;user =
+# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;"""
+;password =
+;cert_file =
+;key_file =
+;skip_verify = false
+;from_address = admin@grafana.localhost
+;from_name = Grafana
+# EHLO identity in SMTP dialog (defaults to instance_name)
+;ehlo_identity = dashboard.example.com
+# SMTP startTLS policy (defaults to 'OpportunisticStartTLS')
+;startTLS_policy = NoStartTLS
+
+[emails]
+;welcome_email_on_sign_up = false
+;templates_pattern = emails/*.html, emails/*.txt
+;content_types = text/html
+
+#################################### Logging ##########################
+[log]
+# Either "console", "file", "syslog". Default is console and  file
+# Use space to separate multiple modes, e.g. "console file"
+;mode = console file
+
+# Either "debug", "info", "warn", "error", "critical", default is "info"
+;level = info
+
+# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
+;filters =
+
+# For "console" mode only
+[log.console]
+;level =
+
+# log line format, valid options are text, console and json
+;format = console
+
+# For "file" mode only
+[log.file]
+;level =
+
+# log line format, valid options are text, console and json
+;format = text
+
+# This enables automated log rotate(switch of following options), default is true
+;log_rotate = true
+
+# Max line number of single file, default is 1000000
+;max_lines = 1000000
+
+# Max size shift of single file, default is 28 means 1 << 28, 256MB
+;max_size_shift = 28
+
+# Segment log daily, default is true
+;daily_rotate = true
+
+# Expired days of log file(delete after max days), default is 7
+;max_days = 7
+
+[log.syslog]
+;level =
+
+# log line format, valid options are text, console and json
+;format = text
+
+# Syslog network type and address. This can be udp, tcp, or unix. If left blank, the default unix endpoints will be used.
+;network =
+;address =
+
+# Syslog facility. user, daemon and local0 through local7 are valid.
+;facility =
+
+# Syslog tag. By default, the process' argv[0] is used.
+;tag =
+
+[log.frontend]
+# Should Sentry javascript agent be initialized
+;enabled = false
+
+# Sentry DSN if you want to send events to Sentry.
+;sentry_dsn =
+
+# Custom HTTP endpoint to send events captured by the Sentry agent to. Default will log the events to stdout.
+;custom_endpoint = /log
+
+# Rate of events to be reported between 0 (none) and 1 (all), float
+;sample_rate = 1.0
+
+# Requests per second limit enforced an extended period, for Grafana backend log ingestion endpoint (/log).
+;log_endpoint_requests_per_second_limit = 3
+
+# Max requests accepted per short interval of time for Grafana backend log ingestion endpoint (/log).
+;log_endpoint_burst_limit = 15
+
+#################################### Usage Quotas ########################
+[quota]
+; enabled = false
+
+#### set quotas to -1 to make unlimited. ####
+# limit number of users per Org.
+; org_user = 10
+
+# limit number of dashboards per Org.
+; org_dashboard = 100
+
+# limit number of data_sources per Org.
+; org_data_source = 10
+
+# limit number of api_keys per Org.
+; org_api_key = 10
+
+# limit number of alerts per Org.
+;org_alert_rule = 100
+
+# limit number of orgs a user can create.
+; user_org = 10
+
+# Global limit of users.
+; global_user = -1
+
+# global limit of orgs.
+; global_org = -1
+
+# global limit of dashboards
+; global_dashboard = -1
+
+# global limit of api_keys
+; global_api_key = -1
+
+# global limit on number of logged in users.
+; global_session = -1
+
+# global limit of alerts
+;global_alert_rule = -1
+
+#################################### Unified Alerting ####################
+[unified_alerting]
+#Enable the Unified Alerting sub-system and interface. When enabled we'll migrate all of your alert rules and notification channels to the new system. New alert rules will be created and your notification channels will be converted into an Alertmanager configuration. Previous data is preserved to enable backwards compatibility but new data is removed.```
+;enabled = true
+
+# Comma-separated list of organization IDs for which to disable unified alerting. Only supported if unified alerting is enabled.
+;disabled_orgs =
+
+# Specify the frequency of polling for admin config changes.
+# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
+;admin_config_poll_interval = 60s
+
+# Specify the frequency of polling for Alertmanager config changes.
+# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
+;alertmanager_config_poll_interval = 60s
+
+# Listen address/hostname and port to receive unified alerting messages for other Grafana instances. The port is used for both TCP and UDP. It is assumed other Grafana instances are also running on the same port. The default value is `0.0.0.0:9094`.
+;ha_listen_address = "0.0.0.0:9094"
+
+# Listen address/hostname and port to receive unified alerting messages for other Grafana instances. The port is used for both TCP and UDP. It is assumed other Grafana instances are also running on the same port. The default value is `0.0.0.0:9094`.
+;ha_advertise_address = ""
+
+# Comma-separated list of initial instances (in a format of host:port) that will form the HA cluster. Configuring this setting will enable High Availability mode for alerting.
+;ha_peers = ""
+
+# Time to wait for an instance to send a notification via the Alertmanager. In HA, each Grafana instance will
+# be assigned a position (e.g. 0, 1). We then multiply this position with the timeout to indicate how long should
+# each instance wait before sending the notification to take into account replication lag.
+# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
+;ha_peer_timeout = "15s"
+
+# The interval between sending gossip messages. By lowering this value (more frequent) gossip messages are propagated
+# across cluster more quickly at the expense of increased bandwidth usage.
+# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
+;ha_gossip_interval = "200ms"
+
+# The interval between gossip full state syncs. Setting this interval lower (more frequent) will increase convergence speeds
+# across larger clusters at the expense of increased bandwidth usage.
+# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
+;ha_push_pull_interval = "60s"
+
+# Enable or disable alerting rule execution. The alerting UI remains visible. This option has a legacy version in the `[alerting]` section that takes precedence.
+;execute_alerts = true
+
+# Alert evaluation timeout when fetching data from the datasource. This option has a legacy version in the `[alerting]` section that takes precedence.
+# The timeout string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
+;evaluation_timeout = 30s
+
+# Number of times we'll attempt to evaluate an alert rule before giving up on that evaluation. This option has a legacy version in the `[alerting]` section that takes precedence.
+;max_attempts = 3
+
+# Minimum interval to enforce between rule evaluations. Rules will be adjusted if they are less than this value  or if they are not multiple of the scheduler interval (10s). Higher values can help with resource management as we'll schedule fewer evaluations over time. This option has a legacy version in the `[alerting]` section that takes precedence.
+# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
+;min_interval = 10s
+
+#################################### Alerting ############################
+[alerting]
+# Disable legacy alerting engine & UI features
+;enabled = false
+
+# Makes it possible to turn off alert execution but alerting UI is visible
+;execute_alerts = true
+
+# Default setting for new alert rules. Defaults to categorize error and timeouts as alerting. (alerting, keep_state)
+;error_or_timeout = alerting
+
+# Default setting for how Grafana handles nodata or null values in alerting. (alerting, no_data, keep_state, ok)
+;nodata_or_nullvalues = no_data
+
+# Alert notifications can include images, but rendering many images at the same time can overload the server
+# This limit will protect the server from render overloading and make sure notifications are sent out quickly
+;concurrent_render_limit = 5
+
+# Default setting for alert calculation timeout. Default value is 30
+;evaluation_timeout_seconds = 30
+
+# Default setting for alert notification timeout. Default value is 30
+;notification_timeout_seconds = 30
+
+# Default setting for max attempts to sending alert notifications. Default value is 3
+;max_attempts = 3
+
+# Makes it possible to enforce a minimal interval between evaluations, to reduce load on the backend
+;min_interval_seconds = 1
+
+# Configures for how long alert annotations are stored. Default is 0, which keeps them forever.
+# This setting should be expressed as a duration. Examples: 6h (hours), 10d (days), 2w (weeks), 1M (month).
+;max_annotation_age =
+
+# Configures max number of alert annotations that Grafana stores. Default value is 0, which keeps all alert annotations.
+;max_annotations_to_keep =
+
+#################################### Annotations #########################
+[annotations]
+# Configures the batch size for the annotation clean-up job. This setting is used for dashboard, API, and alert annotations.
+;cleanupjob_batchsize = 100
+
+[annotations.dashboard]
+# Dashboard annotations means that annotations are associated with the dashboard they are created on.
+
+# Configures how long dashboard annotations are stored. Default is 0, which keeps them forever.
+# This setting should be expressed as a duration. Examples: 6h (hours), 10d (days), 2w (weeks), 1M (month).
+;max_age =
+
+# Configures max number of dashboard annotations that Grafana stores. Default value is 0, which keeps all dashboard annotations.
+;max_annotations_to_keep =
+
+[annotations.api]
+# API annotations means that the annotations have been created using the API without any
+# association with a dashboard.
+
+# Configures how long Grafana stores API annotations. Default is 0, which keeps them forever.
+# This setting should be expressed as a duration. Examples: 6h (hours), 10d (days), 2w (weeks), 1M (month).
+;max_age =
+
+# Configures max number of API annotations that Grafana keeps. Default value is 0, which keeps all API annotations.
+;max_annotations_to_keep =
+
+#################################### Explore #############################
+[explore]
+# Enable the Explore section
+;enabled = true
+
+#################################### Help #############################
+[help]
+# Enable the Help section
+;enabled = true
+
+#################################### Profile #############################
+[profile]
+# Enable the Profile section
+;enabled = true
+
+#################################### Query History #############################
+[query_history]
+# Enable the Query history
+;enabled = true
+
+#################################### Internal Grafana Metrics ##########################
+# Metrics available at HTTP URL /metrics and /metrics/plugins/:pluginId
+[metrics]
+# Disable / Enable internal metrics
+;enabled           = true
+# Graphite Publish interval
+;interval_seconds  = 10
+# Disable total stats (stat_totals_*) metrics to be generated
+;disable_total_stats = false
+
+#If both are set, basic auth will be required for the metrics endpoints.
+; basic_auth_username =
+; basic_auth_password =
+
+# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
+# can expose more information about the Grafana instance.
+[metrics.environment_info]
+#exampleLabel1 = exampleValue1
+#exampleLabel2 = exampleValue2
+
+# Send internal metrics to Graphite
+[metrics.graphite]
+# Enable by setting the address setting (ex localhost:2003)
+;address =
+;prefix = prod.grafana.%(instance_name)s.
+
+#################################### Grafana.com integration  ##########################
+# Url used to import dashboards directly from Grafana.com
+[grafana_com]
+;url = https://grafana.com
+
+#################################### Distributed tracing ############
+# Opentracing is deprecated use opentelemetry instead
+[tracing.jaeger]
+# Enable by setting the address sending traces to jaeger (ex localhost:6831)
+;address = localhost:6831
+# Tag that will always be included in when creating new spans. ex (tag1:value1,tag2:value2)
+;always_included_tag = tag1:value1
+# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote
+;sampler_type = const
+# jaeger samplerconfig param
+# for "const" sampler, 0 or 1 for always false/true respectively
+# for "probabilistic" sampler, a probability between 0 and 1
+# for "rateLimiting" sampler, the number of spans per second
+# for "remote" sampler, param is the same as for "probabilistic"
+# and indicates the initial sampling rate before the actual one
+# is received from the mothership
+;sampler_param = 1
+# sampling_server_url is the URL of a sampling manager providing a sampling strategy.
+;sampling_server_url =
+# Whether or not to use Zipkin propagation (x-b3- HTTP headers).
+;zipkin_propagation = false
+# Setting this to true disables shared RPC spans.
+# Not disabling is the most common setting when using Zipkin elsewhere in your infrastructure.
+;disable_shared_zipkin_spans = false
+
+[tracing.opentelemetry.jaeger]
+# jaeger destination (ex http://localhost:14268/api/traces)
+; address = http://localhost:14268/api/traces
+# Propagation specifies the text map propagation format: w3c, jaeger
+; propagation = jaeger
+
+# This is a configuration for OTLP exporter with GRPC protocol
+[tracing.opentelemetry.otlp]
+# otlp destination (ex localhost:4317)
+; address = localhost:4317
+# Propagation specifies the text map propagation format: w3c, jaeger
+; propagation = w3c
+
+#################################### External image storage ##########################
+[external_image_storage]
+# Used for uploading images to public servers so they can be included in slack/email messages.
+# you can choose between (s3, webdav, gcs, azure_blob, local)
+;provider =
+
+[external_image_storage.s3]
+;endpoint =
+;path_style_access =
+;bucket =
+;region =
+;path =
+;access_key =
+;secret_key =
+
+[external_image_storage.webdav]
+;url =
+;public_url =
+;username =
+;password =
+
+[external_image_storage.gcs]
+;key_file =
+;bucket =
+;path =
+
+[external_image_storage.azure_blob]
+;account_name =
+;account_key =
+;container_name =
+
+[external_image_storage.local]
+# does not require any configuration
+
+[rendering]
+# Options to configure a remote HTTP image rendering service, e.g. using https://github.com/grafana/grafana-image-renderer.
+# URL to a remote HTTP image renderer service, e.g. http://localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service.
+;server_url =
+# If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, e.g. http://grafana.domain/.
+;callback_url =
+# Concurrent render request limit affects when the /render HTTP endpoint is used. Rendering many images at the same time can overload the server,
+# which this setting can help protect against by only allowing a certain amount of concurrent requests.
+;concurrent_render_request_limit = 30
+
+[panels]
+# If set to true Grafana will allow script tags in text panels. Not recommended as it enable XSS vulnerabilities.
+;disable_sanitize_html = false
+
+[plugins]
+;enable_alpha = false
+;app_tls_skip_verify_insecure = false
+# Enter a comma-separated list of plugin identifiers to identify plugins to load even if they are unsigned. Plugins with modified signatures are never loaded.
+;allow_loading_unsigned_plugins =
+# Enable or disable installing / uninstalling / updating plugins directly from within Grafana.
+;plugin_admin_enabled = false
+;plugin_admin_external_manage_enabled = false
+;plugin_catalog_url = https://grafana.com/grafana/plugins/
+# Enter a comma-separated list of plugin identifiers to hide in the plugin catalog.
+;plugin_catalog_hidden_plugins =
+
+#################################### Grafana Live ##########################################
+[live]
+# max_connections to Grafana Live WebSocket endpoint per Grafana server instance. See Grafana Live docs
+# if you are planning to make it higher than default 100 since this can require some OS and infrastructure
+# tuning. 0 disables Live, -1 means unlimited connections.
+;max_connections = 100
+
+# allowed_origins is a comma-separated list of origins that can establish connection with Grafana Live.
+# If not set then origin will be matched over root_url. Supports wildcard symbol "*".
+;allowed_origins =
+
+# engine defines an HA (high availability) engine to use for Grafana Live. By default no engine used - in
+# this case Live features work only on a single Grafana server. Available options: "redis".
+# Setting ha_engine is an EXPERIMENTAL feature.
+;ha_engine =
+
+# ha_engine_address sets a connection address for Live HA engine. Depending on engine type address format can differ.
+# For now we only support Redis connection address in "host:port" format.
+# This option is EXPERIMENTAL.
+;ha_engine_address = "127.0.0.1:6379"
+
+#################################### Grafana Image Renderer Plugin ##########################
+[plugin.grafana-image-renderer]
+# Instruct headless browser instance to use a default timezone when not provided by Grafana, e.g. when rendering panel image of alert.
+# See ICU’s metaZones.txt (https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt) for a list of supported
+# timezone IDs. Fallbacks to TZ environment variable if not set.
+;rendering_timezone =
+
+# Instruct headless browser instance to use a default language when not provided by Grafana, e.g. when rendering panel image of alert.
+# Please refer to the HTTP header Accept-Language to understand how to format this value, e.g. 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5'.
+;rendering_language =
+
+# Instruct headless browser instance to use a default device scale factor when not provided by Grafana, e.g. when rendering panel image of alert.
+# Default is 1. Using a higher value will produce more detailed images (higher DPI), but will require more disk space to store an image.
+;rendering_viewport_device_scale_factor =
+
+# Instruct headless browser instance whether to ignore HTTPS errors during navigation. Per default HTTPS errors are not ignored. Due to
+# the security risk it's not recommended to ignore HTTPS errors.
+;rendering_ignore_https_errors =
+
+# Instruct headless browser instance whether to capture and log verbose information when rendering an image. Default is false and will
+# only capture and log error messages. When enabled, debug messages are captured and logged as well.
+# For the verbose information to be included in the Grafana server log you have to adjust the rendering log level to debug, configure
+# [log].filter = rendering:debug.
+;rendering_verbose_logging =
+
+# Instruct headless browser instance whether to output its debug and error messages into running process of remote rendering service.
+# Default is false. This can be useful to enable (true) when troubleshooting.
+;rendering_dumpio =
+
+# Additional arguments to pass to the headless browser instance. Default is --no-sandbox. The list of Chromium flags can be found
+# here (https://peter.sh/experiments/chromium-command-line-switches/). Multiple arguments is separated with comma-character.
+;rendering_args =
+
+# You can configure the plugin to use a different browser binary instead of the pre-packaged version of Chromium.
+# Please note that this is not recommended, since you may encounter problems if the installed version of Chrome/Chromium is not
+# compatible with the plugin.
+;rendering_chrome_bin =
+
+# Instruct how headless browser instances are created. Default is 'default' and will create a new browser instance on each request.
+# Mode 'clustered' will make sure that only a maximum of browsers/incognito pages can execute concurrently.
+# Mode 'reusable' will have one browser instance and will create a new incognito page on each request.
+;rendering_mode =
+
+# When rendering_mode = clustered, you can instruct how many browsers or incognito pages can execute concurrently. Default is 'browser'
+# and will cluster using browser instances.
+# Mode 'context' will cluster using incognito pages.
+;rendering_clustering_mode =
+# When rendering_mode = clustered, you can define the maximum number of browser instances/incognito pages that can execute concurrently. Default is '5'.
+;rendering_clustering_max_concurrency =
+# When rendering_mode = clustered, you can specify the duration a rendering request can take before it will time out. Default is `30` seconds.
+;rendering_clustering_timeout =
+
+# Limit the maximum viewport width, height and device scale factor that can be requested.
+;rendering_viewport_max_width =
+;rendering_viewport_max_height =
+;rendering_viewport_max_device_scale_factor =
+
+# Change the listening host and port of the gRPC server. Default host is 127.0.0.1 and default port is 0 and will automatically assign
+# a port not in use.
+;grpc_host =
+;grpc_port =
+
+[enterprise]
+# Path to a valid Grafana Enterprise license.jwt file
+;license_path =
+
+[feature_toggles]
+# there are currently two ways to enable feature toggles in the `grafana.ini`.
+# you can either pass an array of feature you want to enable to the `enable` field or
+# configure each toggle by setting the name of the toggle to true/false. Toggles set to true/false
+# will take presidence over toggles in the `enable` list.
+
+;enable = feature1,feature2
+
+;feature1 = true
+;feature2 = false
+
+[date_formats]
+# For information on what formatting patterns that are supported https://momentjs.com/docs/#/displaying/
+
+# Default system date format used in time range picker and other places where full time is displayed
+;full_date = YYYY-MM-DD HH:mm:ss
+
+# Used by graph and other places where we only show small intervals
+;interval_second = HH:mm:ss
+;interval_minute = HH:mm
+;interval_hour = MM/DD HH:mm
+;interval_day = MM/DD
+;interval_month = YYYY-MM
+;interval_year = YYYY
+
+# Experimental feature
+;use_browser_locale = false
+
+# Default timezone for user preferences. Options are 'browser' for the browser local timezone or a timezone name from IANA Time Zone database, e.g. 'UTC' or 'Europe/Amsterdam' etc.
+;default_timezone = browser
+
+[expressions]
+# Enable or disable the expressions functionality.
+;enabled = true
+
+[geomap]
+# Set the JSON configuration for the default basemap
+;default_baselayer_config = `{
+;  "type": "xyz",
+;  "config": {
+;    "attribution": "Open street map",
+;    "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
+;  }
+;}`
+
+# Enable or disable loading other base map layers
+;enable_custom_baselayers = true
diff --git a/otel/grafana/provisioning/dashboards/general.yaml b/otel/grafana/provisioning/dashboards/general.yaml
new file mode 100644 (file)
index 0000000..f53d3a9
--- /dev/null
@@ -0,0 +1,10 @@
+apiVersion: 1
+providers:
+  - name: 'OpenTelemetry Demo'
+    orgId: 1
+    folder: 'Demo'
+    type: file
+    disableDeletion: false
+    editable: true
+    options:
+      path: /etc/grafana/provisioning/dashboards/general
diff --git a/otel/grafana/provisioning/dashboards/general/demo-dashboard.json b/otel/grafana/provisioning/dashboards/general/demo-dashboard.json
new file mode 100644 (file)
index 0000000..2d2a660
--- /dev/null
@@ -0,0 +1,693 @@
+{
+    "annotations": {
+      "list": [
+        {
+          "builtIn": 1,
+          "datasource": {
+            "type": "grafana",
+            "uid": "-- Grafana --"
+          },
+          "enable": true,
+          "hide": true,
+          "iconColor": "rgba(0, 211, 255, 1)",
+          "name": "Annotations & Alerts",
+          "target": {
+            "limit": 100,
+            "matchAny": false,
+            "tags": [],
+            "type": "dashboard"
+          },
+          "type": "dashboard"
+        }
+      ]
+    },
+    "editable": true,
+    "fiscalYearStartMonth": 0,
+    "graphTooltip": 0,
+    "id": 1,
+    "links": [],
+    "liveNow": false,
+    "panels": [
+      {
+        "collapsed": false,
+        "gridPos": {
+          "h": 1,
+          "w": 24,
+          "x": 0,
+          "y": 0
+        },
+        "id": 14,
+        "panels": [],
+        "title": "Metrics",
+        "type": "row"
+      },
+      {
+        "datasource": {
+          "type": "prometheus",
+          "uid": "webstore-metrics"
+        },
+        "fieldConfig": {
+          "defaults": {
+            "color": {
+              "mode": "palette-classic"
+            },
+            "custom": {
+              "axisCenteredZero": false,
+              "axisColorMode": "text",
+              "axisLabel": "",
+              "axisPlacement": "auto",
+              "barAlignment": 0,
+              "drawStyle": "line",
+              "fillOpacity": 0,
+              "gradientMode": "none",
+              "hideFrom": {
+                "legend": false,
+                "tooltip": false,
+                "viz": false
+              },
+              "lineInterpolation": "linear",
+              "lineWidth": 1,
+              "pointSize": 5,
+              "scaleDistribution": {
+                "type": "linear"
+              },
+              "showPoints": "auto",
+              "spanNulls": false,
+              "stacking": {
+                "group": "A",
+                "mode": "none"
+              },
+              "thresholdsStyle": {
+                "mode": "off"
+              }
+            },
+            "mappings": [],
+            "thresholds": {
+              "mode": "absolute",
+              "steps": [
+                {
+                  "color": "green",
+                  "value": null
+                },
+                {
+                  "color": "red",
+                  "value": 80
+                }
+              ]
+            },
+            "unit": "percent"
+          },
+          "overrides": []
+        },
+        "gridPos": {
+          "h": 8,
+          "w": 12,
+          "x": 0,
+          "y": 1
+        },
+        "id": 6,
+        "options": {
+          "legend": {
+            "calcs": [],
+            "displayMode": "list",
+            "placement": "bottom",
+            "showLegend": true
+          },
+          "tooltip": {
+            "mode": "single",
+            "sort": "none"
+          }
+        },
+        "targets": [
+          {
+            "datasource": {
+              "type": "prometheus",
+              "uid": "webstore-metrics"
+            },
+            "editorMode": "code",
+            "expr": "rate(runtime_cpython_cpu_time{type=~\"system\"}[$__interval])*100",
+            "legendFormat": "__auto",
+            "range": true,
+            "refId": "A"
+          },
+          {
+            "datasource": {
+              "type": "prometheus",
+              "uid": "webstore-metrics"
+            },
+            "editorMode": "code",
+            "expr": "rate(runtime_cpython_cpu_time{type=~\"user\"}[$__interval])*100",
+            "hide": false,
+            "legendFormat": "__auto",
+            "range": true,
+            "refId": "B"
+          }
+        ],
+        "title": "Recommendation Service (CPU%)",
+        "type": "timeseries"
+      },
+      {
+        "datasource": {
+          "type": "prometheus",
+          "uid": "webstore-metrics"
+        },
+        "fieldConfig": {
+          "defaults": {
+            "color": {
+              "mode": "palette-classic"
+            },
+            "custom": {
+              "axisCenteredZero": false,
+              "axisColorMode": "text",
+              "axisLabel": "",
+              "axisPlacement": "auto",
+              "barAlignment": 0,
+              "drawStyle": "line",
+              "fillOpacity": 0,
+              "gradientMode": "none",
+              "hideFrom": {
+                "legend": false,
+                "tooltip": false,
+                "viz": false
+              },
+              "lineInterpolation": "linear",
+              "lineWidth": 1,
+              "pointSize": 5,
+              "scaleDistribution": {
+                "type": "linear"
+              },
+              "showPoints": "auto",
+              "spanNulls": false,
+              "stacking": {
+                "group": "A",
+                "mode": "none"
+              },
+              "thresholdsStyle": {
+                "mode": "off"
+              }
+            },
+            "mappings": [],
+            "thresholds": {
+              "mode": "absolute",
+              "steps": [
+                {
+                  "color": "green",
+                  "value": null
+                },
+                {
+                  "color": "red",
+                  "value": 80
+                }
+              ]
+            },
+            "unit": "decmbytes"
+          },
+          "overrides": []
+        },
+        "gridPos": {
+          "h": 8,
+          "w": 12,
+          "x": 12,
+          "y": 1
+        },
+        "id": 8,
+        "options": {
+          "legend": {
+            "calcs": [],
+            "displayMode": "list",
+            "placement": "bottom",
+            "showLegend": true
+          },
+          "tooltip": {
+            "mode": "single",
+            "sort": "none"
+          }
+        },
+        "targets": [
+          {
+            "datasource": {
+              "type": "prometheus",
+              "uid": "webstore-metrics"
+            },
+            "editorMode": "code",
+            "expr": "rate(runtime_cpython_memory{type=~\"rss|vms\"}[$__interval])/1024/1024",
+            "legendFormat": "__auto",
+            "range": true,
+            "refId": "A"
+          }
+        ],
+        "title": "Recommendation Service (Memory)",
+        "type": "timeseries"
+      },
+      {
+        "datasource": {
+          "type": "prometheus",
+          "uid": "webstore-metrics"
+        },
+        "fieldConfig": {
+          "defaults": {
+            "color": {
+              "mode": "palette-classic"
+            },
+            "custom": {
+              "axisCenteredZero": false,
+              "axisColorMode": "text",
+              "axisLabel": "",
+              "axisPlacement": "auto",
+              "barAlignment": 0,
+              "drawStyle": "bars",
+              "fillOpacity": 0,
+              "gradientMode": "none",
+              "hideFrom": {
+                "legend": false,
+                "tooltip": false,
+                "viz": false
+              },
+              "lineInterpolation": "linear",
+              "lineWidth": 1,
+              "pointSize": 5,
+              "scaleDistribution": {
+                "type": "linear"
+              },
+              "showPoints": "auto",
+              "spanNulls": false,
+              "stacking": {
+                "group": "A",
+                "mode": "none"
+              },
+              "thresholdsStyle": {
+                "mode": "off"
+              }
+            },
+            "mappings": [],
+            "thresholds": {
+              "mode": "absolute",
+              "steps": [
+                {
+                  "color": "green",
+                  "value": null
+                },
+                {
+                  "color": "red",
+                  "value": 80
+                }
+              ]
+            }
+          },
+          "overrides": []
+        },
+        "gridPos": {
+          "h": 8,
+          "w": 12,
+          "x": 0,
+          "y": 9
+        },
+        "id": 4,
+        "options": {
+          "legend": {
+            "calcs": [],
+            "displayMode": "list",
+            "placement": "bottom",
+            "showLegend": true
+          },
+          "tooltip": {
+            "mode": "single",
+            "sort": "none"
+          }
+        },
+        "targets": [
+          {
+            "datasource": {
+              "type": "prometheus",
+              "uid": "webstore-metrics"
+            },
+            "editorMode": "code",
+            "expr": "rate(app_recommendations_counter{recommendation_type=\"catalog\"}[$__interval])",
+            "legendFormat": "__auto",
+            "range": true,
+            "refId": "A"
+          }
+        ],
+        "title": "Recommendations Count",
+        "type": "timeseries"
+      },
+      {
+        "datasource": {
+          "type": "prometheus",
+          "uid": "webstore-metrics"
+        },
+        "fieldConfig": {
+          "defaults": {
+            "color": {
+              "mode": "palette-classic"
+            },
+            "custom": {
+              "axisCenteredZero": false,
+              "axisColorMode": "text",
+              "axisLabel": "",
+              "axisPlacement": "auto",
+              "barAlignment": 0,
+              "drawStyle": "line",
+              "fillOpacity": 0,
+              "gradientMode": "none",
+              "hideFrom": {
+                "legend": false,
+                "tooltip": false,
+                "viz": false
+              },
+              "lineInterpolation": "linear",
+              "lineWidth": 1,
+              "pointSize": 5,
+              "scaleDistribution": {
+                "type": "linear"
+              },
+              "showPoints": "auto",
+              "spanNulls": false,
+              "stacking": {
+                "group": "A",
+                "mode": "none"
+              },
+              "thresholdsStyle": {
+                "mode": "off"
+              }
+            },
+            "mappings": [],
+            "thresholds": {
+              "mode": "absolute",
+              "steps": [
+                {
+                  "color": "green",
+                  "value": null
+                },
+                {
+                  "color": "red",
+                  "value": 80
+                }
+              ]
+            }
+          },
+          "overrides": []
+        },
+        "gridPos": {
+          "h": 8,
+          "w": 12,
+          "x": 12,
+          "y": 9
+        },
+        "id": 10,
+        "options": {
+          "legend": {
+            "calcs": [],
+            "displayMode": "list",
+            "placement": "bottom",
+            "showLegend": true
+          },
+          "tooltip": {
+            "mode": "single",
+            "sort": "none"
+          }
+        },
+        "targets": [
+          {
+            "datasource": {
+              "type": "prometheus",
+              "uid": "webstore-metrics"
+            },
+            "editorMode": "code",
+            "expr": "rate(calls_total{status_code=\"STATUS_CODE_ERROR\"}[$__interval])",
+            "legendFormat": "__auto",
+            "range": true,
+            "refId": "A"
+          }
+        ],
+        "title": "Error Rate",
+        "type": "timeseries"
+      },
+      {
+        "datasource": {
+          "type": "prometheus",
+          "uid": "webstore-metrics"
+        },
+        "fieldConfig": {
+          "defaults": {
+            "color": {
+              "mode": "palette-classic"
+            },
+            "custom": {
+              "axisCenteredZero": false,
+              "axisColorMode": "text",
+              "axisLabel": "",
+              "axisPlacement": "auto",
+              "barAlignment": 0,
+              "drawStyle": "line",
+              "fillOpacity": 0,
+              "gradientMode": "none",
+              "hideFrom": {
+                "legend": false,
+                "tooltip": false,
+                "viz": false
+              },
+              "lineInterpolation": "linear",
+              "lineWidth": 1,
+              "pointSize": 5,
+              "scaleDistribution": {
+                "type": "linear"
+              },
+              "showPoints": "auto",
+              "spanNulls": false,
+              "stacking": {
+                "group": "A",
+                "mode": "none"
+              },
+              "thresholdsStyle": {
+                "mode": "off"
+              }
+            },
+            "mappings": [],
+            "thresholds": {
+              "mode": "absolute",
+              "steps": [
+                {
+                  "color": "green",
+                  "value": null
+                },
+                {
+                  "color": "red",
+                  "value": 80
+                }
+              ]
+            },
+            "unit": "dtdurationms"
+          },
+          "overrides": []
+        },
+        "gridPos": {
+          "h": 8,
+          "w": 12,
+          "x": 0,
+          "y": 17
+        },
+        "id": 2,
+        "options": {
+          "legend": {
+            "calcs": [],
+            "displayMode": "list",
+            "placement": "bottom",
+            "showLegend": true
+          },
+          "tooltip": {
+            "mode": "single",
+            "sort": "none"
+          }
+        },
+        "targets": [
+          {
+            "datasource": {
+              "type": "prometheus",
+              "uid": "webstore-metrics"
+            },
+            "editorMode": "code",
+            "expr": "histogram_quantile(0.50, sum(rate(latency_bucket{service_name=\"${service}\"}[$__rate_interval])) by (le))",
+            "legendFormat": "__auto",
+            "range": true,
+            "refId": "A"
+          },
+          {
+            "datasource": {
+              "type": "prometheus",
+              "uid": "webstore-metrics"
+            },
+            "editorMode": "code",
+            "expr": "histogram_quantile(0.95, sum(rate(latency_bucket{service_name=\"${service}\"}[$__rate_interval])) by (le))",
+            "hide": false,
+            "legendFormat": "__auto",
+            "range": true,
+            "refId": "B"
+          },
+          {
+            "datasource": {
+              "type": "prometheus",
+              "uid": "webstore-metrics"
+            },
+            "editorMode": "code",
+            "expr": "histogram_quantile(0.99, sum(rate(latency_bucket{service_name=\"${service}\"}[$__rate_interval])) by (le))",
+            "hide": false,
+            "legendFormat": "__auto",
+            "range": true,
+            "refId": "C"
+          },
+          {
+            "datasource": {
+              "type": "prometheus",
+              "uid": "webstore-metrics"
+            },
+            "editorMode": "code",
+            "expr": "histogram_quantile(0.999, sum(rate(latency_bucket{service_name=\"${service}\"}[$__rate_interval])) by (le))",
+            "hide": false,
+            "legendFormat": "__auto",
+            "range": true,
+            "refId": "D"
+          }
+        ],
+        "title": "Service Latency (from SpanMetrics)",
+        "type": "timeseries"
+      },
+      {
+        "datasource": {
+          "type": "prometheus",
+          "uid": "webstore-metrics"
+        },
+        "fieldConfig": {
+          "defaults": {
+            "color": {
+              "mode": "palette-classic"
+            },
+            "custom": {
+              "axisCenteredZero": false,
+              "axisColorMode": "text",
+              "axisLabel": "",
+              "axisPlacement": "auto",
+              "barAlignment": 0,
+              "drawStyle": "line",
+              "fillOpacity": 0,
+              "gradientMode": "none",
+              "hideFrom": {
+                "legend": false,
+                "tooltip": false,
+                "viz": false
+              },
+              "lineInterpolation": "linear",
+              "lineWidth": 1,
+              "pointSize": 5,
+              "scaleDistribution": {
+                "type": "linear"
+              },
+              "showPoints": "auto",
+              "spanNulls": false,
+              "stacking": {
+                "group": "A",
+                "mode": "none"
+              },
+              "thresholdsStyle": {
+                "mode": "off"
+              }
+            },
+            "mappings": [],
+            "thresholds": {
+              "mode": "absolute",
+              "steps": [
+                {
+                  "color": "green",
+                  "value": null
+                },
+                {
+                  "color": "red",
+                  "value": 80
+                }
+              ]
+            },
+            "unit": "reqps"
+          },
+          "overrides": []
+        },
+        "gridPos": {
+          "h": 8,
+          "w": 12,
+          "x": 12,
+          "y": 17
+        },
+        "id": 12,
+        "options": {
+          "legend": {
+            "calcs": [],
+            "displayMode": "list",
+            "placement": "bottom",
+            "showLegend": true
+          },
+          "tooltip": {
+            "mode": "single",
+            "sort": "none"
+          }
+        },
+        "targets": [
+          {
+            "datasource": {
+              "type": "prometheus",
+              "uid": "webstore-metrics"
+            },
+            "editorMode": "code",
+            "expr": "rate(latency_count{service_name=\"${service}\"}[$__rate_interval])",
+            "legendFormat": "__auto",
+            "range": true,
+            "refId": "A"
+          }
+        ],
+        "title": "Endpoint Rate by Service",
+        "type": "timeseries"
+      }
+    ],
+    "schemaVersion": 37,
+    "style": "dark",
+    "tags": [],
+    "templating": {
+      "list": [
+        {
+          "allValue": "",
+          "current": {
+            "selected": false,
+            "text": "recommendationservice",
+            "value": "recommendationservice"
+          },
+          "datasource": {
+            "type": "prometheus",
+            "uid": "webstore-metrics"
+          },
+          "definition": "latency_bucket",
+          "hide": 0,
+          "includeAll": false,
+          "multi": false,
+          "name": "service",
+          "options": [],
+          "query": {
+            "query": "latency_bucket",
+            "refId": "StandardVariableQuery"
+          },
+          "refresh": 1,
+          "regex": "/.*service_name=\\\"([^\\\"]+)\\\".*/",
+          "skipUrlSync": false,
+          "sort": 1,
+          "type": "query"
+        }
+      ]
+    },
+    "time": {
+      "from": "now-15m",
+      "to": "now"
+    },
+    "timepicker": {},
+    "timezone": "",
+    "title": "Demo Dashboard",
+    "uid": "W2gX2zHVk",
+    "version": 2,
+    "weekStart": ""
+  }
\ No newline at end of file
diff --git a/otel/grafana/provisioning/dashboards/general/opentelemetry-collector.json b/otel/grafana/provisioning/dashboards/general/opentelemetry-collector.json
new file mode 100644 (file)
index 0000000..3cc0d28
--- /dev/null
@@ -0,0 +1,1554 @@
+{
+  "__inputs": [
+    {
+      "name": "webstore",
+      "label": "Prometheus",
+      "description": "",
+      "type": "datasource",
+      "pluginId": "prometheus",
+      "pluginName": "Prometheus"
+    }
+  ],
+  "__requires": [
+    {
+      "type": "grafana",
+      "id": "grafana",
+      "name": "Grafana",
+      "version": "7.0.3"
+    },
+    {
+      "type": "panel",
+      "id": "graph",
+      "name": "Graph",
+      "version": ""
+    },
+    {
+      "type": "datasource",
+      "id": "prometheus",
+      "name": "Prometheus",
+      "version": "1.0.0"
+    },
+    {
+      "type": "panel",
+      "id": "stat",
+      "name": "Stat",
+      "version": ""
+    }
+  ],
+  "annotations": {
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": "-- Grafana --",
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "type": "dashboard"
+      }
+    ]
+  },
+  "description": "Provides information about the status of the OpenTelemetry Collector",
+  "editable": true,
+  "gnetId": 12553,
+  "graphTooltip": 0,
+  "id": 22,
+  "iteration": 1593502896956,
+  "links": [],
+  "panels": [
+    {
+      "collapsed": false,
+      "datasource": "Prometheus",
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 0
+      },
+      "id": 23,
+      "panels": [],
+      "title": "Receivers",
+      "type": "row"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "description": "Rate of spans successfully accepted vs refused per second",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 0,
+        "y": 1
+      },
+      "hiddenSeries": false,
+      "id": 28,
+      "interval": "",
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(rate(otelcol_receiver_accepted_spans{receiver=~\"$receiver\"}[1m])) by (receiver)",
+          "format": "time_series",
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "{{receiver}} spans accepted / sec",
+          "refId": "A"
+        },
+        {
+          "expr": "sum(rate(otelcol_receiver_refused_spans{receiver=~\"$receiver\"}[1m])) by (receiver)",
+          "interval": "",
+          "legendFormat": "{{ receiver }} spans refused /sec",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Spans Accepted vs Refused /Second",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "description": "Rate of metrics successfully accepted vs refused per second",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 12,
+        "y": 1
+      },
+      "hiddenSeries": false,
+      "id": 19,
+      "interval": "",
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(rate(otelcol_receiver_accepted_metric_points{receiver=~\"$receiver\"}[1m])) by (receiver)",
+          "format": "time_series",
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "{{receiver}} accepted /sec",
+          "refId": "A"
+        },
+        {
+          "expr": "sum(rate(otelcol_receiver_refused_metric_points{receiver=~\"$receiver\"}[1m])) by (receiver)",
+          "interval": "",
+          "legendFormat": "{{ receiver }} refused / sec",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Metrics Accepted vs Refused Received/Second",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "collapsed": false,
+      "datasource": "Prometheus",
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 10
+      },
+      "id": 25,
+      "panels": [],
+      "title": "Exporters",
+      "type": "row"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "description": "Rate of spans successfully exported vs Failed per second",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 0,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 0,
+        "y": 11
+      },
+      "hiddenSeries": false,
+      "id": 30,
+      "interval": "",
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(rate(otelcol_exporter_sent_spans{exporter=~\"$exporter\"}[1m])) by (exporter)",
+          "format": "time_series",
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "{{exporter}} sent / sec",
+          "refId": "A"
+        },
+        {
+          "expr": "sum(rate(otelcol_exporter_send_failed_spans{exporter=~\"$exporter\"}[1m])) by (exporter)",
+          "interval": "",
+          "legendFormat": "{{ exporter }} failed /sec",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Spans Exported vs Failed / Second",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "description": "Rate of timeseries successfully exported per second",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 12,
+        "y": 11
+      },
+      "hiddenSeries": false,
+      "id": 31,
+      "interval": "",
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(rate(otelcol_exporter_sent_metric_points{exporter=~\"$exporter\"}[1m])) by (exporter)",
+          "format": "time_series",
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "{{exporter}} sent /sec",
+          "refId": "A"
+        },
+        {
+          "expr": "sum(rate(otelcol_exporter_send_failed_metric_points{exporter=~\"$exporter\"}[1m])) by (exporter)",
+          "interval": "",
+          "legendFormat": "{{ exporter }} failed / sec",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Metrics Exported vs failed /Second",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "collapsed": false,
+      "datasource": "Prometheus",
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 20
+      },
+      "id": 21,
+      "panels": [],
+      "repeat": null,
+      "title": "Processors",
+      "type": "row"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 8,
+        "x": 0,
+        "y": 21
+      },
+      "hiddenSeries": false,
+      "id": 10,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "max(otelcol_processor_queued_retry_queue_length)",
+          "format": "time_series",
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "Queue Length",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Queued Retry Max Queue Length",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 8,
+        "x": 8,
+        "y": 21
+      },
+      "hiddenSeries": false,
+      "id": 11,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(otelcol_processor_queued_retry_queue_latency_sum/ otelcol_processor_queued_retry_queue_latency_count)",
+          "format": "time_series",
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "ms",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Queued Retry Processor In-queue Latency (ms)",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 8,
+        "x": 16,
+        "y": 21
+      },
+      "hiddenSeries": false,
+      "id": 32,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(otelcol_processor_queued_retry_send_latency_sum/ otelcol_processor_queued_retry_send_latency_count)",
+          "format": "time_series",
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "ms",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Queued Retry Processor Send Latency (ms)",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 0,
+        "y": 30
+      },
+      "hiddenSeries": false,
+      "id": 5,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(rate(otelcol_processor_spans_received{processor=~\"$processor\"}[1m])) by (processor)",
+          "format": "time_series",
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "{{processor}} | received",
+          "refId": "A"
+        },
+        {
+          "expr": "sum(rate(otelcol_processor_spans_dropped{processor=~\"$processor\"}[1m])) by (processor)",
+          "interval": "",
+          "legendFormat": "{{processor}} | dropped",
+          "refId": "B"
+        },
+        {
+          "expr": "sum(rate(otelcol_processor_accepted_spans{processor=~\"$processor\"}[1m])) by (processor)",
+          "interval": "",
+          "legendFormat": "{{processor}} | accepted",
+          "refId": "C"
+        },
+        {
+          "expr": "sum(rate(otelcol_processor_refused_spans{processor=~\"$processor\"}[1m])) by (processor)",
+          "interval": "",
+          "legendFormat": "{{processor}} | refused",
+          "refId": "E"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Processor Spans Received, Dropped, Accepted, Refused/Second",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "decimals": null,
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 12,
+        "y": 30
+      },
+      "hiddenSeries": false,
+      "id": 14,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(rate(otelcol_processor_queued_retry_success_send{processor=~\"$processor\"}[1m])) by (processor)",
+          "format": "time_series",
+          "hide": false,
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "{{processor}}",
+          "refId": "A"
+        },
+        {
+          "expr": "sum(rate(otelcol_processor_queued_retry_fail_send{processor=~\"$processor\"}[1m])) by (processor)",
+          "interval": "",
+          "legendFormat": "{{processor}}",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "QueuedRetry Successful vs Failed Sent/Second",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 0,
+        "y": 39
+      },
+      "hiddenSeries": false,
+      "id": 2,
+      "interval": "",
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(rate(otelcol_processor_spans_received{processor=~\"$processor\"}[1m]) / rate(otelcol_processor_batches_received{processor=~\"$processor\"}[1m])) by (processor)",
+          "format": "time_series",
+          "hide": false,
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "{{processor}}",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Spans per Batch (avg from rates)",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 12,
+        "y": 39
+      },
+      "hiddenSeries": false,
+      "id": 12,
+      "interval": "",
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(rate(otelcol_processor_batches_received{processor=~\"$processor\"}[1m])) by (processor)",
+          "format": "time_series",
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "{{processor}} | received",
+          "refId": "A"
+        },
+        {
+          "expr": "sum(rate(otelcol_processor_trace_batches_dropped{processor=~\"$processor\"}[1m])) by (processor)",
+          "interval": "",
+          "legendFormat": "{{processor}} | trace dropped",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Batches Received vs Dropped /Second",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 0,
+        "y": 48
+      },
+      "hiddenSeries": false,
+      "id": 6,
+      "interval": "",
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(otelcol_processor_queued_retry_success_send{processor=~\"$processor\"}) by (processor)",
+          "format": "time_series",
+          "hide": false,
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "{{processor}}",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Successful Batches Sent Cumulative",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Prometheus",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 12,
+        "y": 48
+      },
+      "hiddenSeries": false,
+      "id": 3,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(otelcol_processor_spans_received{exporter=~\"$exporter\"}) by (exporter)",
+          "format": "time_series",
+          "hide": false,
+          "interval": "",
+          "intervalFactor": 1,
+          "legendFormat": "{{exporter}}",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Enqueued Spans Cumulative",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    }
+  ],
+  "refresh": false,
+  "schemaVersion": 25,
+  "style": "dark",
+  "tags": [
+    "opentelemetry"
+  ],
+  "templating": {
+    "list": [
+      {
+        "allValue": ".*",
+        "current": {
+          "selected": false,
+          "text": "All",
+          "value": "$__all"
+        },
+        "datasource": "Prometheus",
+        "definition": "label_values(otelsvc_receiver)",
+        "hide": 0,
+        "includeAll": true,
+        "label": "receiver",
+        "multi": true,
+        "name": "receiver",
+        "options": [],
+        "query": "label_values(otelsvc_receiver)",
+        "refresh": 2,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 0,
+        "tagValuesQuery": "",
+        "tags": [],
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "allValue": ".*",
+        "current": {
+          "selected": false,
+          "text": "All",
+          "value": "$__all"
+        },
+        "datasource": "Prometheus",
+        "definition": "label_values(otelsvc_exporter)",
+        "hide": 0,
+        "includeAll": true,
+        "label": "exporter",
+        "multi": true,
+        "name": "exporter",
+        "options": [],
+        "query": "label_values(otelsvc_exporter)",
+        "refresh": 2,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 0,
+        "tagValuesQuery": "",
+        "tags": [],
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "allValue": null,
+        "current": {
+          "selected": false,
+          "text": "All",
+          "value": "$__all"
+        },
+        "datasource": "Prometheus",
+        "definition": "label_values(processor)",
+        "hide": 0,
+        "includeAll": true,
+        "label": "processor",
+        "multi": true,
+        "name": "processor",
+        "options": [],
+        "query": "label_values(processor)",
+        "refresh": 2,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tags": [],
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      }
+    ]
+  },
+  "time": {
+    "from": "now-15m",
+    "to": "now"
+  },
+  "timepicker": {
+    "refresh_intervals": [
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "time_options": [
+      "5m",
+      "15m",
+      "1h",
+      "6h",
+      "12h",
+      "24h",
+      "2d",
+      "7d",
+      "30d"
+    ]
+  },
+  "timezone": "",
+  "title": "OpenTelemetry Collector",
+  "uid": "BKf2sowmj",
+  "version": 7
+}
diff --git a/otel/grafana/provisioning/datasources/default.yaml b/otel/grafana/provisioning/datasources/default.yaml
new file mode 100644 (file)
index 0000000..3efab92
--- /dev/null
@@ -0,0 +1,9 @@
+apiVersion: 1
+
+datasources:
+  - name: Prometheus
+    uid: webstore-metrics
+    type: prometheus
+    url: http://prometheus:9090
+    editable: true
+    isDefault: true
diff --git a/otel/grafana/provisioning/datasources/jaeger.yaml b/otel/grafana/provisioning/datasources/jaeger.yaml
new file mode 100644 (file)
index 0000000..75b211f
--- /dev/null
@@ -0,0 +1,9 @@
+apiVersion: 1
+
+datasources:
+  - name: Jaeger
+    uid: webstore-traces
+    type: jaeger
+    url: http://jaeger:16686/jaeger/ui
+    editable: true
+    isDefault: false
diff --git a/otel/otelcollector/otelcol-config-extras.yml b/otel/otelcollector/otelcol-config-extras.yml
new file mode 100644 (file)
index 0000000..9b76acd
--- /dev/null
@@ -0,0 +1,2 @@
+# extra settings to be merged into OpenTelemetry Collector configuration
+# do not delete this file
diff --git a/otel/otelcollector/otelcol-config.yml b/otel/otelcollector/otelcol-config.yml
new file mode 100644 (file)
index 0000000..b9a77e4
--- /dev/null
@@ -0,0 +1,34 @@
+receivers:
+  otlp:
+    protocols:
+      grpc:
+      http:
+        cors:
+          allowed_origins:
+            - "http://*"
+            - "https://*"
+
+exporters:
+  otlp:
+    endpoint: "jaeger:4317"
+    tls:
+      insecure: true
+  logging:
+  prometheus:
+    endpoint: "otelcol:9464"
+
+processors:
+  batch:
+  spanmetrics:
+    metrics_exporter: prometheus
+
+service:
+  pipelines:
+    traces:
+      receivers: [otlp]
+      processors: [spanmetrics, batch]
+      exporters: [logging, otlp]
+    metrics:
+      receivers: [otlp]
+      processors: [batch]
+      exporters: [prometheus, logging]
diff --git a/otel/prometheus/prometheus-config.yaml b/otel/prometheus/prometheus-config.yaml
new file mode 100644 (file)
index 0000000..08f7220
--- /dev/null
@@ -0,0 +1,12 @@
+global:
+  evaluation_interval: 30s
+  scrape_interval: 5s
+scrape_configs:
+- job_name: otel
+  static_configs:
+  - targets:
+    - 'otelcol:9464'
+- job_name: otel-collector
+  static_configs:
+  - targets:
+    - 'otelcol:8888'