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

Setup tracing and document tracing usage #12730

Merged
merged 3 commits into from
Aug 8, 2024
Merged

Setup tracing and document tracing usage #12730

merged 3 commits into from
Aug 8, 2024

Conversation

MichaReiser
Copy link
Member

@MichaReiser MichaReiser commented Aug 7, 2024

This PR extends our tracing configuration with more refined verbosity levels and adds a small guide on how/when to use tracing.

Filtering

I haven't figured out how to do field-level filtering. It is supported according to the EnvFilter docs but I always get too many events even if I write complete rubbish field filters ;(

Existing messages

We should revisit our existing messages and improve them. But we can do so while working on the relevant code and as we learn more about logging.

When to use instrument

My take on this is that instrument helps us to get nice flamegraphs and to attribute events to specific spans. I'm not entirely sure yet what the right amount of instrument attributes is. I suspect that we currently have too many. Let's figure this out over time.

Test plan

Using -v

INFO Target version: py38

Using -vv

2024-08-07 15:21:47.531307982 DEBUG Checking workspace
2024-08-07 15:21:47.531494121 DEBUG Checking package /home/micha/astral/test
2024-08-07 15:21:47.535507928 DEBUG Checking file /home/micha/astral/test/c.py
2024-08-07 15:21:47.536372106 INFO Target version: py38
2024-08-07 15:21:47.536477595 DEBUG Resolved module 'child.a' to '/home/micha/astral/test/child/a.py'.
2024-08-07 15:21:47.539610905 DEBUG Resolved module 'typing' to 'stdlib/typing.pyi'.
2024-08-07 15:21:47.552381082 DEBUG Resolved module 'builtins' to 'stdlib/builtins.pyi'.
2024-08-07 15:21:47.585516812 DEBUG Module 'b' not found in the search paths.
2024-08-07 15:21:47.585969620 DEBUG Checking file /home/micha/astral/test/symlinks/foo.py
2024-08-07 15:21:47.586249639 DEBUG Resolved module 'bar.baz' to '/home/micha/astral/test/bar/baz.py'.
2024-08-07 15:21:47.586441368 DEBUG Checking file /home/micha/astral/test/bar/__init__.py
2024-08-07 15:21:47.586548578 DEBUG Checking file /home/micha/astral/test/hard-links/main.py
2024-08-07 15:21:47.586859207 DEBUG Module 'a' not found in the search paths.
2024-08-07 15:21:47.587004216 DEBUG Module 'a_alias' not found in the search paths.
2024-08-07 15:21:47.587320915 DEBUG Checking file /home/micha/astral/test/hard-links/a.py
2024-08-07 15:21:47.587575634 DEBUG Checking file /home/micha/astral/test/test-notebook.ipynb
2024-08-07 15:21:47.588266662 DEBUG Resolved module 'random' to 'stdlib/random.pyi'.
2024-08-07 15:21:47.588515651 DEBUG Resolved module 'x' to '/home/micha/astral/test/x.py'.
2024-08-07 15:21:47.588970629 DEBUG Checking file /home/micha/astral/test/x.py
2024-08-07 15:21:47.589287938 DEBUG Checking file /home/micha/astral/test/bar/baz.py
2024-08-07 15:21:47.589447748 DEBUG Checking file /home/micha/astral/test/hard-links/sub/a.py
2024-08-07 15:21:47.589697997 DEBUG Checking file /home/micha/astral/test/child/a.py
2024-08-07 15:21:47.589832246 DEBUG Checking file /home/micha/astral/test/child3/a.py
2024-08-07 15:21:47.590109115 DEBUG Checking file /home/micha/astral/test/child2/a.py
2024-08-07 15:21:47.590360335 DEBUG Checking file /home/micha/astral/test/test.py
2024-08-07 15:21:47.591255552 DEBUG Resolved module '_typeshed' to 'stdlib/_typeshed/__init__.pyi'.
2024-08-07 15:21:47.596300975 DEBUG Checking file /home/micha/astral/test/hard-links/a_alias.py
2024-08-07 15:21:47.596563415 DEBUG Checking file /home/micha/astral/test/child/__init__.py
2024-08-07 15:21:47.596673024 DEBUG Checking file /home/micha/astral/test/child3/__init__.py
2024-08-07 15:21:47.596774514 DEBUG Checking file /home/micha/astral/test/child2/__init__.py
2024-08-07 15:21:47.596876443 DEBUG Checking file /home/micha/astral/test/y.py
2024-08-07 15:21:47.597101013 DEBUG Checking file /home/micha/astral/test/hard-links/parent_test.py

Using -vvv in release mode

24┐red_knot_workspace::workspace::check{}
24├─   0.000975s   0ms DEBUG red_knot_workspace::workspace Checking workspace
24└─┐red_knot_workspace::workspace::check{self=Package { [salsa id]: Id(0) }}
24  ├─   0.000990s   0ms DEBUG red_knot_workspace::workspace Checking package /home/micha/astral/test
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/child3/a.py")}
24    ├─   0.003016s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/child3/a.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/child3/a.py")}
24      ├─   0.003182s   0ms  INFO red_knot_module_resolver::resolver Target version: py38
24      ├─   0.003698s   0ms DEBUG red_knot_module_resolver::resolver Resolved module 'typing' to 'stdlib/typing.pyi'.
24      ├─   0.005381s   2ms DEBUG red_knot_module_resolver::resolver Resolved module 'builtins' to 'stdlib/builtins.pyi'.
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/test-notebook.ipynb")}
24    ├─   0.009774s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/test-notebook.ipynb
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/test-notebook.ipynb")}
24      ├─   0.009876s   0ms DEBUG red_knot_module_resolver::resolver Resolved module 'random' to 'stdlib/random.pyi'.
24      ├─   0.009895s   0ms DEBUG red_knot_module_resolver::resolver Resolved module 'child.a' to '/home/micha/astral/test/child/a.py'.
24      ├─   0.009928s   0ms DEBUG red_knot_module_resolver::resolver Resolved module 'x' to '/home/micha/astral/test/x.py'.
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/y.py")}
24    ├─   0.009976s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/y.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/y.py")}
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/bar/baz.py")}
24    ├─   0.010015s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/bar/baz.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/bar/baz.py")}
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/x.py")}
24    ├─   0.010051s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/x.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/x.py")}
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/child3/__init__.py")}
24    ├─   0.010094s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/child3/__init__.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/child3/__init__.py")}
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/c.py")}
24    ├─   0.010120s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/c.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/c.py")}
24      ├─   0.010172s   0ms DEBUG red_knot_module_resolver::resolver Module 'b' not found in the search paths.
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/hard-links/main.py")}
24    ├─   0.010200s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/hard-links/main.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/hard-links/main.py")}
24      ├─   0.010241s   0ms DEBUG red_knot_module_resolver::resolver Module 'a' not found in the search paths.
24      ├─   0.010255s   0ms DEBUG red_knot_module_resolver::resolver Module 'a_alias' not found in the search paths.
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/hard-links/a_alias.py")}
24    ├─   0.010279s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/hard-links/a_alias.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/hard-links/a_alias.py")}
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/child/a.py")}
24    ├─   0.010314s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/child/a.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/child/a.py")}
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/child2/a.py")}
24    ├─   0.010331s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/child2/a.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/child2/a.py")}
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/symlinks/foo.py")}
24    ├─   0.010360s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/symlinks/foo.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/symlinks/foo.py")}
24      ├─   0.010389s   0ms DEBUG red_knot_module_resolver::resolver Resolved module 'bar.baz' to '/home/micha/astral/test/bar/baz.py'.
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/hard-links/sub/a.py")}
24    ├─   0.010404s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/hard-links/sub/a.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/hard-links/sub/a.py")}
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/hard-links/parent_test.py")}
24    ├─   0.010442s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/hard-links/parent_test.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/hard-links/parent_test.py")}
24      ├─   0.010535s   0ms DEBUG red_knot_module_resolver::resolver Resolved module '_typeshed' to 'stdlib/_typeshed/__init__.pyi'.
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/child/__init__.py")}
24    ├─   0.011076s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/child/__init__.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/child/__init__.py")}
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/child2/__init__.py")}
24    ├─   0.011104s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/child2/__init__.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/child2/__init__.py")}
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/test.py")}
24    ├─   0.011125s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/test.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/test.py")}
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/bar/__init__.py")}
24    ├─   0.011189s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/bar/__init__.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/bar/__init__.py")}
24    ┌─┘
24  ┌─┘
24  └─┐red_knot_workspace::workspace::check_file{path=System("/home/micha/astral/test/hard-links/a.py")}
24    ├─   0.011213s   0ms DEBUG red_knot_workspace::workspace Checking file /home/micha/astral/test/hard-links/a.py
24    └─┐red_knot_workspace::lint::lint_semantic{file=System("/home/micha/astral/test/hard-links/a.py")}
24    ┌─┘
24  ┌─┘
24┌─┘
24┘

Comment on lines 175 to 251
struct RedKnotFormat {
display_timestamp: bool,
display_level: bool,
show_spans: bool,
}

/// See <https://docs.rs/tracing-subscriber/0.3.18/src/tracing_subscriber/fmt/format/mod.rs.html#1026-1156>
impl<S, N> FormatEvent<S, N> for RedKnotFormat
where
S: Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
fn format_event(
&self,
ctx: &FmtContext<'_, S, N>,
mut writer: Writer<'_>,
event: &Event<'_>,
) -> fmt::Result {
let meta = event.metadata();
let ansi = writer.has_ansi_escapes();

if self.display_timestamp {
let timestamp = chrono::Local::now()
.format("%Y-%m-%d %H:%M:%S.%f")
.to_string();
if ansi {
write!(writer, "{} ", timestamp.dimmed())?;
} else {
write!(
writer,
"{} ",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S.%f")
)?;
}
}

if self.display_level {
let level = meta.level();
// Same colors as tracing
if ansi {
let formatted_level = level.to_string();
match *level {
tracing::Level::TRACE => {
write!(writer, "{} ", formatted_level.purple().bold())?
}
tracing::Level::DEBUG => write!(writer, "{} ", formatted_level.blue().bold())?,
tracing::Level::INFO => write!(writer, "{} ", formatted_level.green().bold())?,
tracing::Level::WARN => write!(writer, "{} ", formatted_level.yellow().bold())?,
tracing::Level::ERROR => write!(writer, "{} ", level.to_string().red().bold())?,
}
} else {
write!(writer, "{level} ")?;
}
}

if self.show_spans {
let span = event.parent();
let mut seen = false;

let span = span
.and_then(|id| ctx.span(id))
.or_else(|| ctx.lookup_current());

let scope = span.into_iter().flat_map(|span| span.scope().from_root());

for span in scope {
seen = true;
if ansi {
write!(writer, "{}:", span.metadata().name().bold())?;
} else {
write!(writer, "{}:", span.metadata().name())?;
}
}

if seen {
writer.write_char(' ')?;
}
}

ctx.field_format().format_fields(writer.by_ref(), event)?;

writeln!(writer)
}
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This is copied from uv. The only change is that I use local time over UTC time.

@MichaReiser MichaReiser added the red-knot Multi-file analysis & type inference label Aug 7, 2024
@MichaReiser MichaReiser marked this pull request as ready for review August 7, 2024 12:57
@zanieb
Copy link
Member

zanieb commented Aug 7, 2024

What the heck it's not even user-facing and it's nicer than the setup we have in uv!

@zanieb
Copy link
Member

zanieb commented Aug 7, 2024

Do you turn on nested span indentation at higher verbosity levels? That's kind of nice.

@MichaReiser
Copy link
Member Author

Do you turn on nested span indentation at higher verbosity levels? That's kind of nice.

It's roughly the same as uv where -vvv uses the hierarchical output format and -vv shows timestamp which -v doesn't

https://github.com/astral-sh/uv/blob/f63a1fde4266a6b2137461469d8ce811f38a639e/crates/uv/src/logging.rs#L166-L178

Most of the code is actually just adopted from UV.

Copy link
Contributor

github-actions bot commented Aug 7, 2024

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

Formatter (stable)

ℹ️ ecosystem check encountered format errors. (no format changes; 1 project error)

openai/openai-cookbook (error)

warning: Detected debug build without --no-cache.
error: Failed to parse examples/Chat_finetuning_data_prep.ipynb:6:18:25: Unparenthesized generator expression cannot be used here
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_box.ipynb:13:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_confluence.ipynb:15:1:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_gmail.ipynb:15:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_jira.ipynb:15:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_notion.ipynb:15:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_sharepoint_doc.ipynb:28:1:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_sharepoint_text.ipynb:28:1:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_sql_database.ipynb:2:2:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_middleware_azure_function.ipynb:37:1:13: Simple statements must be separated by newlines or semicolons

Formatter (preview)

ℹ️ ecosystem check encountered format errors. (no format changes; 1 project error)

openai/openai-cookbook (error)

ruff format --preview

warning: Detected debug build without --no-cache.
error: Failed to parse examples/Chat_finetuning_data_prep.ipynb:6:18:25: Unparenthesized generator expression cannot be used here
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_box.ipynb:13:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_confluence.ipynb:15:1:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_gmail.ipynb:15:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_jira.ipynb:15:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_notion.ipynb:15:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_sharepoint_doc.ipynb:28:1:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_sharepoint_text.ipynb:28:1:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_sql_database.ipynb:2:2:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_middleware_azure_function.ipynb:37:1:13: Simple statements must be separated by newlines or semicolons

@MichaReiser MichaReiser force-pushed the red-knot-tracing branch 2 times, most recently from 500d4d9 to b0a37e9 Compare August 7, 2024 14:40
Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

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

This looks amazing!! Thanks for the clear documentation.

The one thing not mentioned here which I would find really useful is how to enable this for a test in e.g. red_knot_python_semantic crate, or if that's even doable. I often find myself debugging something in the context of a test, and it would be really useful to have tracing output.

crates/red_knot/docs/tracing.md Outdated Show resolved Hide resolved
@@ -0,0 +1,103 @@
# Tracing
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume that the crates/red_knot/docs/... directory is for developer-facing docs, not user-facing docs?

Copy link
Member Author

Choose a reason for hiding this comment

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

For now, that's certainly true I don't know about long-term. We could have docs/users and docs/dev if we end up with needing both. But most user-facing documentation should probably live on the website.

crates/red_knot/src/logging.rs Outdated Show resolved Hide resolved
@MichaReiser
Copy link
Member Author

@carljm in what kind of tracing are you interested in tests? Is it the log messages or primarily the trace spans (or both)

A test helper that accepts an EnvFilter does sound useful for ad-hoc test inspections.

@carljm
Copy link
Contributor

carljm commented Aug 7, 2024

Potentially both. It's for the same debugging purposes that you'd use -vv or -vvv on the command line.

@MichaReiser
Copy link
Member Author

Potentially both. It's for the same debugging purposes that you'd use -vv or -vvv on the command line.

I can take a look as a separate (low priority) PR. I don't think I want to expose both -vv and -vvv styles. I might just go with -vvv

@MichaReiser MichaReiser enabled auto-merge (squash) August 8, 2024 06:24
@MichaReiser MichaReiser merged commit dc6aafe into main Aug 8, 2024
19 checks passed
@MichaReiser MichaReiser deleted the red-knot-tracing branch August 8, 2024 06:28
Copy link

codspeed-hq bot commented Aug 8, 2024

CodSpeed Performance Report

Merging #12730 will improve performances by 7.46%

Comparing red-knot-tracing (6e00f3f) with main (5107a50)

Summary

⚡ 1 improvements
✅ 31 untouched benchmarks

Benchmarks breakdown

Benchmark main red-knot-tracing Change
linter/all-rules[numpy/globals.py] 778.8 µs 724.7 µs +7.46%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
red-knot Multi-file analysis & type inference
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants