SwiftでもやっぱりCocoaLumberjack

![qiitaswiftlumberjacksample.gif](https://qiita-image-store.s3.amazonaws.com/0/16102/301ea54f-5a60-0451-a938-e670b6721238.gif)
  
  
私はObjective-C時代のロギングには[CocoaLumberjack](https://github.com/CocoaLumberjack/CocoaLumberjack)を利用していました(というか今も利用しています)。新規にSwiftベースのアプリを開発するにあたって、充実したロギング環境を実現するために、自分自身でちょっとしたヘルパークラスを作ったり、良さげなライブラリを探したりしましたが、結局、Swiftでも痒いところに手が届くCocoaLumberjackを活用することで落ち着いています。

今回は、Swift環境で新たにCocoalumberjackを利用するにあたっての、ちょっとした注意点やwebページで見かけたことがない機能について少しだけ紹介したいと思います。CocoaLumberjackの基本的な使い方については、（Objective-Cベースですが、）下記あたりが参考になるのではないかと思います。
[Qiita - ログ出力ライブラリのCocoaLumberjackを使う](http://qiita.com/yuch_i/items/b25ddb6ceb788777c83b)

# TL;DR
環境
- Xcode 7.2
- Swift 2.2.1
- iOS 9.2

- 今のところCarthageを使う場合は「$ carthage update --no-use-binaries」を実行する必要あり
- ログフォマッターにはDDDispatchQueueLogFormatterも利用検討しよう
- キューラベルを使ってスレッド情報も確認しよう
- Xcodeプラグイン「KZLinkedConsole」を使ってデバッグ効率をあげよう

# ライブラリの導入
CocoaPodsとCarthageの両方に対応しています。ただし、Carthageを利用する場合は現時点では少し注意が必要です。

#### **CocoaPodsを利用する場合**

```rb:Podfile
platform :ios, '8.0'
use_frameworks!

target 'SwiftLumberjackSample' do
  pod 'CocoaLumberjack/Swift'
end
```
```bash
$ pod install
```
いつも通り「pod install」を実行すればOKです。

#### **Carthageを利用する場合**

```swift:Cartfile
github "CocoaLumberjack/CocoaLumberjack"
```
```bash
$ carthage update --no-use-binaries
```

「no-use-binaries」オプションを付与して「carthage update」を実行する必要があります。
これは現在のCocoaLumberjackの[リリース](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/2.2.0)において、誤ってframework.zipファイルが複数登録されているのが直接の原因で、ワークアラウンドとして必要になります。現在のところ「carthage update」コマンドによるビルド済みのframework.zipファイルのダウンロードについては1ファイルしか対応していないようです。本件に関しては、githubにおいて[私もコメントしている](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/666)ので興味のある方は覗いてみてください。
あとはいつも通りCarthageを使う場合と同様に、生成されたCocoaLumberjack.frameworkとCocoaLumberjackSwift.frameworkをプロジェクトに追加等すればOKです。

# DDDispatchQueueLogFormatter
カスタムログフォーマッターの作成方法として、CocoaLumberjackのドキュメントやWebページではDDLogFormatterを利用する方法のみが紹介されていますが、複数のロガー(例: コンソールロガーとファイルロガー）を利用する場合等にはスレッドセーフを別途考慮する必要があります。
DDLogFormatterとは異なりDDDispatchQueueLogFormatterでは、最初からこのスレッドセーフが考慮されているので、今後カスタムログフォーマッターを作成する場合にはDDDispatchQueueLogFormatterを利用すると便利ではないかと思います。

```swift:LogFormatter.swift
import Foundation
import CocoaLumberjack.DDDispatchQueueLogFormatter

class LogFormatter: DDDispatchQueueLogFormatter {
    
    let dateFormatter: NSDateFormatter
    
    override init!() {
        dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss:SSS"
        super.init()
    }
    
    override func formatLogMessage(logMessage: DDLogMessage!) -> String! {
        
        var logLevel: String
        switch logMessage.flag {
        case DDLogFlag.Error:
            logLevel = "E"
        case DDLogFlag.Warning:
            logLevel = "W"
        case DDLogFlag.Info:
            logLevel = "I"
        case DDLogFlag.Debug:
            logLevel = "D"
        case DDLogFlag.Verbose:
            logLevel = "V"
        default:
            logLevel = "U"  // Unknown
        }
        
        let dateAndTime = dateFormatter.stringFromDate(logMessage.timestamp)
        let queueLabel = queueThreadLabelForLogMessage(logMessage)
        //let fileName = logMessage.fileName
        let fileName = (logMessage.file as NSString).lastPathComponent
        let lineNumber = logMessage.line
        let function = logMessage.function
        let message = logMessage.message
        
        return "\(dateAndTime) [\(logLevel)] [\(queueLabel)] [\(fileName):\(lineNumber) \(function)] \(message) "
    }
    
}
```

# キューラベルのログ表示
上記の通り`queueThreadLabelForLogMessage(logMessage)`を使うことでディスパッチキューのラベルもログ表示させることができます。リアルタイムにどのスレッドが実行されているかを簡単に把握できるので便利です。

```text:ログ出力
2015/12/20 01:16:19:813 [I] [com.apple.root.default-qos] [ViewController.swift:35 doSomething] Here is global queue. 
2015/12/20 01:16:19:814 [I] [main] [ViewController.swift:37 doSomething] Here is main queue. 
```

# KZLinkedConsole
Xcodeプラグインの「[KZLinkedConsole](https://github.com/krzysztofzablocki/KZLinkedConsole)」を組み合わせるとさらに便利になります。[Alcatraz](http://alcatraz.io)を使って導入しましょう。
Xcodeコンソールログ出力のフォーマットに「ファイル名.swift:行番号」を含めることにより、該当箇所へのリンクが動的に張られるようになります。上述のログフォーマッタでは```\(fileName):\(lineNumber)```でこれを実現しています。
**冒頭のアニメーションではこの様子を表示しています。**ログ出力から直接該当ファイルの行に移動できるのでデバッグがはかどります。

# ログ出力レベルの変更
Objective-Cでは```ddLogLevel```を使ってログ出力レベルを変更していましたが、Swiftでは```defaultDebugLevel```を使います。以下は上述のカスタムログフォーマッタを適用して、ログレベルをInfoにした場合のサンプルコードとログ出力結果です。

```swift:ViewController.swift
import UIKit
import CocoaLumberjack

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // apply custom logformatter
        DDTTYLogger.sharedInstance().logFormatter = LogFormatter()
        DDLog.addLogger(DDTTYLogger.sharedInstance())
        
        // change logging level
        defaultDebugLevel = DDLogLevel.Info

        DDLogError("error message")
        DDLogWarn("warn message")
        DDLogInfo("info message")
        DDLogDebug("debug message")
        DDLogVerbose("verbose message")
    }

    @IBAction func doSomething(sender: AnyObject) {
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), {
            DDLogInfo("Here is global queue.")
            dispatch_async(dispatch_get_main_queue(), {
                DDLogInfo("Here is main queue.")
            })
        })
    }
}
```

```text:ログ出力
2015/12/20 01:16:18:171 [E] [main] [ViewController.swift:21 viewDidLoad()] error message 
2015/12/20 01:16:18:174 [W] [main] [ViewController.swift:22 viewDidLoad()] warn message 
2015/12/20 01:16:18:174 [I] [main] [ViewController.swift:23 viewDidLoad()] info message 
2015/12/20 01:16:19:813 [I] [com.apple.root.default-qos] [ViewController.swift:35 doSomething] Here is global queue. 
2015/12/20 01:16:19:814 [I] [main] [ViewController.swift:37 doSomething] Here is main queue. 
```

# まとめ
いかがでしたでしょうか？
私としてはSwift時代においてもやっぱりCocoaLumberjackはロギングライブラリの第1候補です。一方で、もっと便利なライブラリやこんなの作ってみたよ等ありましたら、ぜひぜひ教えて下さい。みなさんでもっともっと快適なアプリ・デバッグ開発環境を目指しましょう！