Edited at

SwiftでもやっぱりCocoaLumberjack

More than 3 years have passed since last update.

qiitaswiftlumberjacksample.gif

私はObjective-C時代のロギングにはCocoaLumberjackを利用していました(というか今も利用しています)。新規にSwiftベースのアプリを開発するにあたって、充実したロギング環境を実現するために、自分自身でちょっとしたヘルパークラスを作ったり、良さげなライブラリを探したりしましたが、結局、Swiftでも痒いところに手が届くCocoaLumberjackを活用することで落ち着いています。

今回は、Swift環境で新たにCocoalumberjackを利用するにあたっての、ちょっとした注意点やwebページで見かけたことがない機能について少しだけ紹介したいと思います。CocoaLumberjackの基本的な使い方については、(Objective-Cベースですが、)下記あたりが参考になるのではないかと思います。

Qiita - ログ出力ライブラリのCocoaLumberjackを使う


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を利用する場合


Podfile

platform :ios, '8.0'

use_frameworks!

target 'SwiftLumberjackSample' do
pod 'CocoaLumberjack/Swift'
end


$ pod install

いつも通り「pod install」を実行すればOKです。


Carthageを利用する場合


Cartfile

github "CocoaLumberjack/CocoaLumberjack"


$ carthage update --no-use-binaries

「no-use-binaries」オプションを付与して「carthage update」を実行する必要があります。

これは現在のCocoaLumberjackのリリースにおいて、誤ってframework.zipファイルが複数登録されているのが直接の原因で、ワークアラウンドとして必要になります。現在のところ「carthage update」コマンドによるビルド済みのframework.zipファイルのダウンロードについては1ファイルしか対応していないようです。本件に関しては、githubにおいて私もコメントしているので興味のある方は覗いてみてください。

あとはいつも通りCarthageを使う場合と同様に、生成されたCocoaLumberjack.frameworkとCocoaLumberjackSwift.frameworkをプロジェクトに追加等すればOKです。


DDDispatchQueueLogFormatter

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

DDLogFormatterとは異なりDDDispatchQueueLogFormatterでは、最初からこのスレッドセーフが考慮されているので、今後カスタムログフォーマッターを作成する場合にはDDDispatchQueueLogFormatterを利用すると便利ではないかと思います。


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)を使うことでディスパッチキューのラベルもログ表示させることができます。リアルタイムにどのスレッドが実行されているかを簡単に把握できるので便利です。


ログ出力

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」を組み合わせるとさらに便利になります。Alcatrazを使って導入しましょう。

Xcodeコンソールログ出力のフォーマットに「ファイル名.swift:行番号」を含めることにより、該当箇所へのリンクが動的に張られるようになります。上述のログフォーマッタでは\(fileName):\(lineNumber)でこれを実現しています。

冒頭のアニメーションではこの様子を表示しています。ログ出力から直接該当ファイルの行に移動できるのでデバッグがはかどります。


ログ出力レベルの変更

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


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.")
})
})
}
}



ログ出力

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候補です。一方で、もっと便利なライブラリやこんなの作ってみたよ等ありましたら、ぜひぜひ教えて下さい。みなさんでもっともっと快適なアプリ・デバッグ開発環境を目指しましょう!