Logging

Logger


The Logger component enables logging of text messages used for error recording and debugging. These messages can be logged to several destinations, for example, the iOS console, one or more files, or custom destinations.

Note: To upload logs to SAP Cloud Platform Mobile Services, use the SAPcpmsLogUploader component from the SAPFoundation framework. In addition to this API reference, see Logging for more information.

Basic Usage

Log levels:

  • off - turns logging off
  • error - indicates a serious failure
  • warn - indicates a potential problem
  • info - informational messages
  • debug - provides tracing information

Log level recommendations:

  • Use the debug and info log levels to trace the current states of your application. Log these messages extensively, for example, when your code handles different conditions of an operation, or when the state of your app changes (for example, in case the connection is lost, or a user interface is switched).

  • Use the warn log level for errors that allow your app to function without having any impact on the user experience.

  • Use the error log level for errors that put your app’s stability at risk or indicate a malfunction, incorrect usage, or runtime error. Typical examples include catching an error or exception, illegal user input, or situations where your app is about to terminate unexpectedly.

Set the log level of the Logger:

Logger.root.logLevel = LogLevel.error

Note: LogLevel.debug can be used for local development, use LogLevel.error in a production environment.

Using the Logger Hierarchy

Multiple logger instances can exist simultaneously. Each logger instance is part of a hierarchy and has a keyPath associated with it. The root of the hierarchy is the root logger: it is the parent of all Logger instances. Each Logger instance you create is a direct or indirect descendant of the root instance.

Loggers are created with a name, which preferably contains dots (“.”) that defines the hierarchy: each element of the name separated by a dot can be a parent of the instance.

You can use a logger instance in any class by calling:

public class MyClass {
    private let logger = Logger.shared(named: <#LoggerIdentifier#>)
}

Log messages

Warning: Avoid logging sensitive data, since logged data is readable in the log.

Set the level of messages to be logged with one of these methods:

logger.error("Message", error: errorObj) // equivalent to logger.log("Message", error: errorObj, forLevel:.error)
logger.warn("Message", error: errorObj)  // equivalent to logger.log("Message", error: errorObj, forLevel:.warn)
logger.info("Message", error: errorObj)  // equivalent to logger.log("Message", error: errorObj, forLevel:.info)
logger.debug("Message", error: errorObj) // equivalent to logger.log("Message", error: errorObj, forLevel:.debug)

If the level of messages is lower than the logger’s, it is not logged.

Advanced Usage

LogHandlers

LogHandlers are attached to Logger instances, and define the destinations where log messages are written: OSLogConsoleHandler and FileLogHandler are typical handlers. They are used to write log messages to the console or to a file respectively.

Handlers that are attached to parent Logger instances are inherited by child Logger instances (unless otherwise specified). This allows fine-grained control and makes it possible to configure the log destination for an entire subtree of the Logger hierarchy.

OSLogConsoleHandler is attached to the Logger.root by default.

Add FileLogHandler to write logs to a file, For example:

let fileHandler = try FileLogHandler(fileURL: <#your URL#>)
Logger.root.add(handler: fileHandler)

A tyical use is to write to the documents forder of the app:

let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let filePath = documentsPath + "/MyAppLog.txt"
let handler = try! FileLogHandler(fileURL: URL(fileURLWithPath: filePath))

These logs can be then read back with xcode by downloading or browsing the container of the app . In Xcode select your device at Window / Devices and Simulators. Clicking the cogwheel icon enables you to browse or download the container.

LogFormatters

LogFormatters are responsible for formatting the messages logged with a Logger instance before they are written to the destination LogHandlers. Such formatting can include adding a sequence number, logging date and time, or formatting the output in a special way, depending on your organization’s logging policy. Typically each logging LogHandler has an associated LogFormatter.

There are two default formatters available:

  • A class for writing log messages to a variety of different sinks, so called handlers.

    In your code, you can log messages with one of the following methods:

    logger.error("Message", error: errorObj)
    logger.warn("Message", error: errorObj)
    logger.info("Message", error: errorObj)
    logger.debug("Message", error: errorObj)
    

    All calls to shared(named: String) always returns an instance of Logger and the instance always has a parent of type Logger. The only exception is the root logger which has no direct parent.

    There can be multiple logger instances at the same time, which are all descendants of the root logger. Each logger instance is part of a hierarchy and has a keyPath associated with it. Each instance is used to create log messages using methods like Logger.error("Message").

    Log messages are handled by the handlers associated with each logger. All attached LogHandlers are passed to the loggers ancestors in the log hierarchy. This behaviour can be controlled through the useParentHandlers parameter.

    Each handler must conform to the LogHandler protocol. Each handler can have its own LogFormatter, which is responsible for the output format of the log messages. Loggers as well as LogHandlers objects can include an optional LogLevel. Consequently, log messages with a lower level are not processed by the logger or handler object respectively. If a specific level is not set, the level is obtained from the ancestor logger instances in the hierarchy.

    IMPORTANT: Avoid logging sensitive data. All logged data will be readable in the log.

    See more

    Declaration

    Swift

    public class Logger : Hashable, Equatable, CustomStringConvertible
  • LogRecord objects are used to pass logging requests between the logging framework and individual log Handlers.

    See more

    Declaration

    Swift

    public struct LogRecord
  • The LogLevel enum defines a set of standard logging levels that can be used to control logging output. The logging Levels are ordered. Enabling logging at a given level also enables logging at all higher levels. Clients must use the predefined constants such as LogLevel.debug. Error is the highest and debug is the lowest level.

    See more

    Declaration

    Swift

    public enum LogLevel : UInt, Comparable, CustomStringConvertible
  • This function compares if a log level is from lower severity than the other.

    Declaration

    Swift

    public func < (lhs: LogLevel, rhs: LogLevel) -> Bool

    Parameters

    lhs

    left hand logLevel for comparison

    rhs

    right hand logLevel for comparison

    Return Value

    true if the left hand log level do has a lower severity than the right hand log level, otherwise false

  • This function compares two log levels, if they do have the same severity.

    Declaration

    Swift

    public func == (lhs: LogLevel, rhs: LogLevel) -> Bool

    Parameters

    lhs

    left hand logLevel for comparison

    rhs

    right hand logLevel for comparison

    Return Value

    true if the log levels do have the same severity, otherwise false

  • Enum which covers all errors occuring in the logging framework

    See more

    Declaration

    Swift

    public enum LoggingError : Error
  • A default implementation of a LogHandler logging to Xcode’s console. This implementation uses Apple’s print() method to log to the console.

    See more

    Declaration

    Swift

    open class ConsoleLogHandler : LogHandler
  • A LogHandler implementation that provides a rolling file handler implementation.

    Log messages are written to a file, which is rolled if it exceeds a specified size. A certain number of backup files can be kept on disk.

    If you want to write logs to file, you can add FileLogHandler as follows:

    let fileLogHandler = try FileLogHandler(fileUrl: fileUrl)
    Logger.root.add(handler: fileLogHandler)
    
    See more

    Declaration

    Swift

    open class FileLogHandler : LogHandler
  • LogHandler protocol accepts a logging request and exports the desired messages to a target, for example, a file, the console, etc. It can be disabled by setting its logging level to .off.

    See more

    Declaration

    Swift

    public protocol LogHandler : AnyObject
  • Default implementation of LogFormatter protocol optimized for console handlers. This default implemention is used by the ConsoleLogHandler.

    See more

    Declaration

    Swift

    open class ConsoleLogFormatter : LogFormatter
  • Default implementation of LogFormatter protocol optimized for file or stream handlers. This implemention is used by the FileLogHandler.

    See more

    Declaration

    Swift

    open class FileLogFormatter : ConsoleLogFormatter
  • A LogFormatter protocol provides support for formatting LogRecords. Typically each LogHandler will have a LogFormatter associated with it. The LogFormatter takes a LogRecord and converts it to a string. Some formatters (such as the FileLogFormatter) need to put head strings above a set of formatted records. The head property can be used to obtain these strings.

    There are 2 default formatters alreadey available:

    1. DefaultLogFormatter -> normally used by a console handler
    2. FileLogFormatter -> normally used by a file handler
    See more

    Declaration

    Swift

    public protocol LogFormatter : AnyObject