How to use a freestanding macro with OSLog

I'm trying to create a macro that adds the file and line to a string that I use with OSLog. However, I only get warnings that either the String is not in the format of OSLogMessage or not an interpolated string. The Macro looks like this at the moment:

public struct LocMacro: ExpressionMacro {
    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) -> ExprSyntax {
        guard let argument = node.argumentList.first?.expression.as(StringLiteralExprSyntax.self)?.segments else {
            fatalError("compiler bug: the macro does not have any StringLiteralExprSyntax arguments.")
        }

        guard let file = context.location(of: node)?.file.as(StringLiteralExprSyntax.self)?.segments,
              let line = context.location(of: node)?.line.as(IntegerLiteralExprSyntax.self) else {
            fatalError("compiler bug: the macro is unable to retrieve file and line numbers")
        }

        return "\"\(argument) - \(file):\(line)\""

    }
}

and here the exposed macro:

@freestanding(expression)
public macro loc(_ text: String) -> String = #externalMacro(module: "LxoMacrosMacros", type: "LocMacro")

I want to use it like this:

import LxoMacros
import OSLog

let logger = Logger()

let someNumber = 17

logger.debug(#loc("Working with some number \(someNumber)"))

which should expand to:

logger.debug("Working with some number \(someNumber) - MyFile.swift:8")

Is there a way to change the macro so that it returns the correct type? When expending the macro it does show the right string, which works with print() and so on, but assuming since the Logger uses some sort of compiler check as well, it doesn't seem to work together as expected.

Replies

I'm trying to create a macro that adds the file and line to a string that I use with OSLog.

Why?

The system log records info about the callsite that lets it recover the file and line number. Xcode 15’s shiny new logging support has an easy facility to access that. And the log command has supported this forever via its --source option.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

eskimo you must never have written a real app lol.

You can't wrap the system log at all right now, because if you do the callsite info will be of the code that wrapped the log statement.

Which means in Xcode 15 you have to pick between getting the log integration or a functional logger where you can store things in files so users can send you bug reports with logs from their devices, etc.

The complete oversight in structured logging in Xcode 15 / iOS 17 is baffling. It's like Apple never heard of logging to a file before.