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

tracing integration (#213) #258

Merged
merged 39 commits into from
Mar 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a61b42d
WIP
ilslv Feb 21, 2023
c063a85
Implement `tracing` events capturing and postponed output in `writer:…
ilslv Feb 22, 2023
6d9a4d7
Minor corrections
ilslv Feb 22, 2023
1358d55
Corrections, docs and clippy
ilslv Feb 23, 2023
864d1df
Add optimisation on PostponedWriter relying on fact, that we get full…
ilslv Feb 23, 2023
14af39c
Forward traces without Scenario Span to all active Scenarios
ilslv Feb 24, 2023
200c3b1
Docs and clippy
ilslv Feb 24, 2023
ea72445
Expose tracing public API to Cucumber
ilslv Feb 24, 2023
e968140
Corrections
ilslv Feb 24, 2023
5cc3068
Corrections
ilslv Feb 24, 2023
1444b28
Correct MSRV
ilslv Feb 24, 2023
e82d2ee
Merge branch 'main' into 213-tracing-integration
ilslv Feb 27, 2023
4d54d6f
tracing output test
ilslv Feb 27, 2023
3f6244d
Corrections
ilslv Feb 27, 2023
ff44f20
Corrections
ilslv Feb 27, 2023
915a30a
Corrections
ilslv Feb 27, 2023
bc323f7
Corrections
ilslv Feb 27, 2023
140f097
Corrections
ilslv Feb 27, 2023
7692a15
Corrections
ilslv Feb 27, 2023
a58cd25
Corrections
ilslv Feb 27, 2023
7fd71dc
Corrections
ilslv Feb 27, 2023
317ed7e
Corrections
ilslv Feb 27, 2023
67005d6
Corrections
ilslv Feb 27, 2023
43f5c20
WIP for fixing race between Scenario finishing and logs emitting from…
ilslv Feb 28, 2023
b080a7a
WIP for fixing race between Scenario finishing and logs emitting from…
ilslv Feb 28, 2023
860f510
WIP for fixing race between Scenario finishing and logs emitting from…
ilslv Feb 28, 2023
878e8e6
Corrections
ilslv Feb 28, 2023
42b8202
Clippy
ilslv Feb 28, 2023
620408e
Clippy
ilslv Feb 28, 2023
59a83e4
Clippy
ilslv Feb 28, 2023
23ff3f7
Clippy
ilslv Feb 28, 2023
01ff07e
Corrections
ilslv Mar 1, 2023
f63f995
Corrections and update GIFs
ilslv Mar 1, 2023
2c7ca52
Corrections
ilslv Mar 1, 2023
9f89f23
Some corrections
tyranron Mar 2, 2023
2389ba4
Replace Terminal#Manual_printing section with Terminal#Logging
ilslv Mar 3, 2023
4349578
Corrections
ilslv Mar 3, 2023
877ff3e
Explain using `print!` for `libtest`
tyranron Mar 3, 2023
8c93ff3
Corrections
tyranron Mar 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ jobs:
- output-json
- output-junit
- libtest
- tracing
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ All user visible changes to `cucumber` crate will be documented in this file. Th



## [0.20.0] · 2023-??-??
[0.20.0]: /../../tree/v0.20.0

[Diff](/../../compare/v0.19.1...v0.20.0) | [Milestone](/../../milestone/24)

### BC Breaks

- Added `Log` variant to `event::Scenario`. ([#258])

### Added

- [`tracing`] crate integration behind the `tracing` feature flag. ([#213], [#258])

[#213]: /../../issues/213
[#258]: /../../pull/258




## [0.19.1] · 2022-12-29
[0.19.1]: /../../tree/v0.19.1

Expand Down Expand Up @@ -680,6 +699,7 @@ All user visible changes to `cucumber` crate will be documented in this file. Th
[`clap`]: https://docs.rs/clap
[`gherkin`]: https://docs.rs/gherkin
[`gherkin_rust`]: https://docs.rs/gherkin_rust
[`tracing`]: https://docs.rs/tracing

[Cargo feature]: https://doc.rust-lang.org/cargo/reference/features.html
[Cucumber Expressions]: https://cucumber.github.io/cucumber-expressions
Expand Down
17 changes: 15 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ repository = "https://github.com/cucumber-rs/cucumber"
readme = "README.md"
categories = ["asynchronous", "development-tools::testing"]
keywords = ["cucumber", "testing", "bdd", "atdd", "async"]
include = ["/src/", "/tests/json.rs", "/tests/junit.rs", "/tests/libtest.rs", "/tests/wait.rs", "/LICENSE-*", "/README.md", "/CHANGELOG.md"]
include = ["/src/", "/tests/json.rs", "/tests/junit.rs", "/tests/libtest.rs", "/tests/tracing.rs", "/tests/wait.rs", "/LICENSE-*", "/README.md", "/CHANGELOG.md"]

[package.metadata.docs.rs]
all-features = true
Expand All @@ -37,12 +37,14 @@ output-json = ["dep:Inflector", "dep:serde", "dep:serde_json", "timestamps"]
output-junit = ["dep:junit-report", "timestamps"]
# Enables timestamps collecting for all events.
timestamps = []
# Enables integraion with `tracing` crate.
tracing = ["dep:crossbeam-utils", "dep:tracing", "dep:tracing-subscriber"]

[dependencies]
async-trait = "0.1.43"
clap = { version = "4.0.27", features = ["derive", "wrap_help"] }
console = "0.15"
derive_more = { version = "0.99.17", features = ["as_ref", "deref", "deref_mut", "display", "error", "from", "into"], default_features = false }
derive_more = { version = "0.99.17", features = ["as_ref", "deref", "deref_mut", "display", "error", "from", "from_str", "into"], default_features = false }
drain_filter_polyfill = "0.1.2"
either = "1.6"
futures = "0.3.17"
Expand All @@ -53,6 +55,7 @@ is-terminal = "0.4.4"
itertools = "0.10"
linked-hash-map = "0.5.3"
once_cell = "1.13"
pin-project = "1.0"
regex = "1.5.5"
sealed = "0.4"
smart-default = "0.6"
Expand All @@ -71,6 +74,11 @@ Inflector = { version = "0.11", default-features = false, optional = true }
# "output-junit" feature dependencies.
junit-report = { version = "0.8", optional = true }

# "tracing" feature dependencies.
crossbeam-utils = { version = "0.8.14", optional = true }
tracing = { version = "0.1", optional = true }
tracing-subscriber = { version = "0.3.16", optional = true }

[dev-dependencies]
derive_more = "0.99.17"
rand = "0.8"
Expand All @@ -89,6 +97,11 @@ required-features = ["output-junit"]
name = "libtest"
required-features = ["libtest"]

[[test]]
name = "tracing"
required-features = ["tracing"]
harness = false

[[test]]
name = "wait"
required-features = ["libtest"]
Expand Down
2 changes: 2 additions & 0 deletions README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [JUnit XML report](output/junit.md)
- [Cucumber JSON format](output/json.md)
- [Multiple outputs](output/multiple.md)
- [`tracing` integration](output/tracing.md)
ilslv marked this conversation as resolved.
Show resolved Hide resolved
- [IntelliJ Rust integration](output/intellij.md)
- [Architecture](architecture/index.md)
- [Custom `Parser`](architecture/parser.md)
Expand Down
3 changes: 2 additions & 1 deletion book/src/output/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ This chapter describes possible way and tweaks of outputting test suite results.
2. [JUnit XML report](junit.md)
3. [Cucumber JSON format](json.md)
4. [Multiple outputs](multiple.md)
5. [IntelliJ Rust integration](intellij.md)
5. [`tracing` integration](tracing.md)
6. [IntelliJ Rust integration](intellij.md)
47 changes: 44 additions & 3 deletions book/src/output/terminal.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,9 @@ async fn main() {



## Manual printing
## Debug printing and/or logging

Though [`cucumber`] crate doesn't capture any manual printing produced in a [step] matching function (such as [`dbg!`] or [`println!`] macros), it may be [quite misleading][#177] to produce and use it for debugging purposes. The reason is simply because [`cucumber`] crate executes [scenario]s concurrently and [normalizes][3] their results before outputting, while any manual print is produced instantly at the moment of its [step] execution.
Though [`cucumber`] crate doesn't capture any manual debug printing produced in a [step] matching function (such as [`dbg!`] or [`println!`] macros), it may be [quite misleading][#177] to produce and use it for debugging purposes. The reason is simply because [`cucumber`] crate executes [scenario]s concurrently and [normalizes][3] their results before outputting, while any manual print is produced instantly at the moment of its [step] execution.

> __WARNING:__ Moreover, manual printing will very likely interfere with [default][1] interactive pretty-printing.

Expand Down Expand Up @@ -348,7 +348,48 @@ async fn main() {
```
![record](../rec/output_terminal_custom.gif)

> __NOTE__: The custom print is still output before its [step], because is printed during the [step] execution.
> __NOTE__: The custom print is still output before its [step], because is printed during the [step] execution.

Much better option for debugging would be using [`tracing` crate integration](tracing.md) instead of [`dbg!`]/[`println!`] for doing logs.

```rust
# extern crate cucumber;
# extern crate tokio;
# extern crate tracing;
#
use std::{
sync::atomic::{AtomicUsize, Ordering},
time::Duration,
};

use cucumber::{given, then, when, World as _};
use tokio::time;

#[derive(cucumber::World, Debug, Default)]
struct World;

#[given(regex = r"(\d+) secs?")]
#[when(regex = r"(\d+) secs?")]
#[then(regex = r"(\d+) secs?")]
async fn sleep(_: &mut World, secs: u64) {
static ID: AtomicUsize = AtomicUsize::new(0);

let id = ID.fetch_add(1, Ordering::Relaxed);

tracing::info!("before {secs}s sleep: {id}");
time::sleep(Duration::from_secs(secs)).await;
tracing::info!("after {secs}s sleep: {id}");
}

#[tokio::main]
async fn main() {
World::cucumber()
.init_tracing()
.run("tests/features/wait")
.await;
}
```
![record](../rec/tracing_basic_writer.gif)



Expand Down
119 changes: 119 additions & 0 deletions book/src/output/tracing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
`tracing` integration
ilslv marked this conversation as resolved.
Show resolved Hide resolved
=====================

[`Cucumber::init_tracing()`] (enabled by `tracing` feature in `Cargo.toml`) initializes global [`tracing::Subscriber`] that intercepts all the [`tracing` events][1] and transforms them into [`event::Scenario::Log`]s. Each [`Writer`] can handle those [`event::Scenario::Log`]s in its own way. [`writer::Basic`], for example, emits all the [`event::Scenario::Log`]s only whenever [scenario] itself is outputted.

```rust
# extern crate cucumber;
# extern crate tokio;
# extern crate tracing;
#
use std::{
sync::atomic::{AtomicUsize, Ordering},
time::Duration,
};

use cucumber::{given, then, when, World as _};
use tokio::time;

#[derive(cucumber::World, Debug, Default)]
struct World;

#[given(regex = r"(\d+) secs?")]
#[when(regex = r"(\d+) secs?")]
#[then(regex = r"(\d+) secs?")]
async fn sleep(_: &mut World, secs: u64) {
static ID: AtomicUsize = AtomicUsize::new(0);

let id = ID.fetch_add(1, Ordering::Relaxed);

tracing::info!("before {secs}s sleep: {id}");
time::sleep(Duration::from_secs(secs)).await;
tracing::info!("after {secs}s sleep: {id}");
}

#[tokio::main]
async fn main() {
World::cucumber()
.init_tracing()
.run("tests/features/wait")
.await;
}
```
![record](../rec/tracing_basic_writer.gif)




## Loosing [`tracing::Span`]

[`tracing::Span`] is used to wire emitted [`tracing` events][1] (logs) to concrete [scenario]s: each [scenario] is executed in its own [`tracing::Span`]. In case a [`tracing` event][1] is emitted outside the [`tracing::Span`] of a [scenario], it will be propagated to every running [scenario] at the moment.

```rust
# extern crate cucumber;
# extern crate tokio;
# extern crate tracing;
#
# use std::{
# sync::atomic::{AtomicUsize, Ordering},
# time::Duration,
# };
#
# use cucumber::{given, then, when, World as _};
# use tokio::time;
#
# #[derive(cucumber::World, Debug, Default)]
# struct World;
#
# #[given(regex = r"(\d+) secs?")]
# #[when(regex = r"(\d+) secs?")]
# #[then(regex = r"(\d+) secs?")]
# async fn sleep(_: &mut World, secs: u64) {
# static ID: AtomicUsize = AtomicUsize::new(0);
#
# let id = ID.fetch_add(1, Ordering::Relaxed);
#
# tracing::info!("before {secs}s sleep: {id}");
# time::sleep(Duration::from_secs(secs)).await;
# tracing::info!("after {secs}s sleep: {id}");
# }
#
#[tokio::main]
async fn main() {
// Background task outside of any scenario.
tokio::spawn(async {
let mut id = 0;
loop {
time::sleep(Duration::from_secs(3)).await;
tracing::info!("Background: {id}");
id += 1;
}
});

World::cucumber()
.init_tracing()
.run("tests/features/wait")
.await;
}
```
![record](../rec/tracing_outside_span.gif)

As we see, `Background: 2`/`3`/`4` is shown in multiple [scenario]s, while being emitted only once each.

> __TIP__: If you're [`spawn`]ing a [`Future`] inside your [step] matching function, consider to [propagate][2] its [`tracing::Span`] into the [`spawn`]ed [`Future`] for outputting its logs properly.




[`Cucumber::init_tracing()`]: https://docs.rs/cucumber/*/cucumber/struct.Cucumber.html#method.init_tracing
[`event::Scenario::Log`]: https://docs.rs/cucumber/*/cucumber/event/enum.Scenario.html#variant.Log
[`Future`]: https://doc.rust-lang.org/stable/std/future/trait.Future.html
[`spawn`]: https://docs.rs/tokio/*/tokio/fn.spawn.html
[`tracing::Span`]: https://docs.rs/tracing/*/tracing/struct.Span.html
[`tracing::Subscriber`]: https://docs.rs/tracing/*/tracing/trait.Subscriber.html
[`Writer`]: https://docs.rs/cucumber/*/cucumber/writer/trait.Writer.html
[`writer::Basic`]: https://docs.rs/cucumber/*/cucumber/writer/struct.Basic.html
[scenario]: https://cucumber.io/docs/gherkin/reference#example
[step]: https://cucumber.io/docs/gherkin/reference#steps
[1]: https://docs.rs/tracing/*/tracing/index.html#events
[2]: https://docs.rs/tracing/*/tracing/struct.Span.html#method.enter
Binary file added book/src/rec/tracing_basic_writer.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added book/src/rec/tracing_outside_span.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/cucumber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ where
/// [`Runner`] executing [`Scenario`]s and producing [`event`]s.
///
/// [`Scenario`]: gherkin::Scenario
runner: R,
pub(crate) runner: R,

/// [`Writer`] outputting [`event`]s to some output.
writer: Wr,
Expand Down
4 changes: 4 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,9 @@ pub enum Scenario<World> {
/// [`Step`] event.
Step(Arc<gherkin::Step>, Step<World>),

/// [`Scenario`]'s log entry is emitted.
Log(String),

/// [`Scenario`] execution being finished.
///
/// [`Scenario`]: gherkin::Scenario
Expand All @@ -512,6 +515,7 @@ impl<World> Clone for Scenario<World> {
Self::Background(Arc::clone(bg), ev.clone())
}
Self::Step(st, ev) => Self::Step(Arc::clone(st), ev.clone()),
Self::Log(msg) => Self::Log(msg.clone()),
Self::Finished => Self::Finished,
}
}
Expand Down
Loading