-
Notifications
You must be signed in to change notification settings - Fork 292
Log action can distinguish data from flush #619
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
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,8 +7,9 @@ use std::time::{Instant, Duration}; | |
| use std::fmt::{self, Debug}; | ||
| use std::marker::PhantomData; | ||
|
|
||
| use timely_container::{ContainerBuilder, PushInto}; | ||
| use timely_container::{Container, ContainerBuilder, PushInto}; | ||
|
|
||
| /// A registry binding names to typed loggers. | ||
| pub struct Registry { | ||
| /// A map from names to typed loggers. | ||
| map: HashMap<String, (Box<dyn Any>, Box<dyn Flush>)>, | ||
|
|
@@ -28,7 +29,9 @@ impl Registry { | |
| /// seen (likely greater or equal to the timestamp of the last event). The end of a | ||
| /// logging stream is indicated only by dropping the associated action, which can be | ||
| /// accomplished with `remove` (or a call to insert, though this is not recommended). | ||
| pub fn insert<CB: ContainerBuilder, F: FnMut(&Duration, &mut CB::Container)+'static>( | ||
| /// | ||
| /// Passing a `&mut None` container to an action indicates a flush. | ||
| pub fn insert<CB: ContainerBuilder, F: FnMut(&Duration, &mut Option<CB::Container>)+'static>( | ||
| &mut self, | ||
| name: &str, | ||
| action: F) -> Option<Box<dyn Any>> | ||
|
|
@@ -84,7 +87,7 @@ impl Flush for Registry { | |
|
|
||
| /// A buffering logger. | ||
| pub struct Logger<CB: ContainerBuilder> { | ||
| inner: Rc<RefCell<LoggerInner<CB, dyn FnMut(&Duration, &mut CB::Container)>>>, | ||
| inner: Rc<RefCell<LoggerInner<CB, dyn FnMut(&Duration, &mut Option<CB::Container>)>>>, | ||
| } | ||
|
|
||
| impl<CB: ContainerBuilder> Clone for Logger<CB> { | ||
|
|
@@ -103,32 +106,28 @@ impl<CB: ContainerBuilder + Debug> Debug for Logger<CB> { | |
| } | ||
| } | ||
|
|
||
| struct LoggerInner<CB: ContainerBuilder, A: ?Sized + FnMut(&Duration, &mut CB::Container)> { | ||
| struct LoggerInner<CB: ContainerBuilder, A: ?Sized + FnMut(&Duration, &mut Option<CB::Container>)> { | ||
| /// common instant used for all loggers. | ||
| time: Instant, | ||
| /// offset to allow re-calibration. | ||
| offset: Duration, | ||
| /// container builder to produce buffers of accumulated log events | ||
| builder: CB, | ||
| /// True if we logged an event since the last flush. | ||
| /// Used to avoid sending empty buffers on drop. | ||
| dirty: bool, | ||
| /// action to take on full log buffers. | ||
| /// action to take on full log buffers, or on flush. | ||
| action: A, | ||
| } | ||
|
|
||
| impl<CB: ContainerBuilder> Logger<CB> { | ||
| /// Allocates a new shareable logger bound to a write destination. | ||
| pub fn new<F>(time: Instant, offset: Duration, action: F) -> Self | ||
| where | ||
| F: FnMut(&Duration, &mut CB::Container)+'static | ||
| F: FnMut(&Duration, &mut Option<CB::Container>)+'static | ||
| { | ||
| let inner = LoggerInner { | ||
| time, | ||
| offset, | ||
| action, | ||
| builder: CB::default(), | ||
| dirty: false, | ||
| }; | ||
| let inner = Rc::new(RefCell::new(inner)); | ||
| Logger { inner } | ||
|
|
@@ -234,57 +233,56 @@ impl<CB: ContainerBuilder, T> std::ops::Deref for TypedLogger<CB, T> { | |
| } | ||
| } | ||
|
|
||
| impl<CB: ContainerBuilder, A: ?Sized + FnMut(&Duration, &mut CB::Container)> LoggerInner<CB, A> { | ||
| impl<CB: ContainerBuilder, A: ?Sized + FnMut(&Duration, &mut Option<CB::Container>)> LoggerInner<CB, A> { | ||
| fn log_many<I>(&mut self, events: I) | ||
| where I: IntoIterator, CB: PushInto<(Duration, I::Item)>, | ||
| { | ||
| let elapsed = self.time.elapsed() + self.offset; | ||
| for event in events { | ||
| self.dirty = true; | ||
| self.builder.push_into((elapsed, event.into())); | ||
| while let Some(container) = self.builder.extract() { | ||
| (self.action)(&elapsed, container); | ||
| let mut c = Some(std::mem::take(container)); | ||
| (self.action)(&elapsed, &mut c); | ||
| if let Some(mut c) = c { | ||
| c.clear(); | ||
| *container = c; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn flush(&mut self) { | ||
| let elapsed = self.time.elapsed() + self.offset; | ||
|
|
||
| let mut action_ran = false; | ||
| while let Some(buffer) = self.builder.finish() { | ||
| (self.action)(&elapsed, buffer); | ||
| action_ran = true; | ||
| } | ||
|
|
||
| if !action_ran { | ||
| // Send an empty container to indicate progress. | ||
| (self.action)(&elapsed, &mut CB::Container::default()); | ||
| while let Some(container) = self.builder.finish() { | ||
| let mut c = Some(std::mem::take(container)); | ||
| (self.action)(&elapsed, &mut c); | ||
| if let Some(mut c) = c { | ||
| c.clear(); | ||
| *container = c; | ||
| } | ||
| } | ||
|
|
||
| self.dirty = false; | ||
| // Send no container to indicate flush. | ||
| (self.action)(&elapsed, &mut None); | ||
|
Member
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. A nit, but one of the reasons to take a
Member
Author
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. I see your point, but I'm wondering if this is actually true! Or put differently, is it something we'd like to be true, or is it something that we had at some point and forgot about? The reason I'm asking is because most (all?) places where we call We could change |
||
| } | ||
| } | ||
|
|
||
| /// Flush on the *last* drop of a logger. | ||
| impl<CB: ContainerBuilder, A: ?Sized + FnMut(&Duration, &mut CB::Container)> Drop for LoggerInner<CB, A> { | ||
| impl<CB: ContainerBuilder, A: ?Sized + FnMut(&Duration, &mut Option<CB::Container>)> Drop for LoggerInner<CB, A> { | ||
| fn drop(&mut self) { | ||
| // Avoid sending out empty buffers just because of drops. | ||
| if self.dirty { | ||
| self.flush(); | ||
| } | ||
| self.flush(); | ||
| } | ||
| } | ||
|
|
||
| impl<CB, A: ?Sized + FnMut(&Duration, &mut CB::Container)> Debug for LoggerInner<CB, A> | ||
| impl<CB, A: ?Sized + FnMut(&Duration, &mut Option<CB::Container>)> Debug for LoggerInner<CB, A> | ||
| where | ||
| CB: ContainerBuilder + Debug, | ||
| { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| f.debug_struct("LoggerInner") | ||
| .field("time", &self.time) | ||
| .field("offset", &self.offset) | ||
| .field("dirty", &self.dirty) | ||
| .field("action", &"FnMut") | ||
| .field("builder", &self.builder) | ||
| .finish() | ||
|
|
||
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.
No strong opinion, but is this clearing new behavior? Does it e.g. prevent passing back owned data, into which the logger can write? Again, really no strong opinion, but just checking whether the force-clear is new, and whether it is intentional.
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.
It was purely defensive. Thinking about it, it should be the container builder's responsibility to enforce what needs to be true about a container after extracting or finishing it, so it doesn't make sense to have the
clearcall here.