Skip to content

Commit

Permalink
Fix {tid} incorrectly outputting TID of thread pool in AsyncPoolSink
Browse files Browse the repository at this point in the history
  • Loading branch information
SpriteOvO committed Jul 7, 2023
1 parent 83f1f5b commit f782e6b
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 24 deletions.
2 changes: 2 additions & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# TODO: Remove this in the next minor version (After `RecordOwned` is boxed in `SendToChannelErrorDropped`)
large-error-threshold = 136
1 change: 1 addition & 0 deletions spdlog/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ pub enum SendToChannelError {
#[non_exhaustive]
pub enum SendToChannelErrorDropped {
/// A `log` operation and a record are dropped.
// TODO: Box the `RecordOwned` in the next minor version, as it's a bit large.
Record(RecordOwned),
/// A `flush` operation is dropped.
Flush,
Expand Down
3 changes: 3 additions & 0 deletions spdlog/src/formatter/pattern_formatter/pattern/process_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ impl Pattern for ProcessId {
}
}

// TODO: We can cache the PID someway to improve the performance, but remember
// to test the case of process forking.

#[cfg(target_family = "unix")]
#[must_use]
fn get_current_process_id() -> u64 {
Expand Down
26 changes: 2 additions & 24 deletions spdlog/src/formatter/pattern_formatter/pattern/thread_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,10 @@ pub struct ThreadId;
impl Pattern for ThreadId {
fn format(
&self,
_record: &Record,
record: &Record,
dest: &mut StringBuf,
_ctx: &mut PatternContext,
) -> crate::Result<()> {
let tid = get_current_thread_id();
write!(dest, "{}", tid).map_err(Error::FormatRecord)
write!(dest, "{}", record.tid()).map_err(Error::FormatRecord)
}
}

#[cfg(target_os = "linux")]
#[must_use]
fn get_current_thread_id() -> u64 {
let tid = unsafe { libc::gettid() };
tid as u64
}

#[cfg(target_os = "macos")]
#[must_use]
fn get_current_thread_id() -> u64 {
let tid = unsafe { libc::pthread_self() };
tid as u64
}

#[cfg(target_os = "windows")]
#[must_use]
fn get_current_thread_id() -> u64 {
let tid = unsafe { winapi::um::processthreadsapi::GetCurrentThreadId() };
tid as u64
}
42 changes: 42 additions & 0 deletions spdlog/src/record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::{
borrow::{Borrow, Cow},
cell::RefCell,
time::SystemTime,
};

Expand Down Expand Up @@ -33,6 +34,7 @@ struct RecordInner {
level: Level,
source_location: Option<SourceLocation>,
time: SystemTime,
tid: u64,
}

impl<'a> Record<'a> {
Expand All @@ -51,6 +53,7 @@ impl<'a> Record<'a> {
level,
source_location: None,
time: SystemTime::now(),
tid: get_current_tid(),
}),
}
}
Expand Down Expand Up @@ -106,6 +109,13 @@ impl<'a> Record<'a> {
self.inner.time
}

/// Gets the TID when the record was created.
// TODO: Public this new method to users in the next minor version
#[must_use]
pub(crate) fn tid(&self) -> u64 {
self.inner.tid
}

// When adding more getters, also add to `RecordOwned`

#[cfg(feature = "log")]
Expand All @@ -127,6 +137,9 @@ impl<'a> Record<'a> {
level: record.level().into(),
source_location: SourceLocation::from_log_crate_record(record),
time,
// For records from `log` crate, they never seem to come from different threads, so
// getting the current TID here should be correct
tid: get_current_tid(),
}),
}
}
Expand Down Expand Up @@ -243,3 +256,32 @@ impl<'a> RecordBuilder<'a> {
self.record
}
}

fn get_current_tid() -> u64 {
#[cfg(target_os = "linux")]
#[must_use]
fn get_current_tid_inner() -> u64 {
let tid = unsafe { libc::gettid() };
tid as u64
}

#[cfg(target_os = "macos")]
#[must_use]
fn get_current_tid_inner() -> u64 {
let tid = unsafe { libc::pthread_self() };
tid as u64
}

#[cfg(target_os = "windows")]
#[must_use]
fn get_current_tid_inner() -> u64 {
let tid = unsafe { winapi::um::processthreadsapi::GetCurrentThreadId() };
tid as u64
}

thread_local! {
static TID: RefCell<Option<u64>> = RefCell::new(None);
}

TID.with(|tid| *tid.borrow_mut().get_or_insert_with(get_current_tid_inner))
}
39 changes: 39 additions & 0 deletions spdlog/tests/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,3 +430,42 @@ fn test_builtin_patterns() {
check(pattern!("{tid}"), None as Option<&str>, vec![OS_ID_RANGE]);
check(pattern!("{eol}"), Some("{eol}"), vec![]);
}

#[cfg(feature = "multi-thread")]
#[test]
fn test_different_context() {
use std::time::Duration;

use spdlog::{sink::AsyncPoolSink, ThreadPool};

// TODO: test process forking for `{pid}`

let formatter = Box::new(PatternFormatter::new(pattern!("{tid} | {pid}{eol}")));
let thread_pool = Arc::new(ThreadPool::builder().build().unwrap());
let buffer_sink = Arc::new(
WriteSink::builder()
.formatter(formatter)
.target(Vec::new())
.build()
.unwrap(),
);
let sinks: [Arc<dyn Sink>; 2] = [
buffer_sink.clone(),
Arc::new(
AsyncPoolSink::builder()
.sink(buffer_sink.clone())
.thread_pool(thread_pool)
.build()
.unwrap(),
),
];
let logger = Arc::new(Logger::builder().sinks(sinks).build().unwrap());

info!(logger: logger, "");
std::thread::sleep(Duration::from_millis(200));

let buffer = String::from_utf8(buffer_sink.clone_target()).unwrap();
let buffer = buffer.lines().collect::<Vec<_>>();
assert_eq!(buffer.len(), 2);
buffer.windows(2).for_each(|w| assert_eq!(w[0], w[1]))
}

0 comments on commit f782e6b

Please sign in to comment.