Skip to main content

AuditSink

Enum AuditSink 

Source
pub enum AuditSink {
    Disabled,
    File {
        path: PathBuf,
        max_bytes: Option<u64>,
        state: Mutex<FileState>,
    },
}
Expand description

Where audit events go.

Disabled unless a path is configured: writing to a file nobody asked for would be a surprising side effect, and the operator is the one who knows where such a file belongs.

Variants§

§

Disabled

Nothing is recorded.

§

File

Appended to a file, one JSON object per line.

Fields

§path: PathBuf

Path being appended to, kept for diagnostics.

§max_bytes: Option<u64>

Size at which the file is rotated, if bounded.

Implementations§

Source§

impl AuditSink

Source

pub fn file(path: impl AsRef<Path>) -> Result<Self>

Open path for appending, creating it if needed.

Unbounded: rotation is file_with_limit, and the binary passes DEFAULT_MAX_BYTES through that. This constructor exists for consumers that manage the file’s size themselves.

Source

pub fn file_with_limit( path: impl AsRef<Path>, max_bytes: Option<u64>, ) -> Result<Self>

Open path, rotating to <path>.1 once it passes max_bytes.

One generation is kept. A trail that grows without bound eventually fills the disk it is meant to protect, and keeping several generations would be a retention policy — which belongs to whoever runs the machine, not to this process.

Source

pub fn is_enabled(&self) -> bool

Whether anything is being recorded.

Source

pub async fn record_async(self: &Arc<Self>, event: AuditEvent)

Record one event from an async context, off the runtime’s workers.

record opens, writes and flushes a file, which is blocking work — the filesystem handlers already thread it into the spawn_blocking bodies they are running in for that reason, so a slow disk cannot starve the worker pool that also runs /health and the accept loop. A handler that has no blocking body of its own has nowhere to put it and used to call record straight from the runtime thread. This is that missing half: same write, same ordering.

What this buys and what it costs were both measured rather than reasoned (tests/blocking_pool.rs). With every blocking thread held, /health answered in 2.5 µs — the worker threads really are untouched. The same run had this method take 2.96 s against 1.57 ms once a thread was free: moving blocking work off the workers does not make it free, it moves it onto a pool that is shared with every command in flight. So a burst of concurrent commands does delay an audited response, and that is a deliberate trade against blocking the accept loop, not an oversight.

Awaited rather than detached, deliberately. Spawning and walking away would return the response first and leave the entry to land whenever — or not at all, if the process stops in between. An audit trail that drops its last entries under load is untrustworthy exactly where it is load-bearing, which is the same reason record flushes per event.

The hop is skipped entirely when nothing is being recorded: with no trail configured record returns immediately, and paying for a task dispatch to do nothing would be a cost on every request of the default configuration.

Source

pub fn record(&self, event: AuditEvent)

Record one event.

Flushed per event rather than buffered until convenient: a trail that loses its last entries when the process dies is least trustworthy exactly when it matters most.

Blocking. From an async context use record_async, or call this inside a spawn_blocking body that is already running.

Trait Implementations§

Source§

impl Debug for AuditSink

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for AuditSink

Source§

fn default() -> AuditSink

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more