Skip to content

Using tracing crate #3960

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

Closed
wants to merge 4 commits into from
Closed

Using tracing crate #3960

wants to merge 4 commits into from

Conversation

link2xt
Copy link
Collaborator

@link2xt link2xt commented Jan 16, 2023

No description provided.

@link2xt link2xt force-pushed the link2xt/tracing branch 2 times, most recently from 7c8626e to d137f9e Compare January 16, 2023 15:22
@flub
Copy link
Contributor

flub commented Jan 16, 2023

Oh, I've wanted this for a while :)

@link2xt
Copy link
Collaborator Author

link2xt commented Jan 17, 2023

There are three main tasks in setting up the tracing crate:

  1. Adding context ID spans to context methods, so it is possible to identify which context emitted an event.
  2. Emitting tracing events instead of Delta Chat INFO/WARNING/ERROR events.
  3. Setting up a subscriber to catch tracing events, convert them into Delta Chat events and push into the event channel.

Subscriber is already implemented in this PR as an EventLayer layer and Events.to_layer() method, so it is easy to construct a subscriber, but it is not obvious where to set it up.

One option is to instrument all Context and Accounts calls.
This option does not introduce any API changes,
but makes it impossible for library users to setup other advanced subscribers to collect traces instead of collecting all tracing events to the Delta Chat event emitter.

I am currently considering not setting up the subscriber from the deltachat crate at all, so it is up to the user whether they want to get logs in the form of Delta Chat events or using their own subscriber.

Instead:

  1. Rust programs, such as REPL, examples and bots will set up their own subscriber manually. They don't need tracing events to be converted into Delta Chat events in most cases.
  2. deltachat-rpc-server may convert all tracing events into JSON-RPC notifications itself, using its own subscriber.
  3. deltachat-ffi will set up subscribers on every FFI call for compatibility, using with_subscriber on the future before passing it into block_on() or .awaiting it. This affects non-async python bindings and node module. Python tests need this, because they set up many independent contexts with independent event channels and rely on INFO events being emitted from the corresponding channel.

@flub
Copy link
Contributor

flub commented Jan 17, 2023

Subscriber is already implemented in this PR as an EventLayer layer and Events.to_layer() method, so it is easy to construct a subscriber, but it is not obvious where to set it up.

This layer should be part of the Rust public API.

1. Rust programs, such as REPL, examples and bots will set up their own subscriber manually. They don't need `tracing` events to be converted into Delta Chat events in most cases.

2. `deltachat-rpc-server` may convert all tracing events into JSON-RPC notifications itself, using its own subscriber.

3. `deltachat-ffi` will set up subscribers on every FFI call for compatibility, using [with_subscriber](https://docs.rs/tracing-futures/latest/tracing_futures/trait.WithSubscriber.html) on the future before passing it into `block_on()` or `.await`ing it. This affects non-async python bindings and node module. Python tests need this, because they set up many independent contexts with independent event channels and rely on INFO events being emitted from the corresponding channel.

Yes, I agree this is the right approach.

src/events.rs Outdated
let &level = event.metadata().level();

// FIXME
let context_id = 0;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's still a question where to get this context_id from.

It should be possible to set it from Accounts.start_io() for every account.start_io() future.
I am currently considering looking for context_id field on the current span. If current span has no context ID field, then set it to 0. The problem is that some library called by delta chat core may set up a span too, then context_id will become invisible, but at least all delta chat events will have proper context ID. We can also try to walk the span tree if needed.

Copy link
Contributor

@flub flub Jan 18, 2023

Choose a reason for hiding this comment

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

Could we require a subscriber per context and insert the context id into the subscriber at creation time?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought about this, but this means account manager will have to create subscribers, and these subscribers will catch all the tracing events, converting them to Delta Chat events. For Rust programs like repl and Rust bots these subscribers are not needed, they might want to subscribe to all events directly from main() and account manager creating its own subscribers will interfere.

@hpk42
Copy link
Contributor

hpk42 commented Jan 19, 2023 via email

@flub
Copy link
Contributor

flub commented Jan 19, 2023 via email

@link2xt link2xt force-pushed the link2xt/tracing branch 7 times, most recently from f2bedff to ad03ac7 Compare January 23, 2023 00:04
@link2xt link2xt marked this pull request as ready for review January 23, 2023 01:54
@iequidoo
Copy link
Collaborator

I guess a standard span name of context with an id field would work this, rather than searching any span for a matching id (or context_id) field. The trickier bit will be to activate that span in the right places.

Took a glance at tracing crate, but don't see any way for looking for a particular span, even for getting a parent one. Only Span::current() function. Can't you point me to the proper API?

@link2xt
Copy link
Collaborator Author

link2xt commented Jan 26, 2023

Took a glance at tracing crate, but don't see any way for looking for a particular span, even for getting a parent one. Only Span::current() function. Can't you point me to the proper API?

See events.rs, it's already implemented. When span is created, layer is notified and records "context_id" field of a span into "extension", which can then be looked up when event arrives. It already works and is tested by python tests.

@link2xt
Copy link
Collaborator Author

link2xt commented Jan 26, 2023

The PR is ready for review, it only needs rebasing and maybe repl/README updates to make it usable. But applications and bots built with this will already work.

src/events.rs Outdated
if let Some(span_context_id) = span.extensions().get::<EventContextId>() {
context_id = *span_context_id;
if context_id.0 > 0 {
break;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can't we also set here EventContextId for the original span so that we don't need to loop again on subsequent events?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also i'd prefer to have a span id in the log instead of a context id (which is per-account afaiu). So, when an event occurs, and it's the first event in its span, we, say, first log that there's a new span (in the context of its parent span), i.e. send a fake event "span created" from its parent span (which propagate up if needed), and then process the original event. This way logs would be more structured. And also spans w/o "real" events wouldn't produce any logs

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Can't we also set here EventContextId for the original span so that we don't need to loop again on subsequent events?

I am not sure such microoptimization makes things better. If there is only one event emitted in this span, then it's an additional memory write for no gain.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Also i'd prefer to have a span id in the log instead of a context id (which is per-account afaiu).

Context ID inside event indicates which account received an event when account manager is used and there is only a single event emitter for multiple accounts. I do not see how span ID helps with this, it only indicates part of the code which emitted an event, but there is no way to tell if it's the current account or another account running in the background.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I am not sure such microoptimization makes things better. If there is only one event emitted in this span, then it's an additional memory write for no gain.

Yes, but i personally prefer things that scale better in questional situations. Anyway, it's not critical here.

Context ID inside event indicates which account received an event when account manager is used and there is only a single event emitter for multiple accounts. I do not see how span ID helps with this, it only indicates part of the code which emitted an event, but there is no way to tell if it's the current account or another account running in the background.

I suppose we have different root spans for accounts and thus can learn from logs which account an event belongs to. Anyway, i don't suggest to implement this now as it changes the structure of logs. Just to think how we could make logs more structured and also get rid of such loops searching for EventContextId

@@ -11,18 +11,24 @@ use deltachat_jsonrpc::api::{Accounts, CommandApi};
use futures_lite::stream::StreamExt;
use tokio::io::{self, AsyncBufReadExt, BufReader};
use tokio::task::JoinHandle;
use tracing::{info, trace};
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we can reexport all such macros somewhere in our crate to just write smth like use crate::tracing::macros::*? Because if we need, say, info, we also need other macros too and it's not convenient to constantly fix these use statements

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There is an upstream proposal to implement use tracing::prelude::*: tokio-rs/tracing#167 . Wildcard import of modules not called prelude is generally a bad style, clippy will complain.

We can of course add our own internal prelude module, but this seems better suited for upstream or at least a separate crate.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Btw, for now we can use tracing as log; and this commit will be smaller and we don't need to list all necessary macros explicitly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We can also define our own private module deltachat::prelude and include deltachat::prelude::* everywhere.

@iequidoo iequidoo self-requested a review January 27, 2023 16:24
Copy link
Collaborator

@iequidoo iequidoo left a comment

Choose a reason for hiding this comment

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

Looks good to me but maybe somebody with more experience with logging/tracing in Rust should take a look also

@link2xt link2xt force-pushed the link2xt/tracing branch 2 times, most recently from c14a00e to a85b988 Compare January 30, 2023 11:28
@link2xt link2xt marked this pull request as ready for review March 14, 2023 13:49
@link2xt link2xt requested a review from flub March 14, 2023 13:49
@link2xt
Copy link
Collaborator Author

link2xt commented Mar 14, 2023

I removed all the #[instrument] macros and implemented .bind_context_id() instead.

@link2xt link2xt requested a review from iequidoo March 14, 2023 13:55
@link2xt link2xt force-pushed the link2xt/tracing branch 2 times, most recently from d24dc3a to 75b4100 Compare March 15, 2023 13:50
@link2xt link2xt marked this pull request as draft March 15, 2023 13:55
@link2xt
Copy link
Collaborator Author

link2xt commented Mar 15, 2023

It is unclear now how to set the context/account ID when JSON-RPC requests are made. In dc_jsonrpc_request we spawn a request handler, but have no universal way to attribute an account ID to the request. For some requests it is a positional argument, but not for all of them.

@link2xt link2xt marked this pull request as ready for review March 15, 2023 15:25
@@ -50,13 +50,13 @@ impl Context {
/// Set last error string.
/// Implemented as blocking as used from macros in different, not always async blocks.
pub fn set_last_error(&self, error: &str) {
let mut last_error = self.last_error.write().unwrap();
let mut last_error = self.events.last_error.write().unwrap();
*last_error = error.to_string();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Again, maybe call to_string() before taking lock?

}

#[macro_export]
macro_rules! warn {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since the new warn! macro doesn't take a context anymore, would be nice to adapt log_err() and so on, too, maybe by just letting it use the warn! macro. After rebasing though, because it'll create a merge conflict otherwise, and it's not urgent either.

@link2xt link2xt deleted the branch master October 25, 2023 21:22
@link2xt link2xt closed this Oct 25, 2023
@link2xt link2xt reopened this Oct 25, 2023
@link2xt link2xt deleted the branch master December 17, 2023 17:58
@link2xt link2xt closed this Dec 17, 2023
@dignifiedquire
Copy link
Collaborator

@link2xt what happened to this?

@link2xt
Copy link
Collaborator Author

link2xt commented May 18, 2024

Was probably closed when we renamed master to main and deleted master.

But the problem is that every caller has to set an account_id and if someone uses deltachat crate directly they have to do it as well - currently it is not needed.

@link2xt
Copy link
Collaborator Author

link2xt commented Jun 23, 2025

This is now replaced with a much simpler approach #6919

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants