Logging

Efficiently capture log messages to memory and disk. Manage logging behavior and persistence.

Overview

The unified logging system provides a single, efficient, performant API for capturing messaging across all levels of the system. This unified system centralizes the storage of log data in memory and in a data store on disk. The system implements global settings that govern logging behavior and persistence, while at the same time providing fine-grained control during debugging via the log command-line tool and through the use of custom logging configuration profiles. Log messages are viewed using the Console app in /Applications/Utilities/ and the log command-line tool. Logging and activity tracing are integrated to make problem diagnosis easier. If activity tracing is used while logging, related messages are automatically correlated.

Log Levels

There are several log levels employed by the unified logging system, which correspond to the different types of messages your app may need to capture, and define when messages are saved to the data store and how long they persist. The system implements standard behavior for each level. This behavior can be overridden using the log command-line tool or a custom configuration profile (see Customizing Logging Behavior While Debugging).

Default

Default-level messages are initially stored in memory buffers. Without a configuration change, they are compressed and moved to the data store as memory buffers fill. They remain there until a storage quota is exceeded, at which point, the oldest messages are purged. Use this level to capture information about things that might result a failure.

Info

Info-level messages are initially stored in memory buffers. Without a configuration change, they are not moved to the data store and are purged as memory buffers fill. They are, however, captured in the data store when faults and, optionally, errors occur. When info-level messages are added to the data store, they remain there until a storage quota is exceeded, at which point, the oldest messages are purged. Use this level to capture information that may be helpful, but isn’t essential, for troubleshooting errors.

Debug

Debug-level messages are only captured in memory when debug logging is enabled through a configuration change. They’re purged in accordance with the configuration’s persistence setting. Messages logged at this level contain information that may be useful during development or while troubleshooting a specific problem. Debug logging is intended for use in a development environment and not in shipping software.

Error

Error-level messages are always saved in the data store. They remain there until a storage quota is exceeded, at which point, the oldest messages are purged. Error-level messages are intended for reporting process-level errors. If an activity object exists, logging at this level captures information for the entire process chain.

Fault

Fault-level messages are always saved in the data store. They remain there until a storage quota is exceeded, at which point, the oldest messages are purged. Fault-level messages are intended for capturing system-level or multi-process errors only. If an activity object exists, logging at this level captures information for the entire process chain.

Performing Logging

To send a message to the logging system, call an os_log function corresponding to the desired log level. Table 1 shows the main os_log functions. Provide a log object—the OS_LOG_DEFAULT constant or a custom log object created by calling the os_log_create function—and a constant string or format string representing the message. The OS_LOG_DEFAULT constant causes logging to occur in accordance with the system’s standard behavior. A custom log object causes logging to occur according to settings contained within a logging profile for a specific subsystem. Calling an os_log function doesn’t always guarantee that a message is captured. This is governed by the current logging configuration for the level of message being sent.

Table 1

Primary Logging functions

Function

Description

os_log

Sends a default-level message to the logging system. See Listing 1.

os_log_info

Sends an info-level message to the logging system. See Listing 2.

os_log_debug

Sends a debug-level message to the logging system. See Listing 3. Note that debug-level messages are only captured when debug logging has been explicitly enabled via the log command-line tool or a custom logging profile. See Customizing Logging Behavior While Debugging.

os_log_error

Sends an error-level message to the logging system.

os_log_fault

Logs a fault-level message to the logging system.

Listing 1

Logging a default-level message

os_log("This is a log message.")
Listing 2

Logging an info-level message

os_log("This is additional info that may be helpful for troubleshooting.", log: OSLog.default, type: .info)
Listing 3

Logging a debug-level message for a specific subsystem

let customLog = OSLog(subsystem: "com.your_company.your_subsystem_name.plist", category: "your_category_name")
os_log("This is info that may be helpful during development or debugging.", log: customLog, type: .debug)

Messages can also be sent to the logging system by calling the os_log_with_type function and providing a log object, a log type constant (see os_log_type_t), and a message. In Listing 4, the os_log_with_type function sends an info-level message to the logging system.

Listing 4

Logging a message at a specified level

os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_INFO, "This is another way to log an info-level message.");

Viewing Log Messages

Use the Console app or the log command-line tool to view and filter log messages.

Customizing Logging Behavior While Debugging

Logging behavior is normally governed by the system. However, while debugging in macOS, you can enable different logging levels for a subsystem using the log command-line tool’s config argument while logged in as root. See Listing 5, which shows how to enable debug-level logging for a subsystem.

Listing 5

Enabling debug-level logging for a subsystem

$ sudo log config --mode "level:debug" --subsystem com.your_company.your_subsystem_name

Use the log tool’s status argument to check the current logging level of a subsystem. See Listing 6.

Listing 6

Checking the log level of a subsystem

$ sudo log config --status --subsystem --subsystem com.your_company.your_subsystem_name
Mode for 'com.your_company.your_subsystem_name'  DEBUG

You can also override the logging behavior of a specific subsystem by creating and installing a logging configuration profile property list file in the /Library/Preferences/Logging/Subsystems/ directory. Name the file using an identifier string, in reverse DNS notation, representing the subsystem. For example, com.your_company.your_subsystem_name.plist. Next, add one or more settings dictionaries to the top level of the file. A DEFAULT-OPTIONS settings dictionary defines global behavior settings for the entire subsystem. Category settings dictionaries define behavior for specific categories of messages within the subsystem. See Listing 7.

Listing 7

Top level structure of a logging profile

<dict>
    <key>DEFAULT-OPTIONS</key>
    <dict>
       <!-- GLOBAL SUBSYSTEM OR PROCESS SETTINGS -->
    </dict>
    <key>CategoryName</key>
    <dict>
       <!-- CATEGORY SETTINGS -->
    </dict>
</dict>

Each settings dictionary within a logging profile contains a Level subdictionary, which contains the following setting keys:

Key

Description

Enable

Enables a specific log level.

Persist

Controls whether messages are stored in memory and then saved to the data store, or stored in memory only.

The Enable key and Persist key both accept the following string values:

Value

Description

Inherit

Explicitly states that the subsystem or category inherits the behavior of its parent. In the case of a category, the parent is the subsystem. In the case of a subsystem, the parent is the system.

Default

Only default-level messages are captured.

Info

Default-level and info-level messages are captured.

Debug

Default-level, info-level, and debug-level messages are captured.

Listing 8 shows an example of a Level subdictionary that enables info-level logging that inherits the persistence behavior of a subsystem or the system.

Listing 8

Example of a Level subdictionary in a logging profile settings dictionary

<key>Level</key>
<dict>
    <key>Enable</key>
    <string>Info</string>
    <key>Persist</key>
    <string>Inherit</string>
</dict>

Listing 9 shows an example of a complete logging profile, which configures a subsystem to perform info-level logging and a server-connections category within the subsystem to perform debug-level logging.

Listing 9

Example of a complete logging profile

<dict>
    <key>DEFAULT-OPTIONS</key>
    <dict>
        <key>Level</key>
        <dict>
            <key>Enable</key>
            <string>Info</string>
            <key>Persist</key>
            <string>Inherit</string>
        </dict>
    </dict>
    <key>server-connections</key>
    <dict>
        <key>Level</key>
        <dict>
            <key>Enable</key>
            <string>Debug</string>
            <key>Persist</key>
            <string>Inherit</string>
        </dict>
    </dict>
</dict>

Logging Best Practices

Follow these guidelines to produce useful and efficient log messages.

  • Use format strings and specifiers whenever possible to automatically produce user-friendly log messages instead of trying to write custom formatting code. See Formatting Log Messages.

  • Don’t include symbolication information or source file line numbers in messages. The system automatically captures this information.

Included Headers

  • <log.h>

Topics

Creating a Custom Log Object

os_log_create

Creates a custom log object, to be passed to logging functions for sending messages to the logging system.

Generating Log Messages

os_log

Sends a default-level message to the logging system.

os_log_info

Sends an info-level message to the logging system.

os_log_debug

Sends a debug-level message to the logging system.

os_log_error

Sends an error-level message to the logging system.

os_log_fault

Sends a fault-level message to the logging system.

os_log_with_type

Sends a message at a specific logging level, such as default, info, debug, error, or fault, to the logging system.

Determining Whether Logging is Enabled

os_log_info_enabled

Returns a Boolean value indicating whether info-level logging is enabled for a specified log object.

os_log_debug_enabled

Returns a Boolean value indicating whether debug-level logging is enabled for a specified log object.

os_log_type_enabled

Returns a Boolean value indicating whether a specific type of logging, such as default, info, debug, error, or fault, is enabled for a specified log object.

Constants

Log Objects

System-provided log objects that may be used when a custom log object isn’t needed.

os_log_type_t

Logging levels supported by the system.

Data Types

Enumerations

os_log_type_t

Logging levels supported by the system.