Using Os_Log to Log Function Arguments, or Other Dynamic Data

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 to disable 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



Leave a reply



Submit