diff --git a/.all-contributorsrc b/.all-contributorsrc
index aeca5db15fb..a750612cb36 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -182,7 +182,8 @@
"profile": "https://github.com/aknishid",
"contributions": [
"code",
- "maintenance"
+ "maintenance",
+ "doc"
]
},
{
diff --git a/README.md b/README.md
index a5d4a226ee9..f383078d651 100755
--- a/README.md
+++ b/README.md
@@ -264,9 +264,7 @@ make init
## Contributors
-
[![All Contributors](https://img.shields.io/badge/all_contributors-18-orange.svg?style=flat-square)](#contributors)
-
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
@@ -297,7 +295,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
junsei-ando 📖 |
Yusuke Kadowaki 💻 ⚠️ |
- aknishid 💻 🚧 |
+ aknishid 💻 🚧 📖 |
Hrichik Mazumder 📖 |
diff --git a/docs/user-guides/mirroring-configuration.md b/docs/user-guides/mirroring-configuration.md
index 6926f539856..71687d2f06d 100644
--- a/docs/user-guides/mirroring-configuration.md
+++ b/docs/user-guides/mirroring-configuration.md
@@ -58,7 +58,7 @@ gateway:
The cluster role configuration is required when you deploy Vald clusters with Vald Mirror Gateway on multiple Namespaces in the Kubernetes cluster.
-Please refer to [Cluster Role Configuration](./cluster-role-binding.md) about cluster role settings for Mirror Gateway.
+Please refer to [Cluster Role Configuration](../user-guides/cluster-role-binding.md) about cluster role settings for Mirror Gateway.
### Custom Resource Configuration
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 3220f55f3b8..7ef94ac1a3f 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -161,6 +161,12 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
[[package]]
name = "byteorder"
version = "1.5.0"
@@ -195,6 +201,22 @@ dependencies = [
"unicode-width",
]
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
[[package]]
name = "cxx"
version = "1.0.124"
@@ -245,6 +267,15 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+[[package]]
+name = "encoding_rs"
+version = "0.8.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "equivalent"
version = "1.0.1"
@@ -267,6 +298,15 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
[[package]]
name = "futures-channel"
version = "0.3.30"
@@ -282,6 +322,34 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+[[package]]
+name = "futures-executor"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "futures-sink"
version = "0.3.30"
@@ -301,9 +369,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
"futures-task",
+ "memchr",
"pin-project-lite",
"pin-utils",
+ "slab",
]
[[package]]
@@ -323,6 +396,12 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
[[package]]
name = "h2"
version = "0.3.26"
@@ -430,6 +509,16 @@ dependencies = [
"tokio-io-timeout",
]
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
[[package]]
name = "indexmap"
version = "1.9.3"
@@ -450,6 +539,12 @@ dependencies = [
"hashbrown 0.14.5",
]
+[[package]]
+name = "ipnet"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+
[[package]]
name = "is_ci"
version = "1.2.0"
@@ -471,6 +566,21 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
[[package]]
name = "libc"
version = "0.2.155"
@@ -502,6 +612,12 @@ dependencies = [
"scopeguard",
]
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
[[package]]
name = "matchit"
version = "0.7.3"
@@ -590,6 +706,15 @@ dependencies = [
"rand",
]
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
[[package]]
name = "object"
version = "0.36.3"
@@ -599,12 +724,125 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "observability"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "opentelemetry",
+ "opentelemetry-otlp",
+ "opentelemetry-semantic-conventions",
+ "opentelemetry_sdk",
+ "paste",
+ "scopeguard",
+ "serde_json",
+ "tokio",
+ "url",
+]
+
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+[[package]]
+name = "opentelemetry"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b69a91d4893e713e06f724597ad630f1fa76057a5e1026c0ca67054a9032a76"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+ "js-sys",
+ "once_cell",
+ "pin-project-lite",
+ "thiserror",
+]
+
+[[package]]
+name = "opentelemetry-http"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0ba633e55c5ea6f431875ba55e71664f2fa5d3a90bd34ec9302eecc41c865dd"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "http",
+ "opentelemetry",
+ "reqwest",
+]
+
+[[package]]
+name = "opentelemetry-otlp"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a94c69209c05319cdf7460c6d4c055ed102be242a0a6245835d7bc42c6ec7f54"
+dependencies = [
+ "async-trait",
+ "futures-core",
+ "http",
+ "opentelemetry",
+ "opentelemetry-http",
+ "opentelemetry-proto",
+ "opentelemetry_sdk",
+ "prost",
+ "reqwest",
+ "thiserror",
+ "tokio",
+ "tonic",
+]
+
+[[package]]
+name = "opentelemetry-proto"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "984806e6cf27f2b49282e2a05e288f30594f3dbc74eb7a6e99422bc48ed78162"
+dependencies = [
+ "opentelemetry",
+ "opentelemetry_sdk",
+ "prost",
+ "tonic",
+]
+
+[[package]]
+name = "opentelemetry-semantic-conventions"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cefe0543875379e47eb5f1e68ff83f45cc41366a92dfd0d073d513bf68e9a05"
+
+[[package]]
+name = "opentelemetry_sdk"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae312d58eaa90a82d2e627fd86e075cf5230b3f11794e2ed74199ebbe572d4fd"
+dependencies = [
+ "async-trait",
+ "futures-channel",
+ "futures-executor",
+ "futures-util",
+ "glob",
+ "lazy_static",
+ "once_cell",
+ "opentelemetry",
+ "ordered-float",
+ "percent-encoding",
+ "rand",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+]
+
+[[package]]
+name = "ordered-float"
+version = "4.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a91171844676f8c7990ce64959210cd2eaef32c2612c50f9fae9f8aaa6065a6"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "owo-colors"
version = "4.0.0"
@@ -634,6 +872,12 @@ dependencies = [
"windows-targets 0.52.6",
]
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
[[package]]
name = "percent-encoding"
version = "2.3.1"
@@ -780,6 +1024,42 @@ dependencies = [
"bitflags 2.6.0",
]
+[[package]]
+name = "reqwest"
+version = "0.11.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
+dependencies = [
+ "base64",
+ "bytes",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "hyper",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "system-configuration",
+ "tokio",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "winreg",
+]
+
[[package]]
name = "rustc-demangle"
version = "0.1.24"
@@ -805,6 +1085,12 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -837,6 +1123,30 @@ dependencies = [
"syn",
]
+[[package]]
+name = "serde_json"
+version = "1.0.121"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
@@ -915,6 +1225,27 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+[[package]]
+name = "system-configuration"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "termcolor"
version = "1.4.1"
@@ -965,6 +1296,21 @@ dependencies = [
"syn",
]
+[[package]]
+name = "tinyvec"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
[[package]]
name = "tokio"
version = "1.39.2"
@@ -1136,6 +1482,12 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
[[package]]
name = "unicode-ident"
version = "1.0.12"
@@ -1148,12 +1500,32 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
+[[package]]
+name = "unicode-normalization"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+dependencies = [
+ "tinyvec",
+]
+
[[package]]
name = "unicode-width"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
+[[package]]
+name = "url"
+version = "2.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
[[package]]
name = "want"
version = "0.3.1"
@@ -1169,6 +1541,82 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "web-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "winapi-util"
version = "0.1.9"
@@ -1326,6 +1774,16 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+[[package]]
+name = "winreg"
+version = "0.50.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
+
[[package]]
name = "zerocopy"
version = "0.7.35"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 666259b2b20..1c1c581bb1e 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -14,4 +14,10 @@
# limitations under the License.
#
[workspace]
-members = ["libs/proto", "libs/ngt", "libs/ngt-rs", "bin/agent"]
+members = [
+ "libs/ngt",
+ "libs/ngt-rs",
+ "libs/observability",
+ "libs/proto",
+ "bin/agent",
+]
diff --git a/rust/libs/observability/Cargo.toml b/rust/libs/observability/Cargo.toml
new file mode 100644
index 00000000000..4ef91d715e8
--- /dev/null
+++ b/rust/libs/observability/Cargo.toml
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2019-2024 vdaas.org vald team
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# You may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+[package]
+name = "observability"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+opentelemetry = { version = "0.23" }
+opentelemetry_sdk = { version = "0.23", features = ["rt-tokio"] }
+opentelemetry-otlp = { version = "0.16.0", features = ["http-proto", "reqwest-client", "logs"] }
+tokio = { version = "1.38.0", features = ["full"] }
+serde_json = { version="1.0.120" }
+opentelemetry-semantic-conventions = { version = "0.16.0"}
+scopeguard = { version = "1.2.0"}
+paste = {version = "1.0.15"}
+anyhow = { version = "1.0.86"}
+url = { version = "2.5.2"}
diff --git a/rust/libs/observability/src/config.rs b/rust/libs/observability/src/config.rs
new file mode 100644
index 00000000000..1faa8fd90ea
--- /dev/null
+++ b/rust/libs/observability/src/config.rs
@@ -0,0 +1,142 @@
+//
+// Copyright (C) 2019-2024 vdaas.org vald team
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+use std::collections::HashMap;
+use std::time::Duration;
+
+use opentelemetry::KeyValue;
+use opentelemetry_sdk::{self, Resource};
+
+#[derive(Clone, Debug)]
+pub struct Config {
+ pub enabled: bool,
+ pub endpoint: String,
+ pub attributes: HashMap,
+ pub tracer: Tracer,
+ pub meter: Meter,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct Tracer {
+ pub enabled: bool,
+}
+
+#[derive(Clone, Debug)]
+pub struct Meter {
+ pub enabled: bool,
+ pub export_duration: Duration,
+ pub export_timeout_duration: Duration,
+}
+
+impl Config {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn enabled(mut self, enabled: bool) -> Self {
+ self.enabled = enabled;
+ self
+ }
+
+ pub fn endpoint(mut self, endpoint: &str) -> Self {
+ self.endpoint = endpoint.to_string();
+ self
+ }
+
+ pub fn attributes(mut self, attrs: HashMap) -> Self {
+ self.attributes = attrs;
+ self
+ }
+
+ pub fn attribute(mut self, key: &str, value: &str) -> Self {
+ self.attributes.insert(key.to_string(), value.to_string());
+ self
+ }
+
+ pub fn tracer(mut self, cfg: Tracer) -> Self {
+ self.tracer = cfg;
+ self
+ }
+
+ pub fn meter(mut self, cfg: Meter) -> Self {
+ self.meter = cfg;
+ self
+ }
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ Self {
+ enabled: false,
+ endpoint: "".to_string(),
+ attributes: HashMap::new(),
+ tracer: Tracer::default(),
+ meter: Meter::default(),
+ }
+ }
+}
+
+impl From<&Config> for Resource {
+ fn from(value: &Config) -> Self {
+ let key_values: Vec = value
+ .attributes
+ .iter()
+ .map(|(key, val)| KeyValue::new(key.clone(), val.clone()))
+ .collect();
+ Resource::new(key_values)
+ }
+}
+
+impl Tracer {
+ pub fn new() -> Self {
+ Tracer::default()
+ }
+
+ pub fn enabled(mut self, enabled: bool) -> Self {
+ self.enabled = enabled;
+ self
+ }
+}
+
+impl Meter {
+ pub fn new() -> Self {
+ Meter::default()
+ }
+
+ pub fn enabled(mut self, enabled: bool) -> Self {
+ self.enabled = enabled;
+ self
+ }
+
+ pub fn export_duration(mut self, dur: Duration) -> Self {
+ self.export_duration = dur;
+ self
+ }
+
+ pub fn export_timeout_duration(mut self, dur: Duration) -> Self {
+ self.export_timeout_duration = dur;
+ self
+ }
+}
+
+impl Default for Meter {
+ fn default() -> Self {
+ Self {
+ enabled: false,
+ export_duration: Duration::from_secs(1),
+ export_timeout_duration: Duration::from_secs(5),
+ }
+ }
+}
diff --git a/rust/libs/observability/src/lib.rs b/rust/libs/observability/src/lib.rs
new file mode 100644
index 00000000000..bf0aef235c3
--- /dev/null
+++ b/rust/libs/observability/src/lib.rs
@@ -0,0 +1,19 @@
+//
+// Copyright (C) 2019-2024 vdaas.org vald team
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+pub mod config;
+pub mod macros;
+pub mod observability;
diff --git a/rust/libs/observability/src/macros.rs b/rust/libs/observability/src/macros.rs
new file mode 100644
index 00000000000..21b57f53314
--- /dev/null
+++ b/rust/libs/observability/src/macros.rs
@@ -0,0 +1,150 @@
+//
+// Copyright (C) 2019-2024 vdaas.org vald team
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#[macro_export]
+macro_rules! tracer {
+ () => {{
+ tracer!("vald")
+ }};
+
+ ($name:expr) => {{
+ opentelemetry::global::tracer($name)
+ }};
+}
+
+#[macro_export]
+macro_rules! ctx_span {
+ ($ctx:expr, $name:expr) => {{
+ ctx_span!($ctx, $name, opentelemetry::trace::SpanKind::Internal)
+ }};
+
+ ($ctx:expr, $name:expr, $kind:expr) => {{
+ let tracer = tracer!();
+ let parent_ctx: &opentelemetry::Context = $ctx;
+ let span = tracer
+ .span_builder($name)
+ .with_kind($kind)
+ .start_with_context(&tracer, parent_ctx);
+ opentelemetry::Context::current_with_span(span)
+ }};
+}
+
+#[macro_export]
+macro_rules! meter {
+ () => {{
+ meter!("vald")
+ }};
+
+ ($name:expr) => {{
+ opentelemetry::global::meter($name)
+ }};
+}
+
+#[derive(Debug, PartialEq)]
+pub enum InstrumentKind {
+ UpdownCounter,
+ Counter,
+ Histogram,
+ Gauge,
+}
+
+#[macro_export]
+macro_rules! instrument {
+ (InstrumentKind::Counter, $typ:ty, $name:expr, $disc:expr, $unit:expr) => {{
+ let meter = meter!();
+ paste::paste! {
+ meter
+ .[<$typ _counter>]($name) // typ = f64 or u64
+ .with_description($disc)
+ .with_unit($unit)
+ .init()
+ }
+ }};
+
+ (InstrumentKind::Counter, $typ:ty, $name:expr, $disc:expr, $unit:expr, $measurement:expr, $key_value:expr) => {{
+ let meter = meter!();
+ paste::paste! {
+ meter
+ .[<$typ _observable_counter>]($name) // typ = f64 or u64
+ .with_description($disc)
+ .with_unit($unit)
+ .with_callback(|observe| {
+ observe.observe($measurement, $key_value);
+ })
+ .init()
+ }
+ }};
+
+ (InstrumentKind::UpdownCounter, $typ:ty, $name:expr, $disc:expr, $unit:expr) => {{
+ let meter = meter!();
+ paste::paste! {
+ meter
+ .[<$typ _up_down_counter>]($name) // typ = f64 or i64
+ .with_description($disc)
+ .with_unit($unit)
+ .init()
+ }
+ }};
+
+ (InstrumentKind::UpdownCounter, $typ:ty, $name:expr, $disc:expr, $unit:expr, $measurement:expr, $key_value:expr) => {{
+ let meter = meter!();
+ paste::paste! {
+ meter
+ .[<$typ _observable_up_down_counter>]($name) // typ = f64 or i64
+ .with_description($disc)
+ .with_unit($unit)
+ .with_callback(|observe| {
+ observe.observe($measurement, $key_value);
+ })
+ .init()
+ }
+ }};
+
+ (InstrumentKind::Histogram, $typ:ty, $name:expr, $disc:expr, $unit:expr) => {{
+ let meter = meter!();
+ paste::paste! {
+ meter
+ .[<$typ _histogram>]($name) // typ = f64 or i64
+ .with_description($disc)
+ .with_unit($unit)
+ .init()
+ }
+ }};
+
+ (InstrumentKind::Gauge, $typ:ty, $name:expr, $disc:expr, $unit:expr) => {{
+ let meter = meter!();
+ paste::paste! {
+ meter
+ .[<$typ _gauge>]($name) // typ = f64 or i64 or u64
+ .with_description($disc)
+ .with_unit($unit)
+ .init()
+ }
+ }};
+
+ (InstrumentKind::Gauge, $typ:ty, $name:expr, $disc:expr, $unit:expr, $measurement:expr, $key_value:expr) => {{
+ let meter = meter!();
+ paste::paste! {
+ meter
+ .[<$typ _observable_gauge>]($name) // typ = f64 or u64 or u64
+ .with_description($disc)
+ .with_unit($unit)
+ .with_callback(|observe| {
+ observe.observe($measurement, $key_value);
+ })
+ .init()
+ }
+ }};
+}
diff --git a/rust/libs/observability/src/observability.rs b/rust/libs/observability/src/observability.rs
new file mode 100644
index 00000000000..a80ab6d92a7
--- /dev/null
+++ b/rust/libs/observability/src/observability.rs
@@ -0,0 +1,124 @@
+//
+// Copyright (C) 2019-2024 vdaas.org vald team
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+use anyhow::{Context, Ok, Result};
+use opentelemetry::global::{self, shutdown_tracer_provider};
+use opentelemetry_otlp::WithExportConfig;
+use opentelemetry_sdk::metrics::SdkMeterProvider;
+use opentelemetry_sdk::propagation::TraceContextPropagator;
+use opentelemetry_sdk::trace::{self, TracerProvider};
+use opentelemetry_sdk::{runtime, Resource};
+use url::Url;
+
+use crate::config::Config;
+
+pub const SERVICE_NAME: &str = opentelemetry_semantic_conventions::resource::SERVICE_NAME;
+
+pub trait Observability {
+ fn shutdown(&mut self) -> Result<()>;
+}
+
+pub struct ObservabilityImpl {
+ config: Config,
+ meter_provider: Option,
+ tracer_provider: Option,
+}
+
+impl ObservabilityImpl {
+ pub fn new(cfg: Config) -> Result {
+ let mut obj = ObservabilityImpl {
+ config: cfg,
+ meter_provider: None,
+ tracer_provider: None,
+ };
+
+ if !obj.config.enabled {
+ return Ok(obj);
+ }
+
+ if obj.config.meter.enabled {
+ // NOTE: Since the agent implementation does not use views, we will use the simplest implementation for the current phase.
+ // If we want flexibility and customization, use SdkMeterProvider::builder.
+ let provider = opentelemetry_otlp::new_pipeline()
+ .metrics(runtime::Tokio)
+ .with_period(obj.config.meter.export_duration)
+ .with_resource(Resource::from(obj.config()))
+ .with_timeout(obj.config.meter.export_timeout_duration)
+ .with_exporter(
+ opentelemetry_otlp::new_exporter().tonic().with_endpoint(
+ Url::parse(obj.config.endpoint.as_str())?
+ .join("/v1/metrics")?
+ .as_str(),
+ ),
+ )
+ .build()?;
+ obj.meter_provider = Some(provider.clone());
+ global::set_meter_provider(provider.clone());
+ }
+
+ if obj.config.tracer.enabled {
+ let tracer = opentelemetry_otlp::new_pipeline()
+ .tracing()
+ .with_exporter(
+ opentelemetry_otlp::new_exporter().tonic().with_endpoint(
+ Url::parse(obj.config.endpoint.as_str())?
+ .join("/v1/traces")?
+ .as_str(),
+ ),
+ )
+ .with_trace_config(
+ trace::config()
+ .with_sampler(trace::Sampler::AlwaysOn)
+ .with_resource(Resource::from(obj.config()))
+ .with_id_generator(trace::RandomIdGenerator::default()),
+ )
+ .install_batch(runtime::Tokio)?;
+ let provider = tracer.provider().context("failed to get provider")?;
+ obj.tracer_provider = Some(provider.clone());
+ global::set_text_map_propagator(TraceContextPropagator::new());
+ global::set_tracer_provider(provider.clone());
+ }
+ Ok(obj)
+ }
+
+ fn config(&self) -> &Config {
+ &self.config
+ }
+}
+
+impl Observability for ObservabilityImpl {
+ fn shutdown(&mut self) -> Result<()> {
+ if !self.config.enabled {
+ return Ok(());
+ }
+
+ if self.config.meter.enabled {
+ if let Some(ref provider) = self.meter_provider {
+ provider.force_flush()?;
+ provider.shutdown()?;
+ }
+ }
+
+ if self.config.meter.enabled {
+ if let Some(ref provider) = self.tracer_provider {
+ for result in provider.force_flush() {
+ result?;
+ }
+ shutdown_tracer_provider();
+ }
+ }
+ Ok(())
+ }
+}