Why does wrapping os_log cause doubles to not be logged correctly?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
os_log is not just an ordinary variadic logging function. The Swift overlay relies on compiler support and strict format-string handling, so when you wrap it in your own generic variadic function, floating-point values such as Double can be encoded incorrectly or lose the special handling that direct os_log calls receive.
Why Direct os_log Calls Behave Differently
When you call os_log directly, Swift can inspect the format string and the arguments at compile time. That gives the unified logging system extra type information, privacy annotations, and optimized encoding behavior.
A wrapper often looks harmless:
But this wrapper changes the call shape. The compiler no longer sees the original os_log invocation in the same direct way, so the special formatting path becomes less reliable.
Why Double Is Often Where It Breaks
Floating-point arguments are more sensitive than simple integers because C variadic calls involve ABI rules and type promotion details. A wrapper that forwards CVarArg values can compile while still producing wrong output for certain types.
That is why you may see integers log correctly while Double values come out as zero, garbage, or some other unexpected value.
The deeper point is that os_log is designed to be called directly with a static format string. It is not a perfect fit for arbitrary variadic forwarding.
Safer Approaches
The safest fix is not to wrap the old os_log formatting API generically. Either:
- call
os_logdirectly at the call site - wrap a preformatted string instead of forwarding arbitrary format arguments
- use the newer
LoggerAPI on supported Apple platforms
A direct call keeps the compiler-aware path intact:
On newer systems, Logger is usually the better API:
That avoids the fragile varargs forwarding pattern entirely.
If You Need a Wrapper Anyway
If you must centralize logging behavior, wrap higher-level messages rather than the raw varargs API. For example:
This loses some of the structured format-string benefits of direct static logging, but it is far safer than trying to recreate os_log internals in a helper.
Modern Guidance
On current Apple platforms, Logger is the path worth favoring for new Swift code. It integrates better with interpolation, privacy control, and modern Swift syntax than the older C-style os_log formatting calls.
If you remain on the older API for deployment-target reasons, keep wrappers minimal and avoid generic forwarding of %f, %d, and mixed-format varargs unless you have tested every case carefully.
Common Pitfalls
The biggest mistake is assuming os_log behaves like printf plus a convenience wrapper. It has extra compiler-aware behavior that a normal helper function does not preserve.
Another issue is using a dynamic format string. os_log expects a static format string for safety and performance reasons.
Developers also often try to forward [CVarArg] or va_list as if the logging system were type-agnostic. That can break structured logging semantics even when the code compiles.
Finally, if you only need centralized logging and not the old format-string API, do not fight os_log. Use Logger or a wrapper around plain strings instead.
Summary
- Direct
os_logcalls get compiler-aware handling that wrappers can lose. - '
Doublevalues often expose the problem because varargs forwarding is type-sensitive.' - Generic wrappers around old
os_logformatting are fragile. - Prefer direct
os_logcalls or the newerLoggerAPI. - If you wrap logging, wrap final messages rather than raw C-style varargs.

