Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

migrate from procedural to declarative macros #386

Merged
merged 24 commits into from
Sep 21, 2023

Conversation

hlbarber
Copy link
Contributor

@hlbarber hlbarber commented Aug 25, 2023

Changes

  • Remove metrics-macros crate
  • Rewrite all existing macros using macro_rules within the metrics crate

The goal here is to not break users and to also maintain the "fast paths" taken by the procedural macro where literals cause static to be used.

This PR is intended as a place of discussion, once it is sufficiently mature I'll close it and reopen a polished PR.

@hlbarber
Copy link
Contributor Author

hlbarber commented Aug 25, 2023

I've picked register_counter to migrate first - from the initial experimentation it feels possible. We're passing cargo t --no-fail-fast with a 2 exceptions.

Copy link
Member

@tobz tobz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far, so good... and in retrospect, much simpler than procedural macros. 😂

Left some feedback.

metrics/src/macros.rs Outdated Show resolved Hide resolved
::metrics::Key::from_static_name(&$name)
};
($name:literal, $($label_key:literal => $label_value:literal),*) => {{
static LABELS: [::metrics::Label; $crate::count! { $($label_key)* }] = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't even realize the curly braces form for calling macros is a thing (TIL!) but for the sake of consistency with most code in the wild, I'd prefer us to do the normal macro_name!(...) form across the board.

metrics/src/macros.rs Outdated Show resolved Hide resolved
metrics/src/macros.rs Outdated Show resolved Hide resolved
Copy link
Member

@tobz tobz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this is seeming pretty reasonable and straightforward.

It does look like some tests are broken, though, specifically around how we're handling building the key, and the expectation that we can always borrow it at the callsite, even when it may already be borrowed.

@hlbarber
Copy link
Contributor Author

The following seem to fix the failing test cases while maintaining the fast paths where possible. Please tell me if this looks off in any way.

https://github.com/metrics-rs/metrics/pull/386/files/c356678f3bd0bab76ad6932a951374ca5a1e484d..5e82f3e41e5ad2034f03359286d2e1a01eb775cf

@hlbarber
Copy link
Contributor Author

hlbarber commented Sep 2, 2023

That's the "counter" cross section of macros replaced. After register_counter it isn't really involved.

Given near perfect symmetry between the metric types I don't foresee any nasty obstructions when transitioning the other macros.

I'll run benchmarks shortly to check for any regressions.

@hlbarber
Copy link
Contributor Author

hlbarber commented Sep 2, 2023

After restoring doc tests there's a bit more to do, will look into it tomorrow.

@hlbarber
Copy link
Contributor Author

hlbarber commented Sep 2, 2023

0a32d83 has made is considerably more branch-y, but still tractable imo. Rather than making use of ? we must enumerate the combinations.

I think with some tinkering this can be compacted. I think perhaps we can parse the arguments as one (or two?) tt and then pass them onto other branches - some prior art can be found in the tracing macros (info etc).

I can push for compactness now, but given #369 is around the corner I can see an argument for postponing.

@tobz
Copy link
Member

tobz commented Sep 3, 2023

I can push for compactness now, but given #369 is around the corner I can see an argument for postponing.

I'd say let's just get the logic correct now and deal with condensing later.

I think with some tinkering this can be compacted. I think perhaps we can parse the arguments as one (or two?) tt and then pass them onto other branches - some prior art can be found in the tracing macros (info etc).

I'm not against the verbose-ness of having a branch for each possible permutation... so long as there's a viable path to, in the future, generalizing that macro logic across the different metric types. Basically, it'd be a small tragedy to just have to duplicate all of it over and over again when we do gauges and histograms... just ripe for introducing a subtle difference.

Having one register_metric macro or something, for example, that we just call into and pass the metric type-appropriate idents, and so on... and it has all of the various branches? That'd be fine.

@hlbarber
Copy link
Contributor Author

hlbarber commented Sep 7, 2023

Having one register_metric macro or something, for example, that we just call into and pass the metric type-appropriate idents, and so on... and it has all of the various branches? That'd be fine.

A reason I haven't re-used register_counter in the current impl is that the branch, in metric_macros::get_register_and_op_code for register_counter (found here) uses metrics::recorder and the counter/absolute_counter/etc branch uses metrics::try_recorder (found here).

If I use metrics::recorder for the counter branch I get a regression on the "uninitialized" benchmarks - this is a little suprising to me, I'd expect the NoopRecorder, after optimizations, to be equal in performance to checking Some(_) = try_recorder. I wonder if this is still the case if we implemented Recorder for Option<T> where T: Recorder and made recorder return it, rather than dyn Recorder.

@tobz
Copy link
Member

tobz commented Sep 7, 2023

I'd say switch to try_recorder across the board.

The regression is surprising to me too, but also, thinking about it... I'm not sure I actually give a crap if it's like 5ns vs 20ns or whatever when a recorder isn't installed. If it ever matters enough to me, or someone else, well... we can figure something out then.

@hlbarber
Copy link
Contributor Author

Rest of the macros are migrated. I've DRY'd it up a little bit with the describe and method macros. I haven't taken the try_recorder change yet because it deduplicated less with method macro in place.

I played around a little bit with $(args: tt),*, trying something like

macro_rules counter {
    ($($args:tt),) => $crate::method(register_counter, increment, $($args),*)
}

similar with what we see in the tracing macros. This doesn't work because of the mix of delimiters we see in the macros (, and =>). You can see the exact problem here https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=def715a3d053a4b933ccb4a821260729. Perhaps this is something the next API will side step. I think there's also an argument to keeping the explicit branches in the "macro signatures" to serve as documentation.

metrics-macro is now empty. I'm guessing I should delete the crate from the workspace entirely so that we don't release an empty crate?

@tobz
Copy link
Member

tobz commented Sep 11, 2023

Perhaps this is something the next API will side step. I think there's also an argument to keeping the explicit branches in the "macro signatures" to serve as documentation.

I think things are fine as is. Good to see your example as it makes it clear, but in seeing it and seeing what you chose to do instead, it makes me think that making it perfectly DRY isn't as big a readability win as I thought it might be anyways.

metrics-macros is now empty. I'm guessing I should delete the crate from the workspace entirely so that we don't release an empty crate?

Yessss, nuke it from orbit. 👨🏻‍🚀

}
}};
($method:ident, $name:expr, $description:expr) => {{
if let Some(recorder) = $crate::try_recorder() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, why doesn't this one need to be fully qualified?

Copy link
Contributor Author

@hlbarber hlbarber Sep 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, whoops. I'll try write a test involving #![no_implicit_prelude] so that me and others wont make this mistake again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a901339

I've slipped #![no_implicit_prelude] into the doc tests.

I think it might be better to write a dedicate test (or even just an example) where we invoke all combinations of all macros with #![no_implicit_prelude] and we don't import things like use std::convert::From which weakens the test. I can do this in a follow up PR unless you think its important now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like it can be a follow-up, to be honest.

@tobz
Copy link
Member

tobz commented Sep 14, 2023

I plan on taking a final look at this over the next few days.

@hlbarber
Copy link
Contributor Author

I can transplant this into a formal PR with a proper description now if you'd like?

@tobz
Copy link
Member

tobz commented Sep 14, 2023

Nah, no worries. At most, feel free to update the PR title and description and mark it as ready to review and all of that... but no further cleanup/tidying required.

@hlbarber hlbarber changed the title experimental: migrate from procedural to declarative macros migrate from procedural to declarative macros Sep 15, 2023
@hlbarber hlbarber marked this pull request as ready for review September 15, 2023 00:45
Copy link
Member

@tobz tobz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this looks really good and most of the comments are nits. There's one comment about a broken rustdoc link that needs to be fixed, however.

metrics/src/macros.rs Outdated Show resolved Hide resolved
metrics/src/macros.rs Outdated Show resolved Hide resolved
metrics/src/macros.rs Outdated Show resolved Hide resolved
metrics/src/macros.rs Outdated Show resolved Hide resolved
@hlbarber
Copy link
Contributor Author

On my machine cargo bench, on main vs this branch, shows no obvious regressions - worth double checking on your machine though.

Copy link
Member

@tobz tobz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this looks good to me.

Thank you for working to get this to the finish line. ❤️

@tobz tobz merged commit d788aae into metrics-rs:main Sep 21, 2023
6 of 11 checks passed
@tobz tobz added C-macros Component: macros. E-complex Effort: complex. T-refactor Type: refactor. S-awaiting-release Status: awaiting a release to be considered fixed/implemented. labels Sep 21, 2023
jvimal-eg added a commit to edgeguard-dev/metrics that referenced this pull request Dec 24, 2023
* fix prometheus metric name and label key sanitizer (metrics-rs#296)

Co-authored-by: Toby Lawrence <tobz@users.noreply.github.com>

* metrics-util: add ability to collect metrics on a per-thread basis via DebuggingRecorder (metrics-rs#299)

Signed-off-by: Toby Lawrence <toby@nuclearfurnace.com>

* (cargo-release) version 0.12.1

* Improve handling of the global recorder instance (metrics-rs#302)

This gets rid of the dangerous `static mut`, adds more comments
about the code, relaxes the orderings and documents the unsoundness
of the `clear` function, in addition to marking it unsafe.

It implements a small once_cell-like abstraction.

Co-authored-by: Toby Lawrence <toby@nuclearfurnace.com>

* Fix `metrics::Cow` provenance issue (metrics-rs#303)

* Quantile Remapping Fix (metrics-rs#304)

* update changelogs prior to release

* (cargo-release) version 0.19.0

* (cargo-release) version 0.13.0

* (cargo-release) version 0.11.0

* (cargo-release) version 0.10.0

* Update CHANGELOG.md

* Update README.md

* Update ci.yml

* Update ci.yml

* Add RollingSummary to prevent summary saturation (metrics-rs#306)

* Update quanta requirement from 0.9.3 to 0.10.0 (metrics-rs#301)

Updates the requirements on [quanta](https://github.com/metrics-rs/quanta) to permit the latest version.
- [Release notes](https://github.com/metrics-rs/quanta/releases)
- [Changelog](https://github.com/metrics-rs/quanta/blob/main/CHANGELOG.md)
- [Commits](metrics-rs/quanta@v0.9.3...v0.10.0)

---
updated-dependencies:
- dependency-name: quanta
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Document CI enforced msrv in readme and rust-version fields (metrics-rs#311)

* Change description to be SharedString (metrics-rs#312)

* Update hashbrown requirement from 0.11 to 0.12 (metrics-rs#266)

Updates the requirements on [hashbrown](https://github.com/rust-lang/hashbrown) to permit the latest version.
- [Release notes](https://github.com/rust-lang/hashbrown/releases)
- [Changelog](https://github.com/rust-lang/hashbrown/blob/master/CHANGELOG.md)
- [Commits](rust-lang/hashbrown@v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: hashbrown
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Toby Lawrence <tobz@users.noreply.github.com>

* Shims remaining AtomicU64 usage (metrics-rs#313)

* Update parking_lot requirement from 0.11 to 0.12 (metrics-rs#268)

Updates the requirements on [parking_lot](https://github.com/Amanieu/parking_lot) to permit the latest version.
- [Release notes](https://github.com/Amanieu/parking_lot/releases)
- [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md)
- [Commits](Amanieu/parking_lot@0.11.0...0.12.0)

---
updated-dependencies:
- dependency-name: parking_lot
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Toby Lawrence <tobz@users.noreply.github.com>

* update changelogs

* (cargo-release) version 0.13.1

* add std atomics handle support back + changelogs

* (cargo-release) version 0.20.0

* (cargo-release) version 0.14.0

* (cargo-release) version 0.11.0

* changelog

* (cargo-release) version 0.12.0

* (cargo-release) version 0.7.0

* update changelog

* (cargo-release) version 0.6.0

* update changelog

* (cargo-release) version 0.20.1

* Remove incorrect return info (metrics-rs#316)

* Add a `KeyName` argument to `LabelFilter::should_include_label` (metrics-rs#342)

* rewind

* (cargo-release) version 0.13.0

* Use std sync primitives instead of parking_lot. (metrics-rs#344)

* Use std::sync::Mutex instead of parking_lot::Mutex.
* Bump MSRV to 1.60.0 for CI.

Co-authored-by: Toby Lawrence <tobz@users.noreply.github.com>

* clean up github CI workflow + rust-toolchain.toml to match quanta

* update portable-atomic to 1.0

* bump to prost 0.11 + fix spelling issue in metrics-tracing-context

* bump MSRV to 1.61 + update hashbrown/ahash deps

* update termion/ordered-float and pin predicates-* to avoid stupid 1.64 MSRV

* update syn

* update quanta + clean up semver notation in cargo.toml

* cleanup README wording for MSRV policy

* Add white background to splash image (metrics-rs#348)

* Install protoc in CI.

* fix spelling error in CI workflow

* Update ci.yml

* no need to run CI against macOS/Windows specifically

* Bring the metrics-observer protobufs up to date (metrics-rs#345)

* Use global paths for macros (metrics-rs#358)

* fix changes to fully qualified metrics crate ref change in macros

* update changelog

* update tui to 0.19

* bump numpy dep

* impl std::error::Error for metrics_exporter_tcp::Error

* changelog

* tweak test to avoid unused code

* rework 32 vs 64-bit arch atomics support + a lot of import consolidation/cleanup

* const-ify some stuff + rewrite some comments for the global recorder init code

* clean up clippy lints

* bump MSRV to 1.61.0

* (cargo-release) version 0.7.0

* (cargo-release) version 0.21.0

* (cargo-release) version 0.15.0

* (cargo-release) version 0.14.0

* (cargo-release) version 0.8.0

* (cargo-release) version 0.12.0

* allow publishing

* (cargo-release) version 0.2.0

* make it publishable pt 2

* push-gateway support authentication (metrics-rs#366)

* update changelog + fix failing feature check test

* (cargo-release) version 0.12.1

* feat(util): new helper type for recovering recorder after installing it (metrics-rs#362)

* Update aho-corasick to 1.0.

* Impl From<std::borrow::Cow> for KeyName (metrics-rs#378)

* changelog

* Add `Borrow` impl to `KeyName` (metrics-rs#381)

* bump deps + clippy stuff

* changelog

* pin hashbrown to avoid MSRV bump

* (cargo-release) version 0.15.1

* (cargo-release) version 0.21.1

* Add metadata to metrics (metrics-rs#380)

* add https support in Prometheus gateway (metrics-rs#392)

* migrate from procedural to declarative macros (metrics-rs#386)

* Make `Unit` methods return `&'static str` where possible (metrics-rs#393)

* simplify macros (metrics-rs#394)

* Add support for `Arc<T>` to `metrics::Cow<'a, T>`. (metrics-rs#402)

* Add support for `tracing::Span::record`ed fields in `metrics-tracing-context` (metrics-rs#408)

* fix(prom): `RollingSummary` overflow panic (metrics-rs#423)

* update CHANGELOG

* (cargo-release) version 0.12.2

* Remove unneeded unsafe from test (metrics-rs#427)

* Fix feature check in CI (metrics-rs#428)

* update changelogs/release notes

* fix unsafe/incorrect crossbeam-epoch usage in Block<T>

* Add a clippy CI check (metrics-rs#416) (metrics-rs#417)

* Try other resolved addresses if the first one fails (metrics-rs#429)

* update changelog

* Update quanta requirement from 0.11 to 0.12 (metrics-rs#396)

Updates the requirements on [quanta](https://github.com/metrics-rs/quanta) to permit the latest version.
- [Changelog](https://github.com/metrics-rs/quanta/blob/v0.12.0/CHANGELOG.md)
- [Commits](metrics-rs/quanta@v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: quanta
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Toby Lawrence <tobz@users.noreply.github.com>

* Update ordered-float requirement from 3.4 to 4.2 (metrics-rs#421)

Updates the requirements on [ordered-float](https://github.com/reem/rust-ordered-float) to permit the latest version.
- [Release notes](https://github.com/reem/rust-ordered-float/releases)
- [Commits](reem/rust-ordered-float@v3.4.0...v4.2.0)

---
updated-dependencies:
- dependency-name: ordered-float
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix quantile api

* missing clear

* reintroduce old way to avoid big refactor

---------

Signed-off-by: Toby Lawrence <toby@nuclearfurnace.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Shaoyuan CHEN <chensy20@mails.tsinghua.edu.cn>
Co-authored-by: Toby Lawrence <tobz@users.noreply.github.com>
Co-authored-by: Toby Lawrence <toby@nuclearfurnace.com>
Co-authored-by: nils <48135649+Nilstrieb@users.noreply.github.com>
Co-authored-by: Dan Wilbanks <78116928+dfwilbanks395@users.noreply.github.com>
Co-authored-by: Daniel Nelson <daniel@wavesofdawn.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Lucas Kent <rubickent@gmail.com>
Co-authored-by: Fredrik Enestad <fredrik@enestad.com>
Co-authored-by: Christopher Hunt <huntchr@gmail.com>
Co-authored-by: zohnannor <35764628+zohnannor@users.noreply.github.com>
Co-authored-by: Sinotov Aleksandr <bratsinot@gmail.com>
Co-authored-by: Jacob Kiesel <kieseljake@live.com>
Co-authored-by: C J Silverio <ceejceej@gmail.com>
Co-authored-by: CinchBlue <aytasato@gmail.com>
Co-authored-by: JasonLi <lijingxuan92@126.com>
Co-authored-by: Mostafa Elhemali <mostafa.elhemaly@gmail.com>
Co-authored-by: Harry Barber <106155934+hlbarber@users.noreply.github.com>
Co-authored-by: Qingwen Zhao <864593343@qq.com>
Co-authored-by: david-perez <d@vidp.dev>
Co-authored-by: Lucio Franco <luciofranco14@gmail.com>
Co-authored-by: Nicolas Stinus <nicolas.stinus@gmail.com>
Co-authored-by: Valeriy V. Vorotyntsev <valery.vv@gmail.com>
@tobz
Copy link
Member

tobz commented Dec 24, 2023

Released as metrics@v0.22.0.

Thanks again for your contribution. ❤️

@tobz tobz removed the S-awaiting-release Status: awaiting a release to be considered fixed/implemented. label Dec 24, 2023
@hlbarber hlbarber deleted the decl-macros branch December 26, 2023 23:17
mnpw pushed a commit to mnpw/metrics that referenced this pull request Mar 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-macros Component: macros. E-complex Effort: complex. T-refactor Type: refactor.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants