LoginSignup
2
2

More than 5 years have passed since last update.

ログを表示するNSTextViewを作成する

Posted at

概要

ログを表示するアプリを作成しました。
右側にあるOperationボタンの操作を行うと、ボタンの文字に合わせたログが表示されます。
(実際はプログラム処理の結果をログで表示するために使用します。)

押下したOperationボタンに応じてUI上にログを表示します。

[DEBUG][TRACE]に関してはログファイルにのみ表示します。(ユーザに知らせる必要のない情報のため)

また同時にappと同じ場所にログファイルを書き出しています。

Mar-20-2019 10-16-18

GitHub

コードの詳細はGitHubを参照ください。

実装-UI部分

ペタペタと部品を貼り付けます。

-w944

今回ログの表示には以下の「Text View」(NSScrollView)を使用します。

ログの見た目を揃えるため、フォントは等幅フォントを使用します。
今回はFamily名をOsakaに設定、Styleを等幅とします。

また右側のOperationの文字の下に並んでいるのは、NSButtonのMatrixです。
以下の通りIBActionを使って押下されたボタンを取得します。

NSButtonのMatrix押下時の動作
// 画面右のOperationボタンが押下された場合
- (IBAction)operationButtonPush:(NSMatrix *)sender {
    int selectedRow = (int)sender.selectedRow;
    ...
}

実装-プログラム部分

コードの先頭で必要な変数をローカル変数として定義しています。

ログレベルに関しては、以下の記事を参考にしました。
ログ設計指針

AppDelegate.m
static NSString *const kLogFileName = @"sample.log"; // 出力ログ名

// ログ表示の調節用
static NSString *const kWhiteSpaceAdjustment = @"                              ";
static NSString *const kSeparateLine = @"---------------------------------------";

// ログのレベル
// コンソール・ファイルの両方に出力
static NSString *const kLogLevel_Fatal = @"[FATAL]"; // プログラムの異常終了を伴うようなもの。
static NSString *const kLogLevel_Error = @"[ERROR]"; // 予期しないその他の実行時エラー
static NSString *const kLogLevel_Warn  = @"[WARN] "; // 廃要素となったAPIの使用、APIの不適切な使用、エラーに近い事象など。異常とは言い切れないが正常ではない予期しない問題
static NSString *const kLogLevel_Info  = @"[INFO] "; // 実行時の何らかの注目すべき事象(開始や終了など)。メッセージ内容は簡潔に止めるべき
// コンソールのみに出力
static NSString *const kLogLevel_Debug = @"[DEBUG]"; // システムの動作状況に関する詳細な情報
static NSString *const kLogLevel_Trace = @"[TRACE]"; // デバッグ情報よりも、更に詳細な情報

またバインドするオブジェクトはNSScrollViewではなくて、NSTextViewであることに注意します。

@property (unsafe_unretained) IBOutlet NSTextView *logTextView;

ログのTextViewへの書き込み部分

TextViewへの末尾への文字列追加に関しては、以下を参考にしました。
NSTextViewの末尾に文字を追加する方法

日付部分の詳細は下記が詳しい。
iPhone SDK 本体のローカライズの書式設定に合わせて日付時刻を取得する方法

/**
 @brief コメントをログビューに追加する(その際にログファイルの更新も併せて行う)
 @param message ログに表示する内容 level ログのレベル
 */
- (void)appendLogMessage:(NSString *)message logLevel:(NSString *)level {
    // ログ記録時刻
    NSDate          *logDate          = [NSDate date];
    NSDateFormatter *logDateFormatter = [[NSDateFormatter alloc] init];
    logDateFormatter.dateStyle        = NSDateFormatterMediumStyle;
    logDateFormatter.timeStyle        = NSDateFormatterMediumStyle;
    NSString        *logDateStr       = [logDateFormatter stringFromDate:logDate];
    NSMutableString *logMessage       = [NSMutableString stringWithFormat:@"%@ %@ %@\r\n", level, logDateStr, message];

    // アプリ画面のログ表示(ログレベルがDEBUG, TRACEならばUI上のログには表示しない)
    if ([level isEqualToString:kLogLevel_Fatal] ||
        [level isEqualToString:kLogLevel_Error] ||
        [level isEqualToString:kLogLevel_Warn]  ||
        [level isEqualToString:kLogLevel_Info] ) {
        [_logTextView setEditable:YES];
        [_logTextView setSelectedRange: NSMakeRange(-1, 0)]; // 文末を選択
        [_logTextView insertText:logMessage replacementRange:NSMakeRange(-1, 0)];    // 末尾にログを追加
        [_logTextView setEditable:NO];
    }

    // ログファイルの更新
    if (![self updateLogFileWithMessage:logMessage]) {
        NSString *message = @"ログの出力時にエラーが発生しました。";
        NSMutableString *logMessage = [NSMutableString stringWithFormat:@"%@ %@ %@\r\n", kLogLevel_Error, logDateStr, message];
        [_logTextView setEditable:YES];
        [_logTextView setSelectedRange: NSMakeRange(-1, 0)]; // 文末を選択
        [_logTextView insertText:logMessage replacementRange:NSMakeRange(-1, 0)];    // 末尾にログを追加
        [_logTextView setEditable:NO];
    }
}

ログファイルの更新

既存のログファイルの中身を読み取り、それにテキストを追加して上書き保存を行います。

/**
 @brief ログファイルの更新を行う
 @param addedMessage ログに追加する文字列
 */
- (BOOL)updateLogFileWithMessage:(NSString *)addedMessage {
    // appと同じ場所にログファイルを書き出す
    NSURL   *bundleURL  = [NSURL fileURLWithPath:[NSBundle.mainBundle.bundlePath stringByDeletingLastPathComponent]];
    NSURL   *logFileURL = [bundleURL URLByAppendingPathComponent:kLogFileName];
    NSError *error      = nil;

    NSString *newLogMessage = [NSString string];   // 更新後のログメッセージ

    // 既存のログファイル読み込み
    NSString *oldLogMessage = [[NSString alloc] initWithContentsOfURL:logFileURL
                                                             encoding:NSUTF8StringEncoding
                                                                error:&error];
    if (oldLogMessage.length == 0) {
        newLogMessage = [NSString stringWithString:addedMessage];
    } else {
        newLogMessage = [oldLogMessage stringByAppendingString:addedMessage];
    }
    // 外部ログファイルに出力
    if (![newLogMessage writeToURL:logFileURL
                         atomically:YES
                           encoding:NSUTF8StringEncoding
                              error:&error]) {
        NSLog(@"%@", error.localizedDescription);
        return NO;
    }
    return YES;
}
出力されるログ
[INFO]  Mar 20, 2019 20:14:14 ---------------------------------------
[INFO]  Mar 20, 2019 20:14:14 *** アプリケーションが起動しました ***
[FATAL] Mar 20, 2019 20:14:18 Operationが実行されました。
[ERROR] Mar 20, 2019 20:14:19 Operationが実行されました。
[WARN]  Mar 20, 2019 20:14:20 Operationが実行されました。
[INFO]  Mar 20, 2019 20:14:21 Operationが実行されました。
[DEBUG] Mar 20, 2019 20:14:22 Operationが実行されました。
[TRACE] Mar 20, 2019 20:14:23 Operationが実行されました。
[ERROR] Mar 20, 2019 20:14:23 Operationを実行します。
                              長いログが出力されています…
                              長いログが出力されています…
                              長いログが出力されています…
                              Operationが終了しました。
[INFO]  Mar 20, 2019 20:14:25 *** アプリケーションを終了します… ***
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2