WebAssembly / wasi-logging

WASI logging API
19 stars 5 forks source link

Support log listener #17

Open oovm opened 6 months ago

oovm commented 6 months ago

Motivation

Consider this scenario, for a pure wasi application, if I want to output logs to the terminal, file, and remote statistician at the same time, how should I implement it?

Solution

Use the log function to send logging events to the host.

Add a log-listener resource type to accept logging events from the host. ​

resource log-listener {
    /// Get or register a listener
    static register: func(id: string) -> log-listener
    /// Remove a listener, regardless of whether it exists
    static unregister: func(id: string)
    /// Set or override the behavior after receiving the log
    /// need async in wasi 0.3.0
    on-receive: func(message: pollable)
}

With this API, users can dynamically change the specific behavior of log during the initialization phase of the program or during runtime.

Very complex combinational logic can be implemented

sunfishcode commented 6 months ago

Currently, the way to implement something like this is to export the logging interface: define a function named log, which can inspect the context argument to dynamically decide what to do, and then import terminal, file, or other APIs to log the output to. A component that does this can then be linked to a component that imports the logging interface.

oovm commented 6 months ago

If used this way, this interface should not exist.


In actual application scenarios, developers of underlying libraries should use a unified log function. You cannot require them to use the my-log function.

The upper-layer application builder may need to read these records and then pretty-print them to the terminal.

sunfishcode commented 6 months ago

I'm not proposing anyone use a my-log function. I'm envisioning underying libraries would use a world like:

world underlying-library {
    import wasi:logging/logging@0.2.0-something;

    export stuff;
}

and log implementations that receive log messages and forward them on to various places could use a world like:

world log-implementation {
    export wasi:logging/logging@0.2.0-something;

    import things;
}

and then you could route the log messages at link time by hooking up log imports to log exports.

If you need dynamic routing, then we'll need to talk about how to fully encapsulate the inner libraries.

That said, the registration interface you sketched above seems to suggest an implied global namespace:

static register: func(id: string) -> log-listener

The component model doesn't have global state, so this namespace would also end up having to be associated with a particular instance, which would also oblige us to do fully encapsulated linking.

oovm commented 6 months ago

This is reasonable, I agree with this design

I want to think about what kind of callback interface should be given after the 0.3.0 release. Although requests such as printing to terminals are synchronized. But consider extreme situations, if you want to send to remote log storage, this is an asynchronous action.

I decided to wait for the asynchronous solution to finally stabilize

oovm commented 6 months ago

Maybe it's not that complicated, the log back no need to know whether it is successful, it is just the simplest notification behavior

Discard or insert the database is the freedom of the receiving end