slog icon indicating copy to clipboard operation
slog copied to clipboard

Features for v3

Open Diggsey opened this issue 3 years ago • 6 comments

Problems

Mutable scopes

Typically applications are structured such that there is some scope (eg. a request, a transaction, etc.) in which we want to add some log keys.

However, it is not always the case that these keys are known at the start of the scope (for example, once we have authenticated a request, we want to include the user ID in further logs, but we don't know that at the outset). To solve this, adding keys should be decoupled from beginning a scope.

Boilerplate / interoperability

The "scope" information is not only useful for slog: that information should also be accessible to an applications error-reporting solution, to telemetry systems (eg. statsd) and other tracing systems.

At the moment, this involves duplicating a lot of information as each system wants to own the way these scopes are managed. Pulling keys back out from slog is very painful.

Evaluated keys

The PushFnValue and FnValue types are very useful, but they are not particularly efficient: lets say I have a scoped-thread-local called TracingCtx with a bunch of fields, and I want to add these fields to log entries. I have to have a separate PushFnValue for each field, meaning I have to access the thread-local multiple times per log entry. It would be much more efficient if I could access the thread-local once and emit a sequence of fields.

Solution

Instead of converting everything to key-value pairs up-front, integrate with serde. Usage would look something like this:

#[derive(Serialize, Debug)]
struct MyRequestCtx {
    url: String,
    method: Method,
    user_id: Option<Uuid>,
}

// Using `scoped_tls_hkt` which allows defining mutable scoped thread locals
scoped_thread_local!(static mut REQUEST_CTX: MyRequestCtx);

fn main() {
    ...
    // This supercedes `slog_scope`.
    // Whenever something is logged, the logger will check each context. If that context is currently set,
    // it will be serialized to with serde, and all keys will be prefixed with "request."
    root_logger.add_context("request.", &REQUEST_CTX);
}

fn handle_request(req: &Request) -> Response {
    // This creates the request scope.
    // Within `inner_handle_request`, all logs will automatically have the "request.url" and "request.method"
    // keys attached.
    REQUEST_CTX.set(&mut MyRequestCtx {
        url: req.url(),
        method: req.method(),
        user_id: None,
    }, inner_handle_request)
}

fn inner_handle_request(req: &Request) -> Response {
    ...
    if let Some(user_id) = authenticate(req) {
        // A feature of `scoped-tls-hkt` is that we can mutate the thread-local to add
        // additional context later on:
        REQUEST_CTX.with(|ctx| ctx.user_id = Some(user_id));
        // This information will persist for the remainder of the request.
    }
    ...
}

Diggsey avatar Oct 30 '20 14:10 Diggsey

Is there anything wrong with:

let mut logger = ...;
// ...
logger = logger.new(o!());

?

Kixunil avatar Jan 12 '21 19:01 Kixunil

Seems to me like this should be implementable as a 3rd party library.

dpc avatar Jan 12 '21 19:01 dpc

@Kixunil I'm afraid I don't see how that addresses the same issue.

@dpc Possibly it could be implemented as a whole-sale replacement of slog-scope...

Diggsey avatar Jan 12 '21 19:01 Diggsey

slog-scope is already a 3rd party library, yes.

dpc avatar Jan 12 '21 19:01 dpc

It doesn't have a separate issue tracker though, so this seemed the right place to discuss.

Diggsey avatar Jan 12 '21 19:01 Diggsey

Ah, this is about slog-scope, I thought you wanted to change the keys inside logger. Sorry about confusion.

Kixunil avatar Jan 12 '21 19:01 Kixunil