Two things here:
Yeah the way to do this is in general:
nonisolated func thing() {
Task { await self._thing() }
... but, you'll lose ordering guarantees (!), which is very very problematic for a logging case like you have, since you could:
log("A")
log("B")
and in theory there's no guarantee that you'll actually handle A and then B... 
So, you have to make a far more advanced dance to achieve what you need here and basically reimplement a mailbox/queue for all those messages, and only consume it from a single task, like so:
actor LogHandler {
let logs: AsyncStream<String>
var logsCC: AsyncStream<String>.Continuation!
var processingTask: Task<Void, Never>? = nil
static func make() {
let cc = AsyncStream<String>.Continuation
let logs: AsyncStream<String> = AsyncStream {
cc = $0 // yes, escape it like this... it's actually fine to do so
}
return Self(logs: logs, cc: cc)
}
private init(logs: AsyncStream<String>, cc: AsyncStream<String>.Continuation) async {
self.logs = logs
self.logsCC = cc
self.processingTask = Task { await self.processing() }
}
// also offer ways to shutdown the processing Task
func processing() {
for await log in logs {
// handle
}
}
nonisolated func log(message: String) {
self.logsCC.yield(message)
}
}
Something like that... You may have to dance around isolation a bit more; but that'll get you the events in send-order into the processing() task. Anything else, that creates a new Task{} for new calls will have undefined ordering...
Relatedly, we also built a similar type to AsyncStream over at swift-nio that is a bit nicer to use (and can handle different backpressure requirements, though here that doesn't matter); See: https://github.com/apple/swift-nio/pull/2230
I personally think we are missing an "enqueue and don't wait" operation for void returning actor methods which would do exactly what you'd want here (like other actor runtimes "tell" or "!").
Though we wanted to spend more time designing it but haven't yet had the time to deep dive into it.
FYI @Douglas_Gregor @Joe_Groff