Skip to content

tracing-appender::NonBlocking - add .flush() method to WorkerGuard #3267

@jlizen

Description

@jlizen

Feature Request

(Open to implementing this change, if crate maintainers would accept a contribution)

Crates

tracing-appender

Motivation

NonBlocking allows you to signal a final flush before shutdown. This is done by dropping a WorkerGuard.

I want to explicitly signal flushes throughout my application's life, instead of only on shutdown.

My use case is long running-process (a server) that is sensitive to latency on the request/response path. The whole process sometimes sleeps for longer periods inbetween requests based on an external orchestrator. So, I want to complete the request/response cycle, then tell my worker thread to flush, before my whole process might be put to sleep.

My specific environment (AWS Lambda) works well with that flush pattern (per-request) because it only receives one request, potentially longer-running, at a time. At the same time Rust lambdas generally drive work on a tokio current-thread runtime, which is very sensitive to blocking by in-thread flushes.

Here is a good example of where you might use the proposed feature.

Proposal

Aflush method on the WorkerGuard would be pretty simple and backwards compatible.

Right now, the logging thread listener accepts two commands:

pub(crate) enum Msg {
     // written to by subscriber
    Line(Vec<u8>),
    // written to on WorkerGuard drop
    Shutdown,
}

The worker thread currently handles the Shutdown variant by triggering a flush, and then when it finishes, signals on a 0-sized channel to tell the WorkerGuard that it successfully flushed and exited.

We could add a third Msg variant, Flush, that is written to in a new WorkerGuard::flush method.

The worker would handle this variant by flushing, signaling completion to the 0-sized response channel, and then continuing on its work loop.

There is a bit of awkwardness regarding handling timeouts, etc, which also exists for the shutdown handling. There is a PR open that makes the timeout configurable via NonBlocking builder. We could apply a similar approach here here (maybe across two PRs).

Alternatives

WorkerGuard::flush_async()

I would love to offer an asynchronous flush API. It's great for a use case like mine, where I am on a current-thread tokio runtime that is sensitive to long future polls. Since, we have some blocking send_with_timeouts in our flush handling that we could sleep on. It also would make timeout handling more elegant in general.

That's a more opinionated design choice though since it would maybe involve introducing new synchronization flows (async channels), feature flags, etc, for the appender.

Some related discussion: #3238

Either way we would would want to expose a sync flush, so better to keep initial scope simpler.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions