Using os_log to log function arguments, or other dynamic data
See Logging:
Formatting Log Messages
To format a log message, use a standard NSString or printf format string, ...
and String Format Specifiers for the standard format string specifiers, such as %@
and %d
.
In your case:
os_log("foo: %@ %@", log: .default, type: .debug, x, y.description)
The format string is restricted to static strings to prevent
(unintentional) expansion of format string specifiers. Here is an example demonstrating the
problem, using NSLog()
because that does not restrict the format
to constant strings:
let s = "50%"
NSLog("\(s)percent")
// Output: 500x0ercent
The %p
expects a pointer on the variable argument list, which is
not provided. This is undefined behavior, it can lead to crashes
or unexpected output.
Passing variadic args in Swift 4 for os_log
Did some more research on this. Turns out that os_log
is actually a C macro. This created all sorts of problems with how it maps in to Swifts variadic args.
However, that macro also captures other debugging info and is probably not safe to wrap up anyways.
os_log Debug & Release Builds
No, the logs won't be stripped in Release. The OSLogType
just describes the type of message to filter in Console.app
, debug
type messages will still be logged in production.
The correct way to disable OS logging in a scheme is to edit the Release
scheme itself:
Set the
OS_ACTIVITY_MODE
environment variable todisable
in your
scheme, then you will not see any logs for your app in the console.
This won't work for Archived apps though, but you should never really disable logging in production anyway. If you really want to, it’s possible to use preprocessor directives in this case.
Problems with Unified Logging, StaticString, CustomStringConvertible and description
A string doesn't have to be static, as long as the format includes %{public}@
. Here's a complete example:
import UIKit
import os
let mylog = OSLog(subsystem: "com.neuburg.matt", category: "testing")
class Foo: CustomStringConvertible {
var bar:Int = 1
var description: String {
return "<\(type(of: self)): bar = \(bar)>"
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let foo = Foo()
os_log("%{public}@", log: mylog, String(describing:foo))
}
}
Prints:
[testing] <Foo: bar = 1>
...which I believe was the goal.
swift logging for iOS = 8 with os_log and NSLog
The first argument of both os_log()
and NSLog()
is a format string, and contains
format specifiers (starting with %
) which are expanded by the following variable argument list.
To log an arbitrary string, use the %@
format, followed by the
string. Otherwise it can crash or produce wrong output if the string contains %
characters:
func LogDebug(log: String) {
if #available(iOS 10.0, macOS 10.12, *) {
os_log("%@", type: .debug, log)
} else {
NSLog("%@", log)
}
}
Viewing os_log messages in device console
The "Devices and Simulators" window only shows crash reports. Use the Console app or Terminal, via the log --stream
command, to see live log output.
To see the device's live log messages via the Console app, either when running from Xcode or when running from the device directly:
- Open Console.app.
- Click on the device's name in the left side panel, under "Devices".
- Select Action, then Include Info Messages from the menu. If you are also using
.debug
level messages, make sure to select Include Debug Messages as well. (Without those items selected, the Console displays.default
,.fault
, and.error
level messages only.)
If you still don't see the messages, try entering this Terminal command to configure the logging levels for your app:
sudo log config --subsystem com.test.testapp --mode level:debug
This turns on .debug
-level logging for subsystem "com.test.testapp" (which includes .info
and .default
messages).
If you want to persist the messages, rather than the default of memory-only, turn on persistence for the three levels at the same time, like so:
sudo log config --subsystem com.test.testapp --mode level:debug,persist:debug
Regardless of any log settings, though, only crash reports will appear in the "Devices and Simulators" window.
How to find source file and line number from os_log()
I don't think it's available in Swift yet, although you can see file and line number in C / C++ in Terminal. See Apple forum here.
I tried something similar to what's inside the forum by creating a simple command-line tool Xcode project:
import Foundation
import os.log
os_log("rrrr")
and type the following in Terminal: log stream --source --predicate 'eventMessage contains "rrrr"'
, and I got this:
Timestamp Thread Type Activity PID
2017-02-18 17:58:46.012381+0700 0x5067d Default 0x0 5637 <testLogSwift`_swift_os_log> rrrr
in contrast to what I got in C/C++ version, which shows the file and line number:
Timestamp Thread Type Activity PID
2017-02-18 17:55:05.056103+0700 0x4aa01 Default 0x0 5218 <testLogging`main (main.cpp:13)> qqq
Related Topics
How to Make List with Single Selection with Swiftui
Changing Color of Button Text and State
Swift Programming Nserrorpointer Error etc
Get Header Data from a Request Response in Swift
Swift Lazy Subscript Ignores Filter
Saving Array to Realm in Swift
Changing Value in Nested Dictionary in Swift
Break on Any Occurrence of "Fatal Error: Unexpectedly Found Nil While Unwrapping an Optional Value"
Propertywrappers and Protocol Declaration
How Are Optional Values Implemented in Swift
Strange Error Nw_Protocol_Get_Quic_Image_Block_Invoke Dlopen Libquic Failed
How to Get Date and Time to Show a Clock in Uilabel
How Is a Return Value of Anyobject! Different from Anyobject
How to Add a Double Tap Gesture Recognizer in Swift
Navigationview Doesn't Display Correctly When Using Tabview in Swiftui
How to Read and Write Data to a Text File in Swift Using Playground