I got this Kemal app where I have a User class with an associated User::Log. I then have a log backend that routes the log entries to appropriate files by inspecting the log context. Works marvelously.
Now, I have some Kemal endpoints where I would like to capture whatever goes into the log while processing that request.
First thought was making a memory based log sink backend one could request the log entries from, but then we have to garbage collect log entries all the time, so that doesn’t seem so elegant.
Then I thought of a backend that’s an observable. When it doesn’t have any observers, it just discards everything, else it passes log entries on to it’s observers. I could then wrap the observer setup in a nice method and end up with something like:
log = capture_log do
# stuff that logs
end
Then I got nervous when I remembered that requests are served by their own fiber, and what happens when the backend instantiated in the main fiber gets observed by an object that’s scoped in a fiber (I guess it’s all right, as long as capture_log ensures it’s un-observed before it returns?)? I’ll admit that I’m still squinting very hard when thinking about how fibers work.
But maybe there’s an elegant trick I haven’t discovered?
What you’re basically describing is Log.capture. However the catch is it’s primarily intended as a spec helper. So would either have to require spec code into your production build, or duplicate its source to your own method.
Tho I guess I gotta ask, what’s the use case for this? Normally logs aren’t something your application really needs to be aware of?
Well, forgot to explicitly state that the capturing backend would be in addition to the normal backend.
For the nitty gritty: I have a Kemal app that runs things in the background for the user, which logs thing, obviously. But the user can also explicitly trigger a run, and in that case I’m giving them the log of what happened (more user-friendly than “Did stuff, check the logs for status”).
Right now I’m doing it by having the worker both log and return the same strings logged as a result, but this doesn’t play well with exception handling, and just feels a bit dumb.
Yeah, I skipped over that, but on closer inspection it doesn’t allow one to just get the log entries. It does serve as a nice example, but I’m a bit iffy about using an undocumented method (Builder#unbind)
Assuming the logs are sent to the client after it’s fully done processing (not in real time), one option could be to 303 See Other - HTTP | MDN to the log endpoint for that job? This way user would just be taken to the logs after their job finishes and you wont have to have special logic just for this one context?