# Swift-log API add async await to avoid problems in async code

**URL:** <https://forums.swift.org/t/swift-log-api-add-async-await-to-avoid-problems-in-async-code/62913>\
**Category:** Pitches\
**Tags:** swift-log\
**Created:** [February 2, 2023, 3:43pm UTC](https://forums.swift.org/t/swift-log-api-add-async-await-to-avoid-problems-in-async-code/62913 "2023-02-02T15:43:55Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![doozMen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/doozmen/32/7803_2.png) [@doozMen](https://forums.swift.org/u/doozMen)\
**Post date:** [February 2, 2023, 3:43pm UTC](https://forums.swift.org/t/swift-log-api-add-async-await-to-avoid-problems-in-async-code/62913/1 "2023-02-02T15:43:55Z")

</div>

I recently wrote a cli tool using `AsyncParsableCommand` everywhere and [sushichop/Puppy: A flexible logging library written in Swift](https://github.com/sushichop/Puppy) as the logger. I noticed sometimes my last log was not running. This was due to puppy that uses `Dispatch` to dispatch to another queue.

I have not considered performance but to me it seams logical that a logger should be allowed to run some code async? But maybe this is not the case?

If my assumption that a logger should be able to run async is correct I propose to add a function to the `LogHandler` protocol.

```swift
func logAsync(level: Logger.Level,
             message: Logger.Message,
             metadata: Logger.Metadata?,
             source: String,
             file: String,
             function: String,
             line: UInt) async

```

Like I have done in fork [Release 1.5.3 - Add async log · doozMen/swift-log](https://github.com/doozMen/swift-log/releases/tag/1.5.3)

I tested it in integration of this in Puppy [Release 0.7.0 - use async await · doozMen/Puppy](https://github.com/doozMen/Puppy/releases/tag/0.7.0)

Please let me know what you think and if this addition is something to persue?

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [February 8, 2023, 11:26am UTC](https://forums.swift.org/t/swift-log-api-add-async-await-to-avoid-problems-in-async-code/62913/2 "2023-02-08T11:26:37Z")

</div>

Hi @doozMen, I've not looked deeper into the linked issue however the topic of an `async` log we have a well formed opinion about: it's not a good choice to make `log` async.

Async loggers generally should _offload_ the "long work" _away_ from the logged at location. Usually this ends up as "package up the message into a value, and enqueue it somewhere", to then be consumed and emitted by another thread. This can be made very efficient using lock-free queues etc.

One of the better examples of a good async logger is log4j's AsyncAppender: [Log4j – Log4j 2 Lock-free Asynchronous Loggers for Low-Latency Logging](https://logging.apache.org/log4j/2.x/manual/async.html) Note what the benefits of such logging backend are: less impact on the application by lowering latency and by batching writes improving the overall throughput of such logger backend. This isn't as much about making the logging calls "async", but leveraging the concurrency of emitting log statements reap those benefits.

The fact that you'd be asking for an `log(...) async` signals to me that you'd be doing the asynchronous work (which I suspect ends up as network or file IO), _on_ the calling task/thread. This is not what logger should be doing as you'd greatly impact the performance profile of the (potentially hot) code doing the logging.

I don't think adding async versions to the swift-log API would be the right thing to do, and we should probably have a look why you ended up asking for this -- it could point at a problem in Puppy.

I'll read the issue you linked to later in more detail, but from a quick skim it seems you've encountered the `main()` function exiting and you "losing" a log statement? That is expected to be honest, and you should structure your application to not exit "until it is safe to do so" so if your intent is to always flush all messages, there should be some form of `await puppy.gracefulShutdown()` or similar.

We are going to provide more guidance about such application lifecycle patterns -- and @FranzBusch has been working on a document leading towards such guidance, but that's the gist of it. I'm also going to be looking at `async main` semantics, but I don't think any of those change the async log story.

Hope this helps!

---

<div class="post-metadata">

**Author:** ![doozMen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/doozmen/32/7803_2.png) [@doozMen](https://forums.swift.org/u/doozMen)\
**Post date:** [February 8, 2023, 2:36pm UTC](https://forums.swift.org/t/swift-log-api-add-async-await-to-avoid-problems-in-async-code/62913/3 "2023-02-08T14:36:46Z")

</div>

Thanks for your reply. I maybe clarify a bit as the problem relates to another issue about ordering of tasks as mentions in other thread [Task: Is order of task execution deterministic?](https://forums.swift.org/t/task-is-order-of-task-execution-deterministic/51553)

My issue was that the use of `Dispatchqueue.global.async { }` was hidden inside the log function.This made the logs as you describe offload the work to queue but puppy lacs to my understanding the `await puppy.gracefulShutdown()` which seams to me to be the underlying issue.

As for my question to have an async function to log. This was me looking for a way to express, like you describe, to `send but don't wait in the current context`. As I understand now this does not seam possible with the current async await.

What I like about async await is that it lets you express that there is async work going on in a function and that this cannot be hidden.

So the way I end up using the logs in my code is using `async let` but this becomes very verbose.

```swift
#!/usr/bin/env swift
import Foundation

let mainTask = Task {
    let work = Task {
      // do some work
      await logger.logAsync("Log some small amount")
     // continue
    }
    async let workResult: () = work.value
    async let heavyLog: () = log.logAsync("large log ...")

    await _ = [workResult, heavyLog]
}

await mainTask.value
await logger.logAsync("all work including async logs done")

```

So what I'm missing is indeed the `await puppy.gracefulShutdown()` and a way for a function to express that it is doing work that will finish without waiting.

```swift
func log() parallel

```

So unlike `await` you would need to call this function with `queue`

```swift
queue log()

```

Queue would be a special global or something that you could wait for.

```swift
\\ do some other work in main
await queue.finished

```

Which could or could not return a value.

I hope this clarifies my need? I just expressed my first naive intension with async/await. No clue if it is something that is logical to others too.  
But thanks very much for both of your clear explanation of the problem. I think I can find a workaround now for puppy.

---

<div class="post-metadata">

**Author:** ![sushichop](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sushichop/32/13734_2.png) [@sushichop](https://forums.swift.org/u/sushichop)\
**Post date:** [February 10, 2023, 9:46am UTC](https://forums.swift.org/t/swift-log-api-add-async-await-to-avoid-problems-in-async-code/62913/4 "2023-02-10T09:46:07Z")

</div>

@doozMen  
Sorry for the late response. I jumped here from my GitHub repository's [issue](https://github.com/sushichop/Puppy/pull/76)

In short, did you encounter the main function process ending before the logging process was finished?  
TBH, this was expected behavior for me as @ktoso said and I originally thought that the application should handle it😅  
However, it is also true that I did not write this idea in the README...😇

Now, my understanding is that what you are asking for is a way to know that the logging process is complete. For example, you want a method to know about the above notification or to flush the log message, right?  
I have just recently received a similar request, and I will add an optional feature of the library when I have time to spare.

Thanks.

---

<div class="post-metadata">

**Author:** ![doozMen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/doozmen/32/7803_2.png) [@doozMen](https://forums.swift.org/u/doozMen)\
**Post date:** [February 13, 2023, 9:51am UTC](https://forums.swift.org/t/swift-log-api-add-async-await-to-avoid-problems-in-async-code/62913/5 "2023-02-13T09:51:39Z")

</div>

I indeed want a way to know that the logging completed. So in short `await puppy.isDone()` like I did in my PR would be all that is needed.

I will close the [PR](https://github.com/sushichop/Puppy/pull/76) I made and let you handle it then.

I hope @ktoso can convince the swift community of a way to express that a function runs async and ordered but that you do not want to wait for it in the current context.

Until then I work with the workaround [FIFOQueue](https://github.com/dfed/swift-async-queue) that I found on Github. But please @sushichop let me know if you have a solution so I do not have to point my work related code to my personal fork for to long.Thanks

---

<div class="post-metadata">

**Author:** ![sushichop](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sushichop/32/13734_2.png) [@sushichop](https://forums.swift.org/u/sushichop)\
**Post date:** [February 13, 2023, 12:31pm UTC](https://forums.swift.org/t/swift-log-api-add-async-await-to-avoid-problems-in-async-code/62913/6 "2023-02-13T12:31:25Z")

</div>

> But please @sushichop let me know if you have a solution so I do not have to point my work related code to my personal fork for to long.

Sure. I'll do it 🙂
