os_log doesn't log file/line to Xcode 15 console for C++ code

Since this api requires &__dso_handle instead of the standard file/line/func, I had to modify my entire log system to pass this down from the macro call sites.

I have many callbacks that typically just forward data from other C++ libraries that never supply a dso_handle. so it's great how this logging system breaks most logger systems and doesn't have a warning level to match fault/error. I have the forwarded threadName, timestamp, etc and no where to store that in os_log. syslog was more powerful and clear than os_log, but I'm sure it's now too late to head down a more reasonable path..

So I pass the &__dso_handle all the way to the log command and hand it into the macro

#define my_os_log_with_type(dso, log, type, format, ...) __extension__({ \
    os_log_t _log_tmp = (log); \
    os_log_type_t _type_tmp = (type); \
    if (os_log_type_enabled(_log_tmp, _type_tmp)) { \
        OS_LOG_CALL_WITH_FORMAT(_os_log_impl, \
                ((void*)dso, _log_tmp, _type_tmp), format, ##__VA_ARGS__); \
    } \
})

Logger.mm

// This doesn't work, logging the dso from the callsite.   No file/line.
my_os_log_with_type(entry.dso, os_log_create( "com.foo", entry.tag), logLevel(entry.level)), "%{public}s", text );

// This does work, but who wants to jump to the forwarding log implementation?
os_log_with_type(os_log_create( "com.foo", entry.tag), logLevel(entry.level)), "%{public}s", text );

Replies

Just to clarify the os_log_with_type works from a .cpp, .m, or. mm file. But if you try to pass a dso handle from a .m, .mm, or .cpp file that is not Logger.cpp or Logger.mm calling my_os_log_with_type, then that &__dso_handle has some sort of protection that doesn't generate any file/line in the new Console.

I’m confused by your goal here. Standard practice when wrapping the system log API in C-based languages is to create a macro that wraps os_log_with_type or one of the other macros that doesn’t have a dso parameter. That way you’re not on the hook for coming up with that value.

Consider this code:

#include <stdio.h>
#include <os/log.h>

#define my_os_log_with_type(log, type, format, ...) __extension__({ \
    os_log_with_type(log, type, format, ##__VA_ARGS__); \
})

int main(int argc, const char * argv[]) {
    printf("before\n");
    my_os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_DEFAULT, "Hello %{public}s World!", "Cruel");
    printf("after\n");
    return 0;
}

I compiled this with Xcode 15.0 and ran it on macOS 13.5.2. Here’s what I saw in the log:

% log stream --source --predicate "eventMessage contains 'Hello'"
Filtering the log data using "composedMessage CONTAINS "Hello""
… PID    TTL  
… 40710  0    <Test737886`main (main.cpp:11)> Test737886: Hello Cruel World!
^C

As you can see, the line number comes from just fine [1]. This is true regardless of whether I compile the code as C or C++.

Share and Enjoy

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

[1] Well, it’s off by one, but isn’t everything in computing (-:

Hey Quinn,

That’s not the way apps/reality works. Okay now my log is 4K characters, and os_log truncates at 1k. Or I need to route errors/warnings to a FILE*. Or I need to filter out specific messages. Or I use fmod or any number of commercial game libraries, and they just supply a callback hook with level/file/line/func/message. Or I need to print thread name and suppress the os_log output to console that only has TID:PID both of which are not human friendly.

We can’t just inject os log macros throughout all this code. Every system already has its own logging system and os_log thwarts routing logs. os_log even reports the call location and timestamp which isn’t passed down.

See android_log_message for a way easier and more flexible log call. A single code point under a mutex can hand it level, group, file, line, message. We’ll just stick with printf, but is there someway we can feed the console all the log fields we have without going through the severely limited os_log?

Ah, it seems like you’re not looking for solutions based on Apple’s existing system log infrastructure but rather for changes to infrastructure. In that case, I encourage you to file one or more enhancement requests describing your requirements.

Please post the numbers of any bugs you file, just for the record.

Share and Enjoy

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

Not exactly. I thought you might know of a new api to talk to the Xcode console.

I’m just trying to pass a dso handle from existing log macros to a central existing C++ logger and get the console to print file/line. Seems simple enough if these calls don’t take file/line/func explicitly. Apparently these calls use the return address to find the caller, and don’t allow that to be passed down.

That’s why the calls have to be injected at the call site. I’m not sure what the dso_handle is for, but must be to quick filter out libraries that Apple wants to hide logs from. It’s just a mach_header which is only .o file and not line specific.

This log system was always built for Apple to use internally, but pretty much impractical to use by anyone else. But it’s all I have access to. But I’ll file something.

Also finding that our ancient syslog calls are just going out to the console even without an openlog call. And there's no API to test if syslog is open or not. So I'll remove that code too, and just go to printf. But I'd like to use os_log solely for the file/line jump. But I need to be able to call __builtin_return_address() and hand that (and the dso_handle?) or file/line/function to the logging system. Being unable to send all logs to a central point in my game code, and do filtering, and locking a mutex, and breaking up multiple os_logs, and supplementing additional data that each macro doesn't is important. I can't do this in Swift, ObjC, or C++. There is no viable log call using NSLog, syslog, and os_log_impl since these all appear to call __builtin_return_address(0) internally. So that forces these Apple specific calls to be injected into all sorts of code that doesn't need that to get the correct file/line.

  • This is also a problem. os/log.h needs a warning level just like the compiler and all standard logging systems. No matter how much os_log wants to argue to not have one. Having to translate to default, error, fault is not good.

    Logger.warning() This method is functionally equivalent to the error(_:) method.
Add a Comment