Good By NSLog ~ CocoaLumberjack 2.x.x ~

CocoaLumberJack

iOS には CocoaLumberJack というログライブラリがあります。Java でいう Log4J のようなものです。ログの出力レベルがあり、コンソールやファイルにログを出力できます。もちろんログフォーマットも自由にカスタマイズできます。このライブラリを使うことで、アプリを実機で検証しているときにアプリクラッシュ時のログが追いやすくなり、不具合改修時の手助けになってくれることを個人的には期待しています。

ログ出力レベル

  • DDLogLevelError
  • DDLogLevelWarning
  • DDLogLevelInfo
  • DDLogLevelDebug
  • DDLogLevelVerbose
  • DDLogLevelOff

インストール

pod 'CocoaLumberjack','~> 2.0.1'

設定

import

*-Prefix.pch に書いておくのが楽です。 デバックビルドでは出力レベルを低く設定、リリースビルドでは出力しないようにしています。

#ifdef __OBJC__
    #define LOG_LEVEL_DEF ddLogLevel
    #import <CocoaLumberjack/CocoaLumberjack.h>
    #ifdef DEBUG
        static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
    #else
        static const DDLogLevel ddLogLevel = DDLogLevelOff;
    #endif
#endif

コンソール出力時のカスタムフォーマット

コンソールに吐き出す際のフォーマットをカスタマイズします。ここでは LogFormatter クラスを新規で作成。

#import <UIKit/UIKit.h>

@interface LogFormatter : NSObject <DDLogFormatter>

@end

ログレベルと、どのクラスの何行目で吐いているか出力するようにしています。       

#import "LogFormatter.h"

@implementation LogFormatter

- (NSString *)formatLogMessage:(DDLogMessage *)logMessage;
{
    NSString *logLevel;
    switch (logMessage.flag)
    {
        case DDLogFlagError     : logLevel = @"error"; break;
        case DDLogFlagWarning   : logLevel = @"warn"; break;
        case DDLogFlagInfo      : logLevel = @"info"; break;
        case DDLogFlagDebug     : logLevel = @"debug"; break;
        default                 : logLevel = @"verbose"; break;
    }
    
    NSDateFormatter *format = [[NSDateFormatter alloc] init];
    [format setDateFormat:@"yyyy/MM/dd HH:mm:ss"];
    NSString *dateTime = [format stringFromDate:[NSDate date]];
    NSString *threadID = logMessage.threadID;
    NSString *fileName = logMessage.fileName;
    NSInteger lineNumber = logMessage.line;
    NSString *message = logMessage.message;
    
    return [NSString stringWithFormat:@"%@ (%@) %@ [%@(%ld)] %@",
            dateTime, threadID, logLevel, fileName, lineNumber, message];
}

@end

AppDelegate での読み込み

AppDelegate に以下を追加。ここではコンソールとファイルに吐き出すための記述をしています。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Console Log
    DDTTYLogger *logger = [DDTTYLogger sharedInstance];
    logger.logFormatter = [[MfLogFormatter alloc] init];
    [DDLog addLogger:logger];
    
    // File Log
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentPathStr = paths[0];
    NSString *filePath = [documentPathStr stringByAppendingString:@"/MFLog"];
    DDLogFileManagerDefault *fileManager =
    [[DDLogFileManagerDefault alloc] initWithLogsDirectory:filePath];
    DDFileLogger *fileLogger = [[DDFileLogger alloc] initWithLogFileManager:fileManager];
    [DDLog addLogger:fileLogger];
    
    return YES;
}

ログ出力

適当に以下のようなログを出力します。

    DDLogError(@"error log");
    DDLogWarn(@"warn log");
    DDLogInfo(@"info log");
    DDLogDebug(@"debug log");

コンソール

先ほどのカスタムフォーマットが適用されこんな感じになります。どのクラス(SampleViewController)の何行目で吐かれたかもちゃんと表示されています。

2015/07/07 18:39:46 (381577) error [SampleViewController(182)] error log
2015/07/07 18:39:46 (381577) warn [SampleViewController(183)] warn log
2015/07/07 18:39:46 (381577) info [SampleViewController(184)] info log
2015/07/07 18:39:46 (381577) debug [SampleViewController(185)] debug log

ちなみにこれまでの NSLog だとこんな感じです。

2015-07-07 18:43:13.015 SampleProject[34394:385270] error log
2015-07-07 18:43:13.015 SampleProject[34394:385270] warn log
2015-07-07 18:43:13.016 SampleProject[34394:385270] info log
2015-07-07 18:43:13.016 SampleProject[34394:385270] debug log

ファイル

AppDelegate で /Documents 配下を指定しているので、/Documents 配下に log ファイルが配置されます。 今はデフォルト設定でファイル出力していますが、ファイル名や拡張子は好きに変更できます。

まとめ

ログの設定がだいぶ柔軟に行えるようになるかと思います。 ログ周りを整備するだけでもだいぶ開発が捗ります。