From 73d04ae0be4f01030d5e2d4ff8bc204206e2762e Mon Sep 17 00:00:00 2001 From: elramen <158566966+elramen@users.noreply.github.com> Date: Mon, 27 May 2024 17:10:02 +0200 Subject: [PATCH] ref(metrics): Add normalization and update set metrics hashing (#658) Add metrics normalization in accordance with the metrics developer docs. Add metric name, unit, and tag truncation to adhere to the metrics user docs. Add to_envelope for a single Metric instance to facilitate sending metrics from sentry-cli. Change hash function to crc32 as described here, this ensures compatibility between different SDKs using the same metric. Use clone_from instead of clone and clone_into instead of to_owned in a couple of places as suggested by clippy. --- Cargo.lock | 996 +++++++++++++++++- sentry-core/Cargo.toml | 3 + sentry-core/src/cadence.rs | 7 +- sentry-core/src/client.rs | 6 +- .../src/{metrics.rs => metrics/mod.rs} | 252 +++-- sentry-core/src/metrics/normalization/mod.rs | 28 + .../metrics/normalization/normalized_name.rs | 33 + .../metrics/normalization/normalized_tags.rs | 179 ++++ .../metrics/normalization/normalized_unit.rs | 57 + sentry-core/src/performance.rs | 4 +- sentry-core/src/units.rs | 6 + sentry-tracing/src/converters.rs | 10 +- sentry-types/src/protocol/envelope.rs | 25 +- 13 files changed, 1467 insertions(+), 139 deletions(-) rename sentry-core/src/{metrics.rs => metrics/mod.rs} (86%) create mode 100644 sentry-core/src/metrics/normalization/mod.rs create mode 100644 sentry-core/src/metrics/normalization/normalized_name.rs create mode 100644 sentry-core/src/metrics/normalization/normalized_tags.rs create mode 100644 sentry-core/src/metrics/normalization/normalized_unit.rs diff --git a/Cargo.lock b/Cargo.lock index 7c934098..03d64734 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -272,6 +272,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923" +dependencies = [ + "as-slice", +] + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -287,6 +296,21 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[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 = "anes" version = "0.1.6" @@ -314,6 +338,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "async-channel" version = "1.9.0" @@ -673,6 +706,28 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +[[package]] +name = "bindgen" +version = "0.63.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 1.0.109", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -740,12 +795,41 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "build-time" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1219c19fc29b7bfd74b7968b420aff5bc951cf517800176e795d6b2300dd382" +dependencies = [ + "chrono", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "bumpalo" version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "0.5.6" @@ -776,6 +860,38 @@ dependencies = [ "crossbeam-channel", ] +[[package]] +name = "camino" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.20", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "cast" version = "0.3.0" @@ -792,12 +908,33 @@ dependencies = [ "libc", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets 0.52.4", +] + [[package]] name = "ciborium" version = "0.2.1" @@ -834,6 +971,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.4.10" @@ -859,6 +1007,15 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + [[package]] name = "concurrent-queue" version = "2.3.0" @@ -875,7 +1032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" dependencies = [ "lazy_static", - "nom", + "nom 5.1.3", "serde", ] @@ -885,6 +1042,26 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -952,9 +1129,9 @@ checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -971,7 +1148,7 @@ dependencies = [ "clap", "criterion-plot", "is-terminal", - "itertools", + "itertools 0.10.5", "num-traits", "once_cell", "oorandom", @@ -992,9 +1169,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools", + "itertools 0.10.5", ] +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -1108,6 +1291,49 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "cvt" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ae9bf77fbf2d39ef573205d554d87e86c12f1994e9ea335b0651b9b278bcf1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "darling" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "darling_macro" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.58", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -1145,6 +1371,38 @@ dependencies = [ "uuid", ] +[[package]] +name = "defmt" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb" +dependencies = [ + "defmt-parser", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "defmt-parser" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f" +dependencies = [ + "thiserror", +] + [[package]] name = "deranged" version = "0.3.9" @@ -1198,6 +1456,127 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +[[package]] +name = "embassy-futures" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" + +[[package]] +name = "embassy-sync" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd938f25c0798db4280fcd8026bf4c2f48789aebf8f77b6e5cf8a7693ba114ec" +dependencies = [ + "cfg-if", + "critical-section", + "embedded-io-async", + "futures-util", + "heapless", +] + +[[package]] +name = "embedded-can" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "embedded-io-async" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" +dependencies = [ + "embedded-io", +] + +[[package]] +name = "embedded-svc" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6f87e7654f28018340aa55f933803017aefabaa5417820a3b2f808033c7bbc" +dependencies = [ + "defmt", + "embedded-io", + "embedded-io-async", + "enumset", + "heapless", + "log", + "no-std-net", + "num_enum", + "serde", + "strum 0.25.0", + "strum_macros 0.25.3", +] + +[[package]] +name = "embuild" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4caa4f198bb9152a55c0103efb83fa4edfcbb8625f4c9e94ae8ec8e23827c563" +dependencies = [ + "anyhow", + "bindgen", + "bitflags 1.3.2", + "cmake", + "filetime", + "globwalk", + "home", + "log", + "remove_dir_all", + "serde", + "serde_json", + "shlex", + "strum 0.24.1", + "tempfile", + "thiserror", + "which", +] + [[package]] name = "encoding_rs" version = "0.8.33" @@ -1207,6 +1586,28 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enumset" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +dependencies = [ + "enumset_derive", + "serde", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "env_logger" version = "0.10.1" @@ -1220,6 +1621,15 @@ dependencies = [ "termcolor", ] +[[package]] +name = "envy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" +dependencies = [ + "serde", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1245,6 +1655,68 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "esp-idf-hal" +version = "0.43.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7adf3fb19a9ca016cbea1ab8a7b852ac69df8fcde4923c23d3b155efbc42a74" +dependencies = [ + "atomic-waker", + "embassy-sync", + "embedded-can", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "embedded-io-async", + "embuild", + "enumset", + "esp-idf-sys", + "heapless", + "log", + "nb 1.1.0", + "num_enum", +] + +[[package]] +name = "esp-idf-svc" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2180642ca122a7fec1ec417a9b1a77aa66aaa067fdf1daae683dd8caba84f26b" +dependencies = [ + "embassy-futures", + "embedded-hal-async", + "embedded-svc", + "embuild", + "enumset", + "esp-idf-hal", + "heapless", + "log", + "num_enum", + "uncased", +] + +[[package]] +name = "esp-idf-sys" +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e148f97c04ed3e9181a08bcdc9560a515aad939b0ba7f50a0022e294665e0af" +dependencies = [ + "anyhow", + "bindgen", + "build-time", + "cargo_metadata", + "const_format", + "embuild", + "envy", + "libc", + "regex", + "serde", + "strum 0.24.1", + "which", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -1287,6 +1759,18 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.52.0", +] + [[package]] name = "findshlibs" version = "0.10.2" @@ -1350,6 +1834,20 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_at" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982f82cc75107eef84f417ad6c53ae89bf65b561937ca4a3b3b0fd04d0aa2425" +dependencies = [ + "aligned", + "cfg-if", + "cvt", + "libc", + "nix", + "windows-sys 0.48.0", +] + [[package]] name = "futures" version = "0.3.29" @@ -1528,6 +2026,30 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "gloo-timers" version = "0.2.6" @@ -1565,6 +2087,15 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1577,6 +2108,23 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "serde", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1609,6 +2157,15 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "hostname" version = "0.4.0" @@ -1843,6 +2400,35 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[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.5.0" @@ -1853,6 +2439,22 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1950,6 +2552,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1995,6 +2606,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "lexical-core" version = "0.7.6" @@ -2014,6 +2631,16 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.4", +] + [[package]] name = "libnghttp2-sys" version = "0.1.8+1.55.1" @@ -2121,6 +2748,12 @@ dependencies = [ "unicase", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -2160,6 +2793,41 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "no-std-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bcece43b12349917e096cddfa66107277f123e6c96a5aea78711dc601a47152" +dependencies = [ + "serde", +] + [[package]] name = "nom" version = "5.1.3" @@ -2171,6 +2839,25 @@ dependencies = [ "version_check", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "normpath" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5831952a9476f2fed74b77d74182fa5ddc4d21c72ec45a333b250e3ed0272804" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2200,6 +2887,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "object" version = "0.32.2" @@ -2323,6 +3031,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -2469,6 +3183,39 @@ dependencies = [ "log", ] +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" @@ -2501,7 +3248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.58", @@ -2651,6 +3398,22 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c707298afce11da2efef2f600116fa93ffa7a032b5d7b628aa17711ec81383ca" +[[package]] +name = "remove_dir_all" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23895cfadc1917fed9c6ed76a8c2903615fa3704f7493ff82b364c6540acc02b" +dependencies = [ + "aligned", + "cfg-if", + "cvt", + "fs_at", + "lazy_static", + "libc", + "normpath", + "windows-sys 0.45.0", +] + [[package]] name = "reqwest" version = "0.12.3" @@ -2746,6 +3509,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.2.3" @@ -2937,6 +3706,9 @@ name = "semver" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +dependencies = [ + "serde", +] [[package]] name = "semver-parser" @@ -2946,11 +3718,13 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "sentry" -version = "0.32.2" +version = "0.32.3" dependencies = [ "actix-web", "anyhow", "curl", + "embedded-svc", + "esp-idf-svc", "http-client", "httpdate", "isahc", @@ -2982,7 +3756,7 @@ dependencies = [ [[package]] name = "sentry-actix" -version = "0.32.2" +version = "0.32.3" dependencies = [ "actix-web", "futures", @@ -2994,7 +3768,7 @@ dependencies = [ [[package]] name = "sentry-anyhow" -version = "0.32.2" +version = "0.32.3" dependencies = [ "anyhow", "sentry", @@ -3004,7 +3778,7 @@ dependencies = [ [[package]] name = "sentry-backtrace" -version = "0.32.2" +version = "0.32.3" dependencies = [ "backtrace", "once_cell", @@ -3014,7 +3788,7 @@ dependencies = [ [[package]] name = "sentry-contexts" -version = "0.32.2" +version = "0.32.3" dependencies = [ "hostname", "libc", @@ -3027,16 +3801,19 @@ dependencies = [ [[package]] name = "sentry-core" -version = "0.32.2" +version = "0.32.3" dependencies = [ "anyhow", "cadence", + "crc32fast", "criterion", "futures", + "itertools 0.13.0", "log", "once_cell", "rand 0.8.5", "rayon", + "regex", "sentry", "sentry-types", "serde", @@ -3048,7 +3825,7 @@ dependencies = [ [[package]] name = "sentry-debug-images" -version = "0.32.2" +version = "0.32.3" dependencies = [ "findshlibs", "once_cell", @@ -3057,7 +3834,7 @@ dependencies = [ [[package]] name = "sentry-log" -version = "0.32.2" +version = "0.32.3" dependencies = [ "log", "pretty_env_logger", @@ -3067,7 +3844,7 @@ dependencies = [ [[package]] name = "sentry-panic" -version = "0.32.2" +version = "0.32.3" dependencies = [ "sentry", "sentry-backtrace", @@ -3076,7 +3853,7 @@ dependencies = [ [[package]] name = "sentry-slog" -version = "0.32.2" +version = "0.32.3" dependencies = [ "erased-serde", "sentry", @@ -3088,7 +3865,7 @@ dependencies = [ [[package]] name = "sentry-tower" -version = "0.32.2" +version = "0.32.3" dependencies = [ "anyhow", "axum 0.7.1", @@ -3108,7 +3885,7 @@ dependencies = [ [[package]] name = "sentry-tracing" -version = "0.32.2" +version = "0.32.3" dependencies = [ "log", "sentry", @@ -3122,7 +3899,7 @@ dependencies = [ [[package]] name = "sentry-types" -version = "0.32.2" +version = "0.32.3" dependencies = [ "debugid", "hex", @@ -3238,6 +4015,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -3326,6 +4109,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "standback" version = "0.2.17" @@ -3390,6 +4179,50 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros 0.24.3", +] + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros 0.25.3", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.58", +] + [[package]] name = "subtle" version = "2.5.0" @@ -3675,6 +4508,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow", +] + [[package]] name = "tonic" version = "0.11.0" @@ -3823,6 +4673,15 @@ dependencies = [ "libc", ] +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unicase" version = "2.7.0" @@ -3853,6 +4712,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "universal-hash" version = "0.4.0" @@ -3931,6 +4796,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "waker-fn" version = "1.1.1" @@ -4059,6 +4930,18 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.32", +] + [[package]] name = "winapi" version = "0.3.9" @@ -4109,6 +4992,15 @@ dependencies = [ "windows-targets 0.52.4", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -4127,6 +5019,21 @@ dependencies = [ "windows-targets 0.52.4", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -4157,6 +5064,12 @@ dependencies = [ "windows_x86_64_msvc 0.52.4", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -4169,6 +5082,12 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -4181,6 +5100,12 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -4193,6 +5118,12 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -4205,6 +5136,12 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -4217,6 +5154,12 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -4229,6 +5172,12 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -4241,6 +5190,15 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.52.0" diff --git a/sentry-core/Cargo.toml b/sentry-core/Cargo.toml index 3c2bfd0e..619f2d31 100644 --- a/sentry-core/Cargo.toml +++ b/sentry-core/Cargo.toml @@ -31,9 +31,12 @@ UNSTABLE_cadence = ["dep:cadence", "UNSTABLE_metrics"] [dependencies] cadence = { version = "0.29.0", optional = true } +crc32fast = "1.4.0" +itertools = "0.13.0" log = { version = "0.4.8", optional = true, features = ["std"] } once_cell = "1" rand = { version = "0.8.1", optional = true } +regex = "1.7.3" sentry-types = { version = "0.32.3", path = "../sentry-types" } serde = { version = "1.0.104", features = ["derive"] } serde_json = { version = "1.0.46" } diff --git a/sentry-core/src/cadence.rs b/sentry-core/src/cadence.rs index 8cd7c3d0..401f53d9 100644 --- a/sentry-core/src/cadence.rs +++ b/sentry-core/src/cadence.rs @@ -154,9 +154,10 @@ mod tests { println!("{metrics}"); - assert!(metrics.contains("sentry.test.count.with.tags:1|c|#foo:bar|T")); - assert!(metrics.contains("sentry.test.some.count:11|c|T")); - assert!(metrics.contains("sentry.test.some.distr:1:2:3|d|T")); + assert!(metrics + .contains("sentry.test.count.with.tags@none:1|c|#environment:production,foo:bar|T")); + assert!(metrics.contains("sentry.test.some.count@none:11|c|#environment:production|T")); + assert!(metrics.contains("sentry.test.some.distr@none:1:2:3|d|#environment:production|T")); assert_eq!(items.next(), None); } } diff --git a/sentry-core/src/client.rs b/sentry-core/src/client.rs index a485d99c..ed52f05b 100644 --- a/sentry-core/src/client.rs +++ b/sentry-core/src/client.rs @@ -207,13 +207,13 @@ impl Client { } if event.release.is_none() { - event.release = self.options.release.clone(); + event.release.clone_from(&self.options.release); } if event.environment.is_none() { - event.environment = self.options.environment.clone(); + event.environment.clone_from(&self.options.environment); } if event.server_name.is_none() { - event.server_name = self.options.server_name.clone(); + event.server_name.clone_from(&self.options.server_name); } if &event.platform == "other" { event.platform = "native".into(); diff --git a/sentry-core/src/metrics.rs b/sentry-core/src/metrics/mod.rs similarity index 86% rename from sentry-core/src/metrics.rs rename to sentry-core/src/metrics/mod.rs index 0e8b6f3b..65cd7478 100644 --- a/sentry-core/src/metrics.rs +++ b/sentry-core/src/metrics/mod.rs @@ -44,10 +44,12 @@ //! //! [our docs]: https://develop.sentry.dev/delightful-developer-metrics/ +mod normalization; + use std::borrow::Cow; -use std::collections::hash_map::{DefaultHasher, Entry}; +use std::collections::hash_map::Entry; use std::collections::{BTreeMap, BTreeSet, HashMap}; -use std::fmt::{self, Write}; +use std::fmt::{self, Display}; use std::sync::{Arc, Mutex}; use std::thread::{self, JoinHandle}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -168,15 +170,23 @@ impl MetricValue { } } +impl Display for MetricValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Counter(v) => write!(f, "{}", v), + Self::Distribution(v) => write!(f, "{}", v), + Self::Gauge(v) => write!(f, "{}", v), + Self::Set(v) => write!(f, "{}", v), + } + } +} + /// Hashes the given set value. /// /// Sets only guarantee 32-bit accuracy, but arbitrary strings are allowed on the protocol. Upon /// parsing, they are hashed and only used as hashes subsequently. fn hash_set_value(string: &str) -> u32 { - use std::hash::Hasher; - let mut hasher = DefaultHasher::default(); - hasher.write(string.as_bytes()); - hasher.finish() as u32 + crc32fast::hash(string.as_bytes()) } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -510,6 +520,26 @@ impl Metric { client.add_metric(self); } } + + /// Convert the metric into an [`Envelope`] containing a single [`EnvelopeItem::Statsd`]. + pub fn to_envelope(self) -> Envelope { + let timestamp = self + .time + .unwrap_or(SystemTime::now()) + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + let data = format!( + "{}@{}:{}|{}|#{}|T{}", + normalization::normalize_name(self.name.as_ref()), + normalization::normalize_unit(self.unit.to_string().as_ref()), + self.value, + self.value.ty(), + normalization::normalize_tags(&self.tags), + timestamp + ); + EnvelopeItem::Statsd(data.into_bytes()).into() + } } /// A builder for metrics. @@ -550,6 +580,26 @@ impl MetricBuilder { self } + /// Adds multiple tags to the metric. + /// + /// Tags allow you to add dimensions to metrics. They are key-value pairs that can be filtered + /// or grouped by in Sentry. + /// + /// When sent to Sentry via [`MetricBuilder::send`] or when added to a + /// [`Client`](crate::Client), the client may add default tags to the metrics, such as the + /// `release` or the `environment` from the Scope. + pub fn with_tags(mut self, tags: T) -> Self + where + T: IntoIterator, + K: Into, + V: Into, + { + for (k, v) in tags { + self.metric.tags.insert(k.into(), v.into()); + } + self + } + /// Sets the timestamp for the metric. /// /// By default, the timestamp is set to the current time when the metric is built or sent. @@ -723,9 +773,14 @@ fn get_default_tags(options: &ClientOptions) -> TagMap { if let Some(ref release) = options.release { tags.insert("release".into(), release.clone()); } - if let Some(ref environment) = options.environment { - tags.insert("environment".into(), environment.clone()); - } + tags.insert( + "environment".into(), + options + .environment + .clone() + .filter(|e| !e.is_empty()) + .unwrap_or(Cow::Borrowed("production")), + ); tags } @@ -778,11 +833,17 @@ impl Worker { for (timestamp, buckets) in buckets { for (key, value) in buckets { - write!(&mut out, "{}", SafeKey(key.name.as_ref()))?; - if key.unit != MetricUnit::None { - write!(&mut out, "@{}", key.unit)?; + write!( + &mut out, + "{}", + normalization::normalize_name(key.name.as_ref()) + )?; + match key.unit { + MetricUnit::Custom(u) => { + write!(&mut out, "@{}", normalization::normalize_unit(u.as_ref()))? + } + _ => write!(&mut out, "@{}", key.unit)?, } - match value { BucketValue::Counter(c) => { write!(&mut out, ":{}", c)?; @@ -807,16 +868,9 @@ impl Worker { } write!(&mut out, "|{}", key.ty.as_str())?; - - for (i, (k, v)) in key.tags.iter().chain(&self.default_tags).enumerate() { - match i { - 0 => write!(&mut out, "|#")?, - _ => write!(&mut out, ",")?, - } - - write!(&mut out, "{}:{}", SafeKey(k.as_ref()), SafeVal(v.as_ref()))?; - } - + let normalized_tags = + normalization::normalize_tags(&key.tags).with_default_tags(&self.default_tags); + write!(&mut out, "|#{}", normalized_tags)?; writeln!(&mut out, "|T{}", timestamp)?; } } @@ -922,51 +976,6 @@ impl Drop for MetricAggregator { } } -fn safe_fmt(f: &mut fmt::Formatter<'_>, string: &str, mut check: F) -> fmt::Result -where - F: FnMut(char) -> bool, -{ - let mut valid = true; - - for c in string.chars() { - if check(c) { - valid = true; - f.write_char(c)?; - } else if valid { - valid = false; - f.write_char('_')?; - } - } - - Ok(()) -} - -// Helper that serializes a string into a safe format for metric names or tag keys. -struct SafeKey<'s>(&'s str); - -impl<'s> fmt::Display for SafeKey<'s> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - safe_fmt(f, self.0, |c| { - c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | '.' | '/') - }) - } -} - -// Helper that serializes a string into a safe format for tag values. -struct SafeVal<'s>(&'s str); - -impl<'s> fmt::Display for SafeVal<'s> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - safe_fmt(f, self.0, |c| { - c.is_alphanumeric() - || matches!( - c, - '_' | ':' | '/' | '@' | '.' | '{' | '}' | '[' | ']' | '$' | '-' - ) - }) - } -} - #[cfg(test)] mod tests { use crate::test::{with_captured_envelopes, with_captured_envelopes_options}; @@ -1007,7 +1016,10 @@ mod tests { }); let metrics = get_single_metrics(&envelopes); - assert_eq!(metrics, format!("my.metric:1|c|#and:more,foo:bar|T{ts}")); + assert_eq!( + metrics, + format!("my.metric@none:1|c|#and:more,environment:production,foo:bar|T{ts}") + ); } #[test] @@ -1022,7 +1034,10 @@ mod tests { }); let metrics = get_single_metrics(&envelopes); - assert_eq!(metrics, format!("my.metric@custom:1|c|T{ts}")); + assert_eq!( + metrics, + format!("my.metric@custom:1|c|#environment:production|T{ts}") + ); } #[test] @@ -1034,7 +1049,10 @@ mod tests { }); let metrics = get_single_metrics(&envelopes); - assert_eq!(metrics, format!("my_metric:1|c|T{ts}")); + assert_eq!( + metrics, + format!("my___metric@none:1|c|#environment:production|T{ts}") + ); } #[test] @@ -1051,29 +1069,43 @@ mod tests { let metrics = get_single_metrics(&envelopes); assert_eq!( metrics, - format!("my.metric:1|c|#foo-bar_blub:_$fΓΆΓΆ{{}}|T{ts}") + format!("my.metric@none:1|c|#environment:production,foo-barblub:%$fΓΆΓΆ{{}}|T{ts}") ); } #[test] - fn test_own_namespace() { + fn test_default_tags() { let (time, ts) = current_time(); - let envelopes = with_captured_envelopes(|| { - Metric::count("ns/my.metric").with_time(time).send(); - }); + let options = ClientOptions { + release: Some("myapp@1.0.0".into()), + environment: Some("development".into()), + ..Default::default() + }; + + let envelopes = with_captured_envelopes_options( + || { + Metric::count("requests") + .with_tag("foo", "bar") + .with_time(time) + .send(); + }, + options, + ); let metrics = get_single_metrics(&envelopes); - assert_eq!(metrics, format!("ns/my.metric:1|c|T{ts}")); + assert_eq!( + metrics, + format!("requests@none:1|c|#environment:development,foo:bar,release:myapp@1.0.0|T{ts}") + ); } #[test] - fn test_default_tags() { + fn test_empty_default_tags() { let (time, ts) = current_time(); - let options = ClientOptions { - release: Some("myapp@1.0.0".into()), - environment: Some("production".into()), + release: Some("".into()), + environment: Some("".into()), ..Default::default() }; @@ -1090,7 +1122,34 @@ mod tests { let metrics = get_single_metrics(&envelopes); assert_eq!( metrics, - format!("requests:1|c|#foo:bar,environment:production,release:myapp@1.0.0|T{ts}") + format!("requests@none:1|c|#environment:production,foo:bar|T{ts}") + ); + } + + #[test] + fn test_override_default_tags() { + let (time, ts) = current_time(); + let options = ClientOptions { + release: Some("default_release".into()), + environment: Some("default_env".into()), + ..Default::default() + }; + + let envelopes = with_captured_envelopes_options( + || { + Metric::count("requests") + .with_tag("environment", "custom_env") + .with_tag("release", "custom_release") + .with_time(time) + .send(); + }, + options, + ); + + let metrics = get_single_metrics(&envelopes); + assert_eq!( + metrics, + format!("requests@none:1|c|#environment:custom_env,release:custom_release|T{ts}") ); } @@ -1104,7 +1163,10 @@ mod tests { }); let metrics = get_single_metrics(&envelopes); - assert_eq!(metrics, format!("my.metric:3|c|T{ts}")); + assert_eq!( + metrics, + format!("my.metric@none:3|c|#environment:production|T{ts}") + ); } #[test] @@ -1121,7 +1183,10 @@ mod tests { }); let metrics = get_single_metrics(&envelopes); - assert_eq!(metrics, format!("my.metric@second:0.2:0.1|d|T{ts}")); + assert_eq!( + metrics, + format!("my.metric@second:0.2:0.1|d|#environment:production|T{ts}") + ); } #[test] @@ -1138,7 +1203,10 @@ mod tests { }); let metrics = get_single_metrics(&envelopes); - assert_eq!(metrics, format!("my.metric:2:1|d|T{ts}")); + assert_eq!( + metrics, + format!("my.metric@none:2:1|d|#environment:production|T{ts}") + ); } #[test] @@ -1153,7 +1221,10 @@ mod tests { }); let metrics = get_single_metrics(&envelopes); - assert_eq!(metrics, format!("my.metric:3410894750:3817476724|s|T{ts}")); + assert_eq!( + metrics, + format!("my.metric@none:907060870:980881731|s|#environment:production|T{ts}") + ); } #[test] @@ -1167,7 +1238,10 @@ mod tests { }); let metrics = get_single_metrics(&envelopes); - assert_eq!(metrics, format!("my.metric:1.5:1:2:4.5:3|g|T{ts}")); + assert_eq!( + metrics, + format!("my.metric@none:1.5:1:2:4.5:3|g|#environment:production|T{ts}") + ); } #[test] @@ -1182,8 +1256,8 @@ mod tests { let metrics = get_single_metrics(&envelopes); println!("{metrics}"); - assert!(metrics.contains(&format!("my.metric:1|c|T{ts}"))); - assert!(metrics.contains(&format!("my.dist:2|d|T{ts}"))); + assert!(metrics.contains(&format!("my.metric@none:1|c|#environment:production|T{ts}"))); + assert!(metrics.contains(&format!("my.dist@none:2|d|#environment:production|T{ts}"))); } #[test] diff --git a/sentry-core/src/metrics/normalization/mod.rs b/sentry-core/src/metrics/normalization/mod.rs new file mode 100644 index 00000000..2b228b90 --- /dev/null +++ b/sentry-core/src/metrics/normalization/mod.rs @@ -0,0 +1,28 @@ +pub mod normalized_name; +pub mod normalized_tags; +pub mod normalized_unit; + +pub use normalized_name::normalize_name; +pub use normalized_tags::normalize_tags; +pub use normalized_unit::normalize_unit; + +pub fn truncate(s: &str, max_chars: usize) -> &str { + match s.char_indices().nth(max_chars) { + None => s, + Some((i, _)) => &s[..i], + } +} + +#[cfg(test)] +mod test { + + #[test] + fn test_truncate_ascii_chars() { + assert_eq!("abc", super::truncate("abcde", 3)); + } + + #[test] + fn test_truncate_unicode_chars() { + assert_eq!("πŸ˜€πŸ˜€πŸ˜€", super::truncate("πŸ˜€πŸ˜€πŸ˜€πŸ˜€πŸ˜€", 3)); + } +} diff --git a/sentry-core/src/metrics/normalization/normalized_name.rs b/sentry-core/src/metrics/normalization/normalized_name.rs new file mode 100644 index 00000000..4abc2fe1 --- /dev/null +++ b/sentry-core/src/metrics/normalization/normalized_name.rs @@ -0,0 +1,33 @@ +use std::{borrow::Cow, sync::OnceLock}; + +use regex::Regex; + +pub fn normalize_name(name: &str) -> Cow { + static METRIC_NAME_RE: OnceLock = OnceLock::new(); + METRIC_NAME_RE + .get_or_init(|| Regex::new(r"[^a-zA-Z0-9_\-.]").expect("Regex should compile")) + .replace_all(super::truncate(name, 150), "_") +} + +#[cfg(test)] +mod test { + + #[test] + fn test_from() { + let expected = "aA1_-.____________"; + + let actual = super::normalize_name("aA1_-./+ΓΆ{πŸ˜€\n\t\r\\| ,"); + + assert_eq!(expected, actual); + } + + #[test] + fn test_length_restriction() { + let expected = "a".repeat(150); + + let too_long_name = "a".repeat(155); + let actual = super::normalize_name(&too_long_name); + + assert_eq!(expected, actual); + } +} diff --git a/sentry-core/src/metrics/normalization/normalized_tags.rs b/sentry-core/src/metrics/normalization/normalized_tags.rs new file mode 100644 index 00000000..f98935fb --- /dev/null +++ b/sentry-core/src/metrics/normalization/normalized_tags.rs @@ -0,0 +1,179 @@ +use itertools::Itertools; +use regex::Regex; +use std::{borrow::Cow, collections::HashMap, sync::OnceLock}; + +use crate::metrics::TagMap; + +pub fn normalize_tags(tags: &TagMap) -> NormalizedTags { + NormalizedTags { + tags: tags + .iter() + .map(|(k, v)| { + ( + NormalizedTags::normalize_key(super::truncate(k, 32)), + NormalizedTags::normalize_value(super::truncate(v, 200)), + ) + }) + .filter(|(k, v)| !k.is_empty() && !v.is_empty()) + .collect(), + } +} + +pub struct NormalizedTags<'a> { + tags: HashMap, String>, +} + +impl<'a> NormalizedTags<'a> { + pub fn with_default_tags(mut self, tags: &'a TagMap) -> Self { + for (k, v) in tags { + let k = Self::normalize_key(super::truncate(k, 32)); + let v = Self::normalize_value(super::truncate(v, 200)); + if !k.is_empty() && !v.is_empty() { + self.tags.entry(k).or_insert(v); + } + } + self + } + + fn normalize_key(key: &str) -> Cow { + static METRIC_TAG_KEY_RE: OnceLock = OnceLock::new(); + METRIC_TAG_KEY_RE + .get_or_init(|| Regex::new(r"[^a-zA-Z0-9_\-./]").expect("Regex should compile")) + .replace_all(key, "") + } + + fn normalize_value(value: &str) -> String { + let mut escaped = String::with_capacity(value.len()); + for c in value.chars() { + match c { + '\t' => escaped.push_str("\\t"), + '\n' => escaped.push_str("\\n"), + '\r' => escaped.push_str("\\r"), + '\\' => escaped.push_str("\\\\"), + '|' => escaped.push_str("\\u{7c}"), + ',' => escaped.push_str("\\u{2c}"), + _ if c.is_control() => (), + _ => escaped.push(c), + } + } + escaped + } +} + +impl std::fmt::Display for NormalizedTags<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let res = self + .tags + .iter() + .map(|(k, v)| format!("{}:{}", k, v)) + .sorted() + .join(","); + write!(f, "{res}") + } +} + +#[cfg(test)] +mod test { + use super::TagMap; + + #[test] + fn test_replacement_characters() { + let tags = TagMap::from_iter( + [ + ("a\na", "a\na"), + ("b\rb", "b\rb"), + ("c\tc", "c\tc"), + ("d\\d", "d\\d"), + ("e|e", "e|e"), + ("f,f", "f,f"), + ] + .into_iter() + .map(|(k, v)| (k.into(), v.into())), + ); + let expected = "aa:a\\na,bb:b\\rb,cc:c\\tc,dd:d\\\\d,ee:e\\u{7c}e,ff:f\\u{2c}f"; + + let actual = super::normalize_tags(&tags).to_string(); + + assert_eq!(expected, actual); + } + + #[test] + fn test_empty_tags() { + let tags = TagMap::from_iter( + [("+", "a"), ("a", ""), ("", "a"), ("", "")] + .into_iter() + .map(|(k, v)| (k.into(), v.into())), + ); + let expected = ""; + + let actual = super::normalize_tags(&tags).to_string(); + + assert_eq!(expected, actual); + } + + #[test] + fn test_special_characters() { + let tags = TagMap::from([("aA1_-./+ΓΆ{ πŸ˜€".into(), "aA1_-./+ΓΆ{ πŸ˜€".into())]); + let expected = "aA1_-./:aA1_-./+ΓΆ{ πŸ˜€"; + + let actual = super::normalize_tags(&tags).to_string(); + + assert_eq!(expected, actual); + } + + #[test] + fn test_add_default_tags() { + let default_tags = TagMap::from([ + ("release".into(), "default_release".into()), + ("environment".into(), "production".into()), + ]); + let expected = "environment:production,release:default_release"; + + let actual = super::normalize_tags(&TagMap::new()) + .with_default_tags(&default_tags) + .to_string(); + + assert_eq!(expected, actual); + } + + #[test] + fn test_override_default_tags() { + let default_tags = TagMap::from([ + ("release".into(), "default_release".into()), + ("environment".into(), "production".into()), + ]); + let expected = "environment:custom_env,release:custom_release"; + + let actual = super::normalize_tags(&TagMap::from([ + ("release".into(), "custom_release".into()), + ("environment".into(), "custom_env".into()), + ])) + .with_default_tags(&default_tags) + .to_string(); + + assert_eq!(expected, actual); + } + + #[test] + fn test_length_restriction() { + let expected = "dk".repeat(16) + + ":" + + "dv".repeat(100).as_str() + + "," + + "k".repeat(32).as_str() + + ":" + + "v".repeat(200).as_str(); + + let actual = super::normalize_tags(&TagMap::from([( + "k".repeat(35).into(), + "v".repeat(210).into(), + )])) + .with_default_tags(&TagMap::from([( + "dk".repeat(35).into(), + "dv".repeat(210).into(), + )])) + .to_string(); + + assert_eq!(expected, actual); + } +} diff --git a/sentry-core/src/metrics/normalization/normalized_unit.rs b/sentry-core/src/metrics/normalization/normalized_unit.rs new file mode 100644 index 00000000..e6b488b1 --- /dev/null +++ b/sentry-core/src/metrics/normalization/normalized_unit.rs @@ -0,0 +1,57 @@ +use std::{borrow::Cow, sync::OnceLock}; + +use regex::Regex; + +use crate::units::MetricUnit; + +pub fn normalize_unit(unit: &str) -> Cow { + static METRIC_UNIT_RE: OnceLock = OnceLock::new(); + let normalized_unit = METRIC_UNIT_RE + .get_or_init(|| Regex::new(r"[^a-zA-Z0-9_]").expect("Regex should compile")) + .replace_all(super::truncate(unit, 15), ""); + if normalized_unit.is_empty() { + MetricUnit::None.to_string().into() + } else { + normalized_unit + } +} + +#[cfg(test)] +mod test { + + #[test] + fn test_from() { + let expected = "aA1_"; + + let actual = super::normalize_unit("aA1_-./+ΓΆ{πŸ˜€\n\t\r\\| ,").to_string(); + + assert_eq!(expected, actual); + } + + #[test] + fn test_from_empty() { + let expected = "none"; + + let actual = super::normalize_unit("").to_string(); + + assert_eq!(expected, actual); + } + + #[test] + fn test_from_empty_after_normalization() { + let expected = "none"; + + let actual = super::normalize_unit("+").to_string(); + + assert_eq!(expected, actual); + } + + #[test] + fn test_length_restriction() { + let expected = "a".repeat(15); + + let actual = super::normalize_unit("a".repeat(20).as_ref()).to_string(); + + assert_eq!(expected, actual); + } +} diff --git a/sentry-core/src/performance.rs b/sentry-core/src/performance.rs index d707c370..552cc563 100644 --- a/sentry-core/src/performance.rs +++ b/sentry-core/src/performance.rs @@ -552,8 +552,8 @@ impl Transaction { Hub::current().with_current_scope(|scope| scope.apply_to_transaction(&mut transaction)); let opts = client.options(); - transaction.release = opts.release.clone(); - transaction.environment = opts.environment.clone(); + transaction.release.clone_from(&opts.release); + transaction.environment.clone_from(&opts.environment); transaction.sdk = Some(std::borrow::Cow::Owned(client.sdk_info.clone())); drop(inner); diff --git a/sentry-core/src/units.rs b/sentry-core/src/units.rs index bc47af65..a0f5026c 100644 --- a/sentry-core/src/units.rs +++ b/sentry-core/src/units.rs @@ -120,6 +120,12 @@ impl From> for MetricUnit { } } +impl From> for MetricUnit { + fn from(unit: Option) -> Self { + unit.map_or_else(|| Self::None, |u| Self::Custom(u.into())) + } +} + /// Time duration units used in [`MetricUnit::Duration`]. /// /// Defaults to `millisecond`. diff --git a/sentry-tracing/src/converters.rs b/sentry-tracing/src/converters.rs index 9f290607..08f32909 100644 --- a/sentry-tracing/src/converters.rs +++ b/sentry-tracing/src/converters.rs @@ -279,10 +279,12 @@ where } if let Some(exception) = exceptions.last_mut() { - exception - .mechanism - .get_or_insert_with(Mechanism::default) - .ty = "tracing".to_owned(); + "tracing".clone_into( + &mut exception + .mechanism + .get_or_insert_with(Mechanism::default) + .ty, + ); } Event { diff --git a/sentry-types/src/protocol/envelope.rs b/sentry-types/src/protocol/envelope.rs index 56809a7f..c978b74f 100644 --- a/sentry-types/src/protocol/envelope.rs +++ b/sentry-types/src/protocol/envelope.rs @@ -533,26 +533,13 @@ impl Envelope { } } -impl From> for Envelope { - fn from(event: Event<'static>) -> Self { - let mut envelope = Self::default(); - envelope.add_item(event); - envelope - } -} - -impl From> for Envelope { - fn from(transaction: Transaction<'static>) -> Self { - let mut envelope = Self::default(); - envelope.add_item(transaction); - envelope - } -} - -impl From for Envelope { - fn from(check_in: MonitorCheckIn) -> Self { +impl From for Envelope +where + T: Into, +{ + fn from(item: T) -> Self { let mut envelope = Self::default(); - envelope.add_item(check_in); + envelope.add_item(item.into()); envelope } }