Twitter Logging Service is a robust and performant logging framework for iOS clients

Overview

Twitter Logging Service

Background

Twitter created a framework for logging in order to fulfill the following requirements:

  • fast (no blocking the main thread)
  • thread safe
  • as easy as NSLog in most situations
  • support pluggable "output streams" to which messages will be delivered
  • "output streams" filter messages rather than global filtering for all "output streams"
  • able to categorize log messages (log channels)
  • able to designate importance to log messages (log levels)
  • require messages to opt-in for persisted logs (a security requirement, fulfilled by using the context feature of TLS)

Twitter has been using Twitter Logging Service since January 2014 with minimal changes. We've decided to share it with the developer community.

List of good alternative logging frameworks

If Twitter Logging Service doesn't meet your needs, there are many great logging frameworks available, including the following high quality and well maintained projects:

  • CocoaLumberjack
  • SwiftyBeaver
  • Apache Logging Services

Architecture

There are 3 components to consider:

  1. the log message and its context
  2. the logging service instance or singleton
  3. the output stream(s)

The log message is sent to the logging service which provides the message to each output stream.

The logging service is configured by adding discrete output streams. Output streams encapsulate their own behavior and decisions, including filtering and logging messages. For instance, logging can mean printing to console with NSLog, writing to a file on disk, or sending the message to a remote server.

Message arguments don't need to be evaluated if the message is going to be filtered out. This avoids expensive, synchronous execution of argument evaluation. The message is then packaged with context before it is sent to the logging service. Context includes information such as the log level, log channel, file name, function name, line number and timestamp.

The logging service marshals the message and its context to a background queue for processing by all available output streams. Streams can then filter or output the message.

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it using the following command:

    $ gem install cocoapods

To integrate TwitterLoggingService into your Xcode project using CocoaPods, specify it in your Podfile:

    platform :ios, '8.0'
    use_frameworks!

    target "MyApp" do
        pod 'TwitterLoggingService', '~> 2.9.0'
    end

Usage

TLSLog.h is the principal header for using TwitterLoggingService. Just include TLSLog.h or @import TwitterLoggingService.

    // The primary macros for *TwitterLoggingService*

    TLSLogError(channel, ...)          // Log at the TLSLogLevelError level
    TLSLogWarning(channel, ...)        // Log at the TLSLogLevelWarning level
    TLSLogInformation(channel, ...)    // Log at the TLSLogLevelInformation level
    TLSLogDebug(channel, ...)          // Log at the TLSLogLevelDebug level

For each macro in the TLSLog family of macros, TLSCanLog is called first to gate whether the actual logging should occur. This saves us from having to evaluate the arguments to the log message and can provide a win in performance when calling a TLSLog macro that will never end up being logged. For more on TLSCanLog see Gating TLSLog messages below.

TLSLog Core Macro

    #define TLSLog(level, channel, ...)

TLSLog is the core macro and takes 3 parameters: a TLSLogLevel level, an NSString channel and then an NSString format with variable formatting arguments. The level and channel parameters are used to filter the log message per TLSOutputStream in the TLSLoggingService singleton. Providing nil as the channel argument to any logging macro, function or method will result in the message not being logged.

Logging Channels, Levels and Context Objects

Channels

The logging channel of a log message is an arbitrary string and acts as a tag to that message to further help identify what the message relates to. Channels can help to quickly identify what a log message relates to in a large code base, as well as provide a mechanism for filtering. A TLSOutputStream can filter based on the logging channel in its implementation of tls_shouldFilterLevel:channel:contextObject:. Providing a nil channel to a log statement has the effect of not logging that message.

Examples of potential logging channels: @"Networking" for the networking stack, @"SignUp" for an app’s signup flow, TLSLogChannelDefault as a catch all default logging channel, and @"Verbose" for anything you just want to log for the helluvit.

Levels

The enum TLSLogLevel specifies 8 logging levels in accordance with the syslog specification for logging. For practical use, however, only 4 log levels are used: TLSLogLevelError, TLSLogLevelWarning, TLSLogLevelInformation and TLSLogLevelDebug. Each log message has a specified logging level which helps quickly identify its level, TLSLogLevelEmergency (or TLSLogLevelError in practice) is the most important while TLSLogLevelDebug is the least. TLSOutputStream instances can filter a log message by its log level (in combination with its logging channel and context object) by implementing tls_shouldFilterLevel:channel:contextObject:.

An implementation detail to keep in mind w.r.t. logging levels is that TLSLogLevelDebug is ALWAYS filtered out in non-DEBUG builds.

Context Objects

Though the TLSLog macros do not have a context object parameter, one can provide a context object to the TLSLogging APIs in order to provide additional context to custom TLSOutputStreams. The context object will carry through the TLSLoggingService so that it is available to all TLSOutputStream instances. The context object can be used to filter in the tls_shouldFilterLevel:channel:contextObject: method. The context object can also be used for additional information in the logging of a message since it carries to the TLSLogMessageInfo object that's passed to tls_outputLogInfo:.

This context object provides near limitless extensibility to the TLSLogging framework beyond the basics of filtering and logging based on a logging level and logging channel. Twitter uses the context object as a way to secure log messages from leaking to output streams that should not log messages unless explicitely told to do so, thus protecting Personally Identifiable Information from being logged as a default behavior.

Setup

Setting up your project to use TwitterLoggingService:

  1. Add the TwitterLoggingService XCode project as a subproject of your XCode project.

  2. Add the libTwitterLoggingService.a library or TwitterLoggingService.framework framework as a dependency in your XCode project.

  3. Set up your project to build the TwitterLoggingService project with DEBUG=1 in debug builds and RELEASE=1 in release builds.

  4. Set up the TLSLoggingService singleton on application startup (often in application:didFinishLaunchingWithOptions: of your UIApplication's delegate for iOS).

    @import TLSLoggingKit;

    // ...

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)options
    {
        // ...

        // Set up the Twitter Logging Service!
        TLSLoggingService *logger = [TLSLoggingService sharedInstance];
    #if DEBUG
        if ([TLSOSLogOutputStream supported]) {
            [logger addOutputStream:[[TLSOSLogOutputStream alloc] init]];
        } else {
            [logger addOutputStream:[[TLSNSLogOutputStream alloc] init]];
        }
    #endif
        [logger addOutputStream:[[TLSFileOutputStream alloc] initWithLogFileName:@"appname-info.log"]];

        // ...
    }

    // ...

    // Someplace else in your project
    - (void)foo
    {
        //  ...

        if (error) {
            TLSLogError(TLSLogChannelDefault, @"Encountered an error while performing foo: %@", error);
        } else {
            TLSLogInformation(@"Verbose", @"foo executed flawlessly!");
        }

        // ...
    }

Best Practices

As a best practice follow these simple guidelines:

  1. Any user sensitive information should not be logged to output streams that persist messages (including being sent over the network to be saved). You can configure your output stream to filter out logs to these sensitive channels. Or do the inverse, and only permit certain "safe" channels to be logged. Twitter has elected to use the pattern where only explicitely "safe" messages (designated via a custom context object) are logged to output streams that will persist. If in doubt, you can log to the TLSLogLevelDebug log level, which is only ever logged in DEBUG builds.

  2. Configure DEBUG builds to have a console output like TLSNSLogOutputStream or TLSStdErrOutputStream - but add only 1 or you'll spam the debug console.

  3. Configure RELEASE builds to not use the console output stream.

  4. Add Crashlytics to your project and add a subclass of the TLSCrashlyticsOutputStream to TLSLoggingService instead of using CLSLog, CLSNSLog or CLS_LOG. You MUST subclass TLSCrashlyticsOutputStream.

TLSLogChannelApplicationDefault function

    FOUNDATION_EXTERN NSString *TLSLogChannelApplicationDefault() __attribute__((const));
    #define TLSLogChannelDefault TLSLogChannelApplicationDefault()

Retrieve a channel based on the application. You can use this as a default channel.

Loads and caches the channel name in the following order of descending priority:

  1. kCFBundleNameKey of main bundle

  2. kCFBundleExecutableKey of main bundle

  3. Binary executable name

  4. @"Default"

The default channel is available as a convenience for quick logging. However, it is recommended to always have concrete, well-defined logging channels to which output is logged (e.g. "Networking", "UI", "Model", "Cache", et al).

TLSLog Helper Functions

There are a number of TLSLog Helper Functions and they all accept as a first parameter a TLSLoggingService. If nil is provided for the service parameter, the shared [TLSLoggingService sharedInstance] will be used. All TLSLog macros use nil for the service parameter, but if there is different instance to be used, these helper functions support that. As an example, Twitter extends TwitterLoggingService with its own set of macros so that a context is provided that defines the duration for which a message can be safely retained (e.g. to avoid retaining sensitive information), and uses custom macros that call these helper functions.

Gating TLSLog messages

    BOOL TLSCanLog(TLSLoggingService *service, TLSLogLevel level, NSString *channel, id contextObject); // gate for logging TLSLog messages

At the moment, TLSCanLog evaluates two things (contextObject is currently ignored): the cached permitted log levels and the cached not permitted log channels. A log message can log given the desired level is permitted by the internal cache of known permitted TLSLogLevels based on the outputStreams of TLSLoggingService AND the given log channel has not been cached as a known to be an always off channel (for TLSLOGMODE=1 that is, see below for different behaviors).

TLSCANLOGMODE build setting

TwitterLoggingService supports being compiled in one of 3 different modes:

  • TLSCANLOGMODE=0
  • TLSCanLog will always return YES
  • Log arguments always evaluate, which can be inefficient for args that won't log
  • TLSCANLOGMODE=1
  • TLSCanLog will base its return value on cached insight into what can and cannot be logged
  • This will save on argument evalution at the minimal cost of a quick lookup of cached information
  • This is the default if TLSCANLOGMODE is not defined
  • TLSCANLOGMODE=2
  • TLSCanLog will base its return value on the filtering behavior of all the registered output streams
  • This will save on argument evalution but requires an expensive examination of all output streams

License

Copyright 2013-2020 Twitter, Inc.

Licensed under the Apache License, Version 2.0: https://www.apache.org/licenses/LICENSE-2.0

Security Issues?

Please report sensitive security issues via Twitter's bug-bounty program (https://hackerone.com/twitter) rather than GitHub.

Comments
  • Update TLS with code cleanup and project updates

    Update TLS with code cleanup and project updates

    • reformatting for consistency
    • xcconfig use to share settings amongst all targets, and to see comments for settings
    • static functions employed (vs private methods) based on evidence of better performance
    opened by NSProgrammer 2
  • File log issue

    File log issue

    Logging in file doesn't work. I try to use your solution for file logging but even in our example application for iOS log files are empty after using. Maybe I don't understand how to use our logging service.

    opened by evolkov 2
  • Update to v2.9.0

    Update to v2.9.0

    2.9.0 (08/06/2020)

    • Drop support for iOS 7, 8 & 9

    2.8.5 (07/21/2020)

    • Improved Swift interface

    2.8.1 (07/01/2020)

    • Convert private static C functions (that take self as an argument) to Objective-C direct methods
      • Same low overhead, better calling syntax

    2.8.0 (06/08/2020)

    • Update initWithOutError: to initAndReturnError: for TLSRollingFileOutputStream
      • More idiomatic
    opened by NSProgrammer 1
  • Update to v2.7.1

    Update to v2.7.1

    • Increase use of @autoreleasepool to keep memory management tighter
    • Improve at launch perf by deferring the pruning of old files for TLSRollingFileOutputStream
    • Other miscellaneous bug fixes and cleanup
    opened by NSProgrammer 1
  • compile error after pod install

    compile error after pod install

    install via pod 'TwitterLoggingService', '~> 2.3.0' Xcode 10.2.1 compile error below: Value for SWIFT_VERSION cannot be empty. (in target 'TwitterLoggingService')

    if I set the Swift Language Version to (Swift5/Swift4.2/Swift4), the TLSLog.swift has many error: ..../Pods/TwitterLoggingService/Classes/TLSLog.swift:34:90: Attribute can only be applied to types, not declarations ..../Pods/TwitterLoggingService/Classes/TLSLog.swift:48:47: Attribute can only be applied to types, not declarations and etc...

    thanks !

    opened by nillcui 1
  • Example support for 3rd party logging service, eg. Papertrail

    Example support for 3rd party logging service, eg. Papertrail

    Congratulations on releasing this! It would be helpful to have more pointers on how to create output streams for existing 3rd party services. Would especially like to see in the README (#10) how to extend TLS to support Papertrail, for example.

    Thanks!

    wontfix 
    opened by moflo 1
  • Add support for iOS 10 & macOS Sierra new OS Logging

    Add support for iOS 10 & macOS Sierra new OS Logging

    https://developer.apple.com/reference/os/1891852-logging

    Apple's new logging system is a major improvement over NSLog, let's create a 3rd console output stream that will use os_log too. It will be left to developers consuming the framework to create a combo output stream that will use os_log for new OS versions and NSLog for old OS versions.

    enhancement 
    opened by NSProgrammer 1
  • Update LICENSE to use the full text

    Update LICENSE to use the full text

    As part of some GitHub cleanup, Twitter's OSPO is updating license text to use the full text of the stated license. If there are no objections in the next 7 days, we will go ahead and merge this PR.

    opened by juliaferraioli 0
  • Update TLS to v2.7.0

    Update TLS to v2.7.0

    2.7.0 (06/28/2019)

    • Add support for capturing os_log logs executed within a running app.
      • TLSExt is provided as the interface and is not a part of TLS proper. This is because TLSExt makes use of private Apple frameworks and would lead to app store rejection.
      • So, the consumer is responsible to compiling and linking the TLSExt*.h|m files themselves and must only do so with non-production builds.
      • This can be of immense help when dogfooding with developers and teammates with an Enterprise distribution (not Test Flight and not production).

    2.6.0 (06/11/2019)

    • Add options to composing a log message string from TLSLogMessageInfo
      • TLSComposeLogMessageInfoOptions provides options for what components to put in the output composeFormattedMessageWithOptions: string
    opened by NSProgrammer 0
  • fix broken TwitterLoggingService.xcodeproj references

    fix broken TwitterLoggingService.xcodeproj references

    this fixes the following pre-commit hook diagnostics:

    <PBXTargetDependency UUID=8B78F2991C63A62B000194DF> attempted to initialize an object with an unknown UUID. 8B78F2981C63A62B000194DF for attribute: target_proxy. This can be the result of a merge and the unknown UUID is being discarded.

    <PBXNativeTarget name=ExampleLoggerUUID=8B7DB13F1869EC2600999DA0> attempted to initialize an object with an unknown UUID. 8B9C92901CEFA9C30052BA09 for attribute: dependencies. This can be the result of a merge and the unknown UUID is being discarded.'

    also, alphabetized group TwitterLoggingServiceTests (mostly as a means of triggering Xcode to auto-fix itself as the pre-commit ruby scripts say it would.)

    opened by twitterkb 0
  • changes to continuous integration

    changes to continuous integration

    We will be dropping our paid Travis CI plan at the end of 2021. We do not expect there to be any visible changes to this repo, but wanted to give some notice just in case. We recommend migrating CI jobs to GitHub Actions.

    Travis CI provides free testing for open source projects. In addition, Twitter has paid for a small number of additional concurrent builds which were available for open source as well as private repositories. Many Twitter projects have already moved to GitHub Actions for CI, and we have no private repos left using Travis, so we will be discontinuing our plan at the end of 2021.

    Since this repo is open source, we do not expect this change to impact Travis CI builds for this project. However, we still recommend most Twitter projects to migrate to GitHub Actions for CI at your convenience.

    opened by juliaferraioli 0
Releases(2.9.0)
  • 2.9.0(Aug 10, 2020)

    2.9.0

    • Drop support for iOS 7, 8 & 9

    2.8.5

    • Improved Swift interface

    2.8.1

    • Convert private static C functions (that take self as an argument) to Objective-C direct methods
      • Same low overhead, better calling syntax

    2.8.0

    • Update initWithOutError: to initAndReturnError: for TLSRollingFileOutputStream
      • More idiomatic
    Source code(tar.gz)
    Source code(zip)
  • 2.7.1(May 30, 2020)

    • Increase use of @autoreleasepool to keep memory management tighter
    • Improve at launch perf by deferring the pruning of old files for TLSRollingFileOutputStream
    • Other miscellaneous bug fixes and cleanup
    Source code(tar.gz)
    Source code(zip)
  • 2.7.0(May 30, 2020)

    2.7.0 (06/28/2019)

    • Add support for capturing os_log logs executed within a running app.
      • TLSExt is provided as the interface and is not a part of TLS proper. This is because TLSExt makes use of private Apple frameworks and would lead to app store rejection.
      • So, the consumer is responsible to compiling and linking the TLSExt*.h|m files themselves and must only do so with non-production builds.
      • This can be of immense help when dogfooding with developers and teammates with an Enterprise distribution (not Test Flight and not production).

    2.6.0 (06/11/2019)

    • Add options to composing a log message string from TLSLogMessageInfo
      • TLSComposeLogMessageInfoOptions provides options for what components to put in the output composeFormattedMessageWithOptions: string
    Source code(tar.gz)
    Source code(zip)
Owner
Twitter
Twitter 💙 #opensource
Twitter
JustLog brings logging on iOS to the next level. It supports console, file and remote Logstash logging via TCP socket with no effort. Support for logz.io available.

JustLog JustLog takes logging on iOS to the next level. It supports console, file and remote Logstash logging via TCP socket with no effort. Support f

Just Eat 509 Dec 10, 2022
A logging backend for swift-log that sends logging messages to Logstash (eg. the ELK stack)

LoggingELK LoggingELK is a logging backend library for Apple's swift-log The LoggingELK library provides a logging backend for Apple's apple/swift-log

null 17 Nov 15, 2022
A fast & simple, yet powerful & flexible logging framework for Mac and iOS

CocoaLumberjack CocoaLumberjack is a fast & simple, yet powerful & flexible logging framework for macOS, iOS, tvOS and watchOS. How to get started Fir

null 12.9k Jan 9, 2023
Simple, lightweight and flexible debug logging framework written in Swift

AELog Simple, lightweight and flexible debug logging minion written in Swift If you find yourself in upcoming statements, then you probably want to us

Marko Tadić 28 Jul 6, 2022
A lightweight logging framework for Swift

HeliumLogger Provides a lightweight logging implementation for Swift which logs to standard output. Features Logs output to stdout by default. You can

Kitura 174 Nov 30, 2022
A lightweight logging framework for Swift

HeliumLogger Provides a lightweight logging implementation for Swift which logs to standard output. Features Logs output to stdout by default. You can

Kitura 174 Nov 30, 2022
An extensible logging framework for Swift

Log is a powerful logging framework that provides built-in themes and formatters, and a nice API to define your owns. Get the most out of Log by insta

Damien 825 Nov 6, 2022
A powerful input-agnostic swift logging framework made to speed up development with maximum readability.

The Swift logging framework. Atlantis is an extremely powerful logging framework that I've created for everyday use, including enterprise development

Andrew Aquino 199 Jan 2, 2023
TraceLog is a highly configurable, flexible, portable, and simple to use debug logging system for Swift and Objective-C applications running on Linux, macOS, iOS, watchOS, and tvOS.

Please star this github repository to stay up to date. TraceLog Introduction TraceLog is a highly configurable, flexible, portable, and simple to use

Tony Stone 52 Oct 28, 2022
Elegant and extensive logging facility for OS X & iOS (includes database, Telnet and HTTP servers)

Overview XLFacility, which stands for Extensive Logging Facility, is an elegant and powerful logging facility for OS X & iOS. It was written from scra

Pierre-Olivier Latour 315 Sep 7, 2022
In-App iOS Debugging Tool With Enhanced Logging, Networking Info, Crash reporting And More.

The debugger tool for iOS developer. Display logs, network request, device informations, crash logs while using the app. Easy accessible with its bubble head button ?? . Easy to integrate in any apps, to handle development or testing apps easier. First version, there is plenty of room for improvement.

Remi ROBERT 1.8k Dec 29, 2022
Logging utility for Swift and Objective C

Swell - Swift Logging A logging utility for Swift and Objective C. ##Features Turn on logging during development, turn them off when building for the

Hubert Rabago 361 Jun 29, 2022
Simple logging for simples needs.

Simple logging for simples needs.

native.dev.br 0 May 30, 2022
Convenient & secure logging during development & release in Swift 3, 4 & 5

Colorful, flexible, lightweight logging for Swift 3, Swift 4 & Swift 5. Great for development & release with support for Console, File & cloud platfor

SwiftyBeaver 5.6k Jan 4, 2023
Class for logging excessive blocking on the main thread

Watchdog Class for logging excessive blocking on the main thread. It watches the main thread and checks if it doesn’t get blocked for more than define

Wojtek Lukaszuk 1.8k Dec 6, 2022
Willow is a powerful, yet lightweight logging library written in Swift.

Willow Willow is a powerful, yet lightweight logging library written in Swift. Features Requirements Migration Guides Communication Installation Cocoa

Nike Inc. 1.3k Nov 16, 2022
Swift Logging Utility for Xcode & Google Docs

QorumLogs Swift Logging Utility in Xcode & Google Docs

Goktug Yilmaz 777 Jul 15, 2022
A flexible logging library written in Swift

Puppy Puppy is a flexible logging library written in Swift ?? It supports multiple transports(console, file, syslog, and oslog) as loggers. It not onl

Koichi Yokota 92 Dec 29, 2022
Most natural Swift logging

Evergreen Most natural Swift logging Evergreen is a logging framework written in Swift. It is designed to work just as you would expect, yet so versat

Nils Leif Fischer 72 Aug 12, 2022