Customizing the behavior of print for a daemon

print is not really great for things that run perpetually on a server, because it does not flush the output buffer on newlines when running as a daemon. this causes logging output to lag or be truncated entirely.

i have tried to sort-of work around the issue by shadowing print with a custom implementation:

#if canImport(Glibc)
import Glibc
#elseif canImport(Darwin)
import Darwin
#endif

func print(_ items:Any..., separator:String = " ", terminator:String = "\n")
{
    for item:Any in items
    {
        Swift.print(item, separator: "", terminator: separator)
    }

    Swift.print(terminator, terminator: "")

    if  terminator.contains(where: \.isNewline)
    {
        fflush(stdout)
    }
}

but this runs afoul of concurrency checking apparently.

reference to var 'stdout' is not concurrency-safe because it involves shared mutable state; this is an error in Swift 6

how should i be flushing the output buffer of print?

1 Like

There's no real good way to handle this today.

You might be able to get rid of the warnings by making an unsafe wrapper around the pointer and making it unchecked sendable and put operations on it into that unchecked type?

Probably the "right" long term thing here would be for Swift to offer its own stdin/out types and have them do the safe wrapping...? It's a bit in the realm of swift-system so maybe it's a question to ask specifically of that package

I think itโ€™s reasonable to assume that print is layered on top of C stdio library, in which case you can achieve this goal by calling setlinebuf on stdin.

Konrad 'ktoso' Malawski wrote:

Probably the "right" long term thing here would be for Swift
to offer its own stdin/out types

Thatโ€™d be lovely.

Share and Enjoy

Quinn โ€œThe Eskimo!โ€ @ DTS @ Apple

5 Likes

@unchecked Sendable had no impact on the warning.

#if canImport(Glibc)
import Glibc
#elseif canImport(Darwin)
import Darwin
#endif

struct StandardOutput:@unchecked Sendable
{
    var file:UnsafeMutablePointer<FILE> { stdout }
//  reference to var 'stdout' is not concurrency-safe because it involves shared mutable state; this is an error in Swift 6
}

I believe this is why swift-log was created. It was actually the very first SSWG proposal (SSWG-0001).

I would expect it to be more suitable than print for industrial-strength logging. Have you tried it?