-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
wrap timed streams and iterators in tracing::Spans; follow Opentelemetry conventions #3313
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,23 @@ macro_rules! private_tracing_dynamic_event { | |
}}; | ||
} | ||
|
||
#[doc(hidden)] | ||
#[macro_export] | ||
macro_rules! private_tracing_dynamic_span { | ||
(target: $target:expr, $level:expr, $($args:tt)*) => {{ | ||
use ::tracing::Level; | ||
|
||
match $level { | ||
Level::ERROR => ::tracing::span!(target: $target, Level::ERROR, $($args)*), | ||
Level::WARN => ::tracing::span!(target: $target, Level::WARN, $($args)*), | ||
Level::INFO => ::tracing::span!(target: $target, Level::INFO, $($args)*), | ||
Level::DEBUG => ::tracing::span!(target: $target, Level::DEBUG, $($args)*), | ||
Level::TRACE => ::tracing::span!(target: $target, Level::TRACE, $($args)*), | ||
} | ||
}}; | ||
} | ||
|
||
|
||
#[doc(hidden)] | ||
pub fn private_level_filter_to_levels( | ||
filter: log::LevelFilter, | ||
|
@@ -68,16 +85,33 @@ pub struct QueryLogger<'q> { | |
rows_affected: u64, | ||
start: Instant, | ||
settings: LogSettings, | ||
pub span: tracing::Span, | ||
} | ||
|
||
impl<'q> QueryLogger<'q> { | ||
pub fn new(sql: &'q str, settings: LogSettings) -> Self { | ||
pub fn new(sql: &'q str, db_system: &'q str, settings: LogSettings) -> Self { | ||
use tracing::field::Empty; | ||
|
||
let level = private_level_filter_to_trace_level(settings.tracing_span_level()).unwrap_or(tracing::Level::TRACE); | ||
|
||
let span = private_tracing_dynamic_span!( | ||
target: "sqlx::query", | ||
level, | ||
"sqlx::query", | ||
summary = Empty, | ||
db.statement = Empty, | ||
db.system = db_system, | ||
rows_affected = Empty, | ||
rows_returned = Empty, | ||
); | ||
|
||
Self { | ||
sql, | ||
rows_returned: 0, | ||
rows_affected: 0, | ||
start: Instant::now(), | ||
settings, | ||
span, | ||
} | ||
} | ||
|
||
|
@@ -100,6 +134,7 @@ impl<'q> QueryLogger<'q> { | |
self.settings.statements_level | ||
}; | ||
|
||
// only create an event if the level filter is set | ||
if let Some((tracing_level, log_level)) = private_level_filter_to_levels(lvl) { | ||
// The enabled level could be set from either tracing world or log world, so check both | ||
// to see if logging should be enabled for our level | ||
|
@@ -122,37 +157,35 @@ impl<'q> QueryLogger<'q> { | |
String::new() | ||
}; | ||
|
||
if was_slow { | ||
self.span.record("summary", summary); | ||
self.span.record("db.statement", sql); | ||
self.span.record("rows_affected", self.rows_affected); | ||
self.span.record("rows_returned", self.rows_returned); | ||
|
||
let _e = self.span.enter(); | ||
if was_slow { | ||
private_tracing_dynamic_event!( | ||
target: "sqlx::query", | ||
tracing_level, | ||
summary, | ||
db.statement = sql, | ||
rows_affected = self.rows_affected, | ||
rows_returned = self.rows_returned, | ||
// When logging to JSON, one can trigger alerts from the presence of this field. | ||
slow_threshold=?self.settings.slow_statements_duration, | ||
// Human-friendly - includes units (usually ms). Also kept for backward compatibility | ||
?elapsed, | ||
// Search friendly - numeric | ||
elapsed_secs = elapsed.as_secs_f64(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good ole' Hyrum's Law https://www.hyrumslaw.com/ Strictly speaking, we never made the exact format of log messages part of our API contract, but I'd also be pretty annoyed if this changed out from under me. It'd also make me a hypocrite, given how the query macros came about. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was hoping this would be rolled into the breaking-changes-allowed 8.x release, as it's a little superfluous to track timings in addition to the tracing span's own timing, but I certainly see how it's useful. Restored and rebased. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. A standards based approach would certainly be better in the long run. I have my own logging breaking change pr open - but its much smaller in scope and at @abonander's suggestion we tagged a few users who were interested to see if that break will affect them, as an alternative to maintaining two outputs. The break that was in this PR (removing the elapses) would affect people. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, to be perfectly honest, this PR required more thinking than I wanted to do while I was trying to wrap up 0.8.0. At a certain point, I have to stop myself from looking at new PRs, or else I'll sit there forever trying to get every one of them merged before I kick out a release. If we're happy calling this a breaking change, we can plan to land this for 0.9.0. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Completely understand; hope you didn't take my comment as a criticism. Currently re-working this to not be a breaking change; the tracing span should be opt-in if it lands in 0.8, and whether or not that would change in 0.9 is an open question. |
||
// When logging to JSON, one can trigger alerts from the presence of this field. | ||
slow_threshold=?self.settings.slow_statements_duration, | ||
// Make sure to use "slow" in the message as that's likely | ||
// what people will grep for. | ||
"slow statement: execution time exceeded alert threshold" | ||
); | ||
) | ||
} else { | ||
private_tracing_dynamic_event!( | ||
target: "sqlx::query", | ||
tracing_level, | ||
summary, | ||
db.statement = sql, | ||
rows_affected = self.rows_affected, | ||
rows_returned = self.rows_returned, | ||
// Human-friendly - includes units (usually ms). Also kept for backward compatibility | ||
?elapsed, | ||
// Search friendly - numeric | ||
elapsed_secs = elapsed.as_secs_f64(), | ||
); | ||
) | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something else to keep in mind, and may need adjustment here, is that logging the query isn't free.
Its not uncommon for
select
statements to exceed 25KB or 50KB or more, and 100% logging of those will have a materially negative impact on both the running application outputting the logs, and the log observer attempting to capture them.This is why the current implementation only logs the query when the query is slow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I'm taking that into account. The revised implementation will allow the span to be leveled individually, and the query will only be recorded if any of the events or spans are within the configured level.