TwitterLoggingService 2.9.0

TwitterLoggingService 2.9.0

测试已测试
语言语言 Obj-CObjective C
许可证 NOASSERTION
发布最新发布2020年8月

Nolan O'BrienTwitter维护。



  • 作者
  • Twitter

Twitter Logging Service

背景

Twitter为了满足以下需求,创建了一个日志框架

  • 快速(不阻塞主线程)
  • 线程安全
  • 在大多数情况下,如NSLog一样简单
  • 支持可插拔的“输出流”,将消息传递到这些“输出流”
  • “输出流”会过滤消息,而不是对所有“输出流”进行全局过滤
  • 能够分类日志消息(日志通道)
  • 能够指定日志消息的重要性(日志级别)
  • 要求消息必须选择以持久化日志(一个安全要求,通过使用TLS的上下文功能来实现)

Twitter从2014年1月开始使用Twitter Logging Service,基本上没有任何改动。我们已经决定将它与开发者社区分享。

优秀的替代日志框架列表

如果Twitter Logging Service不符合您的需求,有许多优秀的日志框架可供选择,包括以下质量高且维护良好的项目

  • CocoaLumberjack
  • SwiftyBeaver
  • Apache Logging Services

架构

需要考虑3个组件

  1. 日志消息及其上下文
  2. 日志服务实例或单例
  3. 输出流

将日志消息发送到日志服务,该服务将消息提供给了每个输出流。

通过添加离散的输出流来配置日志服务。输出流封装了自己的行为和决策,包括过滤和记录消息。例如,日志可以是使用NSLog打印到控制台、写入磁盘上的文件或将消息发送到远程服务器。

如果消息将被过滤,则不需要评估消息参数。这避免了昂贵且同步的参数评估执行。然后将消息与上下文一起打包再发送到日志服务。上下文包括日志级别、日志通道、文件名、函数名、行号和时间戳等信息。

日志服务将消息及其上下文排队在后台队列中,供所有可用的输出流处理。然后流可以过滤或输出消息。

安装

CocoaPods

CocoaPods是一个Cocoa项目的依赖管理器。您可以通过以下命令安装它

    $ gem install cocoapods

要使用CocoaPods将TwitterLoggingService集成到Xcode项目中,请在您的Podfile中指定它

    platform :ios, '8.0'
    use_frameworks!

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

使用

TLSLog.h是使用TwitterLoggingService的主要头文件。只需包含TLSLog.h或使用@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

对于TLSLog系列宏中的每个宏,首先调用TLSCanLog来确定是否实际应该记录日志。这使我们避免了评估日志消息的参数,当调用一个永远不会记录的TLSLog宏时,可以在性能上获得一定的提升。有关TLSCanLog的更多信息,请参阅下文的 gate TLSLog messages

TLSLog 核心宏

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

TLSLog 是核心宏,接收 3 个参数:一个 TLSLogLevel 级别、一个 NSString 通道以及一个带有变量格式参数的 NSString 格式字符串。级别和通道参数用于在 TLSLoggingService 单例中根据 TLSOutputStream 过滤日志消息。将任何日志宏、函数或方法中的通道参数提供为 nil 都会导致消息不记录。

日志通道、级别和上下文对象

通道

日志消息的日志通道是一个任意的字符串,充当该消息的标签,以进一步帮助识别该消息的相关内容。通道可以帮助快速识别在大代码库中的日志消息相关内容,以及提供过滤机制。一个 TLSOutputStream 可以根据其 tls_shouldFilterLevel:channel:contextObject: 的实现根据日志通道进行过滤。向日志语句提供一个 nil 通道将导致不记录该消息。

潜在日志通道的例子:"Networking" 用于网络栈,"SignUp" 用于应用的用户注册流程,TLSLogChannelDefault 作为通用的默认日志通道,以及 "Verbose" 用于你想记录的一切。

级别

枚举类型 TLSLogLevel 根据系统日志规范(syslog)定义了8个日志级别。然而,在实际使用中,只使用了4个日志级别:TLSLogLevelErrorTLSLogLevelWarningTLSLogLevelInformationTLSLogLevelDebug。每个日志消息都有一个指定的日志级别,这有助于快速识别其级别,TLSLogLevelEmergency(在实际应用中通常为TLSLogLevelError)最为重要,而 TLSLogLevelDebug 最为次要。可以通过实现 tls_shouldFilterLevel:channel:contextObject: 方法来根据日志级别(结合其日志通道和上下文对象)过滤日志消息的 TLSOutputStream 实例。

关于日志级别的实现细节需要注意,即在非 DEBUG 构建中始终过滤掉 TLSLogLevelDebug

上下文对象

尽管 TLSLog 宏没有上下文对象参数,但仍可以向 TLSLogging API 提供 上下文对象,以便为自定义的 TLSOutputStream 提供额外的上下文。该 上下文对象 将通过 TLSLoggingService 传递,以便所有的 TLSOutputStream 实例都可以访问。上下文对象可以被用于在 tls_shouldFilterLevel:channel:contextObject: 方法中过滤。上下文对象还可以用于在消息记录中提供额外的信息,因为它携带给传递给 tls_outputLogInfo:TLSLogMessageInfo 对象。

这个 上下文对象TLSLogging 框架提供了几乎无止境的扩展性,超越了基于日志级别和日志通道的过滤和记录的基础。

设置

将您的项目设置为使用 TwitterLoggingService

  1. TwitterLoggingService XCode 项目添加为您 XCode 项目的子项目。

  2. libTwitterLoggingService.a 库或 TwitterLoggingService.framework 框架添加为您 XCode 项目的依赖项。

  3. 设置您的项目以在调试构建中使用 DEBUG=1 构建 TwitterLoggingService 项目,并在发布构建中使用 RELEASE=1

  4. 在应用启动时设置 TLSLoggingService 单例(通常在 iOS 中 UIApplication 委托的 application:didFinishLaunchingWithOptions: 中执行)。

    @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!");
        }

        // ...
    }

最佳实践

作为最佳实践,请遵循以下简单准则

  1. 任何用户敏感信息都应避免记录到持久消息的输出流(包括通过网络发送以保存)。您可以将输出流配置为筛选掉这些敏感通道的日志。或者,反向操作,只允许记录到某些“安全”通道。Twitter已选择使用只明确标记为“安全”的消息(通过自定义上下文对象指定)的输出流来持久化的模式。如有疑问,您可以记录到TLSLogLevelDebug日志级别,该级别仅在DEBUG构建中记录。

  2. DEBUG构建配置为具有类似TLSNSLogOutputStreamTLSStdErrOutputStream的控制台输出 - 但只添加1个,否则会充斥调试控制台。

  3. RELEASE构建配置为不使用控制台输出流。

  4. Crashlytics添加到您的项目中,并将TLSCrashlyticsOutputStream的子类添加到TLSLoggingService,而不是使用CLSLogCLSNSLogCLS_LOG。您必须子类化TLSCrashlyticsOutputStream

TLSLogChannelApplicationDefault函数

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

基于应用程序检索一个通道。您可以将该通道用作默认通道。

按以下降序优先级顺序加载和缓存通道名称

  1. 主包的kCFBundleNameKey

  2. 主包的kCFBundleExecutableKey

  3. 二进制可执行文件名称

  4. @"Default"

默认通道是为了方便快速记录而提供的。然而,始终推荐使用具体的、明确定义的记录通道,以便将输出记录到其中(例如,“网络”、“用户界面”、“模型”、“缓存”等)。

TLSLog辅助函数

存在许多TLSLog辅助函数,并且它们都接受一个TLSLoggingService作为第一个参数。如果为service参数提供了nil,将使用共享的[TLSLoggingService sharedInstance]。所有TLSLog宏使用nilservice参数,但如果要使用不同的实例,这些辅助函数支持该功能。例如,Twitter通过使用提供上下文的宏扩展TwitterLoggingService,这些宏定义了消息可以安全保留的时间长度(例如,为了避免保留敏感信息),并使用自定义宏调用这些辅助函数。

门控TLS日志消息

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

目前,TLSCanLog评估两个东西(当前忽略contextObject):缓存的允许日志级别和缓存的不允许日志通道。只有在内部缓存知道允许的基于TLSLoggingServiceoutputStreamsTLSLogLevel并且给定的日志通道未缓存为始终关闭的通道(对于TLSLOGMODE=1,即下文将不同行为)时,才能记录日志消息。

TLSCANLOGMODE 编译设置

TwitterLoggingService支持以三种不同的方式编译

  • TLSCANLOGMODE=0
  • TLSCanLog将始终返回YES
  • 日志参数始终评估,这对于不会记录的参数可能效率低下
  • TLSCANLOGMODE=1
  • TLSCanLog将根据缓存的对可以和不可以记录内容的理解来设置其返回值
  • 这将最小化对缓存信息的快速查找的成本,从而节省参数评估
  • 如果未定义TLSCANLOGMODE,则为默认设置
  • TLSCANLOGMODE=2
  • TLSCanLog将根据所有注册的输出流的筛选行为来设置其返回值
  • 这将节省参数评估但需要对所有输出流进行昂贵的检查

许可

版权所有 2013-2020Twitter公司。

依据Apache License,版本2.0授权:https://www.apache.org/licenses/LICENSE-2.0

安全问题?

请通过Twitter的bug-bounty计划(https://hackerone.com/twitter)报告敏感的安全问题,而不是通过GitHub。