ログシステムは、AliPlayerKit の基本的なモジュールです。統一ログセンターである LogHub を使用して、ログ出力、レベルフィルタリング、オブザーバーメカニズムなどのコア機能を提供します。これにより、プレーヤーのランタイム情報における可視性とトレーサビリティを確保します。
概念
LogHub とは
LogHub は、AliPlayerKit のすべてのログ出力に対する統一されたエントリーポイントです。シングルトンパターンで設計されています。LogHub は、ネイティブの iOS ログ API である NSLog をカプセル化し、以下のコア機能を追加します:
機能 | 説明 |
ログスイッチ | ログ出力の有効/無効を制御します (マスタースイッチ)。 |
コンソールスイッチ | Xcode コンソールへのログ出力を制御します。 |
レベルフィルタリング | 指定されたレベル以上のログのみを出力します。 |
オブザーバーメカニズム | 外部のオブザーバーがログ出力を受信し、ファイルへの書き込みなどのカスタム処理を行えるようにします。 |
ログオブザーバーとは
ログオブザーバー (LogObserver) は、LogHub からログを受信するためのコールバックプロトコルです。このプロトコルを実装することで、ログの処理方法をカスタマイズできます。例:
テクニカルサポートチームがトラブルシューティングに使用できるよう、ログをファイルに書き込みます。
リモートログ収集を有効にするために、ログをリモートサーバーにアップロードします。
デバッグパネルを作成するために、ログを UI に表示します。
ハイブリッドスレッドモデル
LogHub はハイブリッドスレッドモデルを使用します:
操作 | スレッド |
スレッド名とタイムスタンプのキャプチャ | 呼び出し側スレッドでの同期実行 |
コンソール出力 (NSLog) | 呼び出し側スレッドでの同期実行 |
オブザーバー通知 | バックグラウンドシリアルキューへの非同期ディスパッチ |
LogObserver.onLog: コールバックはバックグラウンドシリアルキューで実行されます。UI を更新するには、メインスレッドに切り替える必要があります。
機能
コア機能
機能 | 説明 |
マルチレベルロギング | Verbose、Debug、Info、Warn、Error の 5 レベルをサポートします。 |
三重制御 | グローバルスイッチ、レベルフィルタリング、コンソールスイッチにより、出力範囲を精密に制御します。 |
オブザーバーメカニズム | ログの永続化やリモートレポートなどに使用するカスタムオブザーバーを登録できます。 |
ヘルパーマクロ | ソースコードの位置情報を自動的に取得する |
ログパネル |
|
グローバルショートカット API |
|
コアコンポーネント
コンポーネント | タイプ | 説明 |
| シングルトン | ログ出力の統合エントリーポイントであるログセンターです。 |
| 列挙型 | ログレベルの定義です。 |
| データクラス | 単一のログエントリの完全な情報をカプセル化したモデルです。 |
| プロトコル | ログオブザーバーインターフェイスです。 |
| スロット | プレーヤーに組み込まれ、主要なログをリアルタイムで表示するログパネルです。 |
使用方法
基本的な使用方法:グローバルクイック設定
推奨される AliPlayerKit のクラスメソッドを使用して、ログシステムをすばやく設定できます:
// AppDelegate で、セットアップ後に設定します
[AliPlayerKit setup];
// ログレベルを設定します
[AliPlayerKit setLogLevel:LogLevelVerbose];
// コンソールログを有効または無効にします
[AliPlayerKit enableConsoleLog:YES];AliPlayerKit メソッド | 説明 |
| ログレベルを設定します。このレベル未満のログは無視されます。デフォルトは |
| 現在のログレベルを取得します。 |
| コンソール出力を有効または無効にします。デフォルトでは有効です。 |
| コンソールログが有効かどうかを確認します。 |
| デバッグモードを有効または無効にします。 |
| デバッグモードが有効かどうかを確認します。 |
| ログパネルスロットを有効または無効にします。デフォルトでは無効です。 |
| ログパネルが有効かどうかを確認します。 |
基本的な使用方法:ログ出力
SLOG シリーズのヘルパーマクロを使用して、レベルごとにログを出力できます。これらのマクロは、ソースファイル名と行番号を自動的に取得します:
#import <AliPlayerKit/LogMacros.h>
// さまざまなレベルでログを出力します (__FILE_NAME__ と __LINE__ を自動的に取得します)
SLOGV(@"viewDidLoad called");
SLOGD(@"Configure player: videoId=%@", videoId);
SLOGI(@"Start playing video: %@", videoTitle);
SLOGW(@"Buffering, current progress: %.1f%%", progress * 100);
SLOGE(@"Playback failed, error code: %ld", (long)errorCode);ヘルパーマクロの一覧:
マクロ | レベル | 説明 |
| 詳細 | 最も詳細なログレベルです。 |
| デバッグ | デバッグ情報です。 |
| 情報 | 一般的な情報です。 |
| 警告 | 潜在的な問題です。 |
| エラー | エラーメッセージです。 |
SLOG マクロは、LogHub の logLevel:file:line:message:error: メソッドを呼び出します。__FILE_NAME__ と __LINE__ を使用してソースコードの位置情報を自動的に取得するため、タグを手動で渡す必要はありません。
LogHub のインスタンスメソッドを直接呼び出し、タグを手動で指定することもできます:
// タグを使用してログの出力元を識別します
[[LogHub sharedHub] i:@"PlayerVC" msg:@"Start playback"];
[[LogHub sharedHub] e:@"MediaPlayer" msg:@"Playback failed" error:error];基本的な使用方法:ログレベルの設定
LogHub を使用して、ログプロパティを直接設定できます:
LogHub *logHub = [LogHub sharedHub];
#ifdef DEBUG
// 開発環境:詳細ログ + コンソール出力
logHub.logEnabled = YES;
logHub.consoleLogEnabled = YES;
logHub.logLevel = LogLevelVerbose;
#else
// 本番環境:簡潔なログ + コンソール出力を無効化
logHub.logEnabled = YES;
logHub.consoleLogEnabled = NO;
logHub.logLevel = LogLevelInfo;
#endif三重制御メカニズム:
制御レイヤー | プロパティ | 説明 |
マスタースイッチ |
| 無効にすると、オブザーバー通知を含むすべてのロギングが停止します。 |
レベルフィルタリング |
| このレベル未満のログは無視されます。デフォルトは |
コンソールスイッチ |
| NSLog を使用してログを Xcode コンソールに出力するかどうかを制御します。 |
consoleLogEnabled を無効にした場合も、ログはオブザーバー通知を通じてディスパッチされますが、コンソール出力は抑止されます。logEnabled を無効にすると、すべてのログ要求が無視され、オブザーバーにも通知されません。
高度な使用方法:ログオブザーバーの登録
LogObserver プロトコルを実装することで、ログ処理をカスタマイズできます:
// ファイルログオブザーバーを作成して登録します
FileLogObserver *fileObserver = [[FileLogObserver alloc] init];
[[LogHub sharedHub] addObserver:fileObserver];
// 不要になったらオブザーバーを削除します
[[LogHub sharedHub] removeObserver:fileObserver];LogHub は、removeObserver: を使用して明示的に削除されるまで、オブザーバーへの強参照を保持します。循環参照を防ぐため、dealloc や viewWillDisappear: などの適切なタイミングで removeObserver: を呼び出す必要があります。
高度な使用方法:ログパネル
LogPanelSlot は、プレーヤービュー内に主要なログ情報をリアルタイムで表示する組み込みのデバッグ用スロットです:
// ログパネルを有効にします (デバッグ用途でのみ推奨)
[AliPlayerKit setLogPanelEnabled:YES];操作 | 説明 |
タイトルバーをクリックする | ログ内容エリアを展開または折りたたみます。 |
[Clear] ボタンをクリックする | 表示中のログをクリアします。 |
ログエリアを長押しする | ログ内容をクリップボードにコピーします。 |
ログパネルに表示されるのはデバッグ以上のレベルのログのみです。setLogPanelEnabled: は rebuildSlots を呼び出す前に実行する必要があります。Minimal シナリオでは、パネルは表示されません。
カスタム開発
LogObserver プロトコル
@protocol LogObserver <NSObject>
@required
/// ログ出力コールバック
/// @param logInfo レベル、タグ、メッセージ、タイムスタンプ、スレッド名、ファイル名、行番号、およびエラー情報を含むログ情報オブジェクト。
/// @warning このコールバックはバックグラウンドシリアルキューで実行されます。UI を更新するには、メインスレッドにディスパッチしてください。
- (void)onLog:(LogInfo *)logInfo;
@endLogInfo データモデル
プロパティ | タイプ | 説明 |
|
| ログレベル (読み取り専用)。 |
|
| ログタグ (読み取り専用)。 |
|
| ログメッセージの内容 (読み取り専用)。 |
|
| ログ生成時のタイムスタンプ (読み取り専用)。 |
|
| ログを生成したスレッド名 (読み取り専用)。 |
|
| ソースファイル名。例: |
|
| ソースコードの行番号 (読み取り専用)。 |
|
| 関連付けられたエラーオブジェクト。nil の場合もあります (読み取り専用)。 |
メソッド | 説明 |
| 整形済みのログ文字列を取得します。 |
| 指定イニシャライザ。 |
| タイムスタンプ、スレッド名、ファイル名、および行番号を自動的に取得するコンビニエンスイニシャライザ。 |
整形後の出力形式:
2024-04-22 10:30:00.123 I/AliPlayerKit [version] [FileName:line] [threadName] : messageエラーがある場合は、さらに1行が追記されます:
Error: domain=xxx code=xxx userInfo=xxx例:カスタムファイルログオブザーバー
以下は、ファイルログオブザーバーの完全な実装例です:
@interface MyFileLogObserver : NSObject <LogObserver>
- (instancetype)initWithLogDirectory:(NSString *)directory;
@end
@implementation MyFileLogObserver {
NSString *_logFilePath;
dispatch_queue_t _writeQueue;
NSFileHandle *_fileHandle;
}
- (instancetype)initWithLogDirectory:(NSString *)directory {
self = [super init];
if (self) {
_writeQueue = dispatch_queue_create("com.example.filelog",
DISPATCH_QUEUE_SERIAL);
[self setupLogFileAtDirectory:directory];
}
return self;
}
- (void)setupLogFileAtDirectory:(NSString *)directory {
NSFileManager *fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath:directory]) {
[fm createDirectoryAtPath:directory
withIntermediateDirectories:YES
attributes:nil
error:nil];
}
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd";
NSString *fileName = [NSString stringWithFormat:@"player_%@.log",
[formatter stringFromDate:[NSDate date]]];
_logFilePath = [directory stringByAppendingPathComponent:fileName];
if (![fm fileExistsAtPath:_logFilePath]) {
[fm createFileAtPath:_logFilePath contents:nil attributes:nil];
}
_fileHandle = [NSFileHandle fileHandleForWritingAtPath:_logFilePath];
[_fileHandle seekToEndOfFile];
}
#pragma mark - LogObserver
- (void)onLog:(LogInfo *)logInfo {
NSString *formatted = [logInfo formattedString];
dispatch_async(_writeQueue, ^{
NSString *line = [formatted stringByAppendingString:@"\n"];
NSData *data = [line dataUsingEncoding:NSUTF8StringEncoding];
@try {
[self->_fileHandle writeData:data];
} @catch (NSException *exception) {
// 書き込み例外は無視します
}
});
}
- (void)dealloc {
[_fileHandle closeFile];
}
@endonLog: コールバックはバックグラウンドシリアルキューで実行されます。ただし、ファイル I/O 操作では、ログのディスパッチキューのブロックを避けるため、別の書き込みキューを使用することを推奨します。キューをブロックすると、他のオブザーバーに影響する可能性があります。
例:完全な統合
// AppDelegate.m
#import <AliPlayerKit/AliPlayerKit.h>
@interface AppDelegate ()
@property (nonatomic, strong) MyFileLogObserver *fileLogObserver;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1. AliPlayerKit を初期化します
[AliPlayerKit setup];
// 2. ログシステムを設定します
#ifdef DEBUG
[AliPlayerKit setLogLevel:LogLevelVerbose];
[AliPlayerKit enableConsoleLog:YES];
// 任意:ログパネルを有効にします
[AliPlayerKit setLogPanelEnabled:YES];
#else
[AliPlayerKit setLogLevel:LogLevelInfo];
[AliPlayerKit enableConsoleLog:NO];
// 3. ファイルログオブザーバーを登録します (本番環境のみ)
NSString *logDir = [NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES).firstObject
stringByAppendingPathComponent:@"PlayerLogs"];
self.fileLogObserver = [[MyFileLogObserver alloc] initWithLogDirectory:logDir];
[[LogHub sharedHub] addObserver:self.fileLogObserver];
#endif
return YES;
}
@endPlayerKitUsages/UsageLogSystem モジュールには、ログシステムの使用方法を示す完全なサンプルが用意されています。このサンプルには、FileLogObserver と、ログを表示するための UI が含まれています。
ベストプラクティス
環境設定の推奨
環境 | マスター スイッチ | コンソール | レベル | オブザーバー | ログ パネル |
開発とデバッグ | YES | YES | Verbose | オプション (UI パネル) | オプション |
内部テスト | YES | YES | Debug | ファイルロギング | NO |
本番環境 | YES | NO | Info | ファイルロギング + リモートレポーティング | NO |
センシティブなシナリオ | NO | — | — | — | NO |
一般的なエラー
エラー 1:オブザーバーコールバックでの UI の直接更新
// ✗ 誤り:onLog: はバックグラウンドシリアルキューで実行されます。UI を直接更新するとクラッシュが発生します。
- (void)onLog:(LogInfo *)logInfo {
self.logLabel.text = [logInfo formattedString]; // クラッシュ!
}
// ✓ 正解:メインスレッドに切り替えて UI を更新します。
- (void)onLog:(LogInfo *)logInfo {
NSString *formatted = [logInfo formattedString];
dispatch_async(dispatch_get_main_queue(), ^{
self.logLabel.text = formatted;
});
}エラー 2:オブザーバーでの同期ブロッキング操作
// ✗ 誤り:コールバックでファイルに同期的に書き込むと、ログディスパッチキューがブロックされます。
- (void)onLog:(LogInfo *)logInfo {
NSString *log = [logInfo formattedString];
[log writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
// ✓ 正解:ファイルに非同期で書き込みます。
- (void)onLog:(LogInfo *)logInfo {
NSString *log = [logInfo formattedString];
dispatch_async(_writeQueue, ^{
// 非同期書き込み...
});
}エラー 3:オブザーバーの削除忘れによる循環参照
// ✗ 誤り:LogHub はオブザーバーを強参照します。削除しないと循環参照が発生します。
- (void)viewDidLoad {
[[LogHub sharedHub] addObserver:self];
// 解放されません...
}
// ✓ 正解:ライフサイクルの最後にオブザーバーを削除します。
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[LogHub sharedHub] removeObserver:self];
}通知システムの Dispatcher とは異なり、LogHub はオブザーバーを強参照します。removeObserver: を呼び出さない場合、オブザーバーは解放されません。
API リファレンス
ログレベル (LogLevel)
レベル | 列挙値 | 数値 | 説明 |
詳細 |
| 0 | 最も詳細で、開発とデバッグに使用します。 |
デバッグ |
| 1 | デバッグ情報。 |
情報 |
| 2 | デフォルトレベル。本番環境に推奨されます。 |
警告 |
| 3 | 潜在的な問題。 |
エラー |
| 4 | エラーメッセージ。 |
なし |
| 100 | すべてのログを無効にします。 |
LogLevel.h は、ログレベルを略語文字列 (V、D、I、W、E、または N) に変換する LogLevelToString() インライン関数も提供します。
LogHub コア API
プロパティ/メソッド | タイプ | 説明 |
| クラスメソッド | シングルトンインスタンスを取得します。 |
|
| マスターログスイッチ (デフォルト: YES)。 |
|
| ログレベル (デフォルト: |
|
| コンソールスイッチ (デフォルト: YES)。 |
| インスタンスメソッド | 詳細ログを出力します。 |
| インスタンスメソッド | デバッグログを出力します。 |
| インスタンスメソッド | 情報ログを出力します。 |
| インスタンスメソッド | 警告ログを出力します。 |
| インスタンスメソッド | エラーログを出力します。 |
| インスタンスメソッド |
|
| インスタンスメソッド | 指定されたレベルでログを出力します。 |
| インスタンスメソッド | コアロギングメソッド ( |
| インスタンスメソッド | オブザーバーを追加します (強参照、重複して追加しても効果はありません)。 |
| インスタンスメソッド | オブザーバーを削除します。 |
| クラスメソッド | レベルを文字列 (V/D/I/W/E/N) に変換します。 |
| クラスメソッド | 文字列をレベルに変換します (大文字と小文字を区別せず、認識できない場合は |
AliPlayerKit ログショートカット API
メソッド | 説明 |
| ログレベルを設定します ( |
| 現在のログレベルを取得します。 |
| コンソールログ出力を有効または無効にします。 |
| コンソールログ出力が有効かどうかを確認します。 |
| デバッグモードを有効または無効にします。 |
| デバッグモードが有効かどうかを確認します。 |
| ログパネルスロットを有効または無効にします。 |
| ログパネルが有効かどうかを確認します。 |
SLOG ヘルパーマクロ
マクロ | レベル | 展開形 |
| 詳細 |
|
| デバッグ | 上記と同様、レベルは Debug です。 |
| 情報 | 上記と同様、レベルは Info です。 |
| 警告 | 上記と同様、レベルは Warn です。 |
| エラー | 上記と同様、レベルは Error です。 |
LogInfo プロパティ
プロパティ | タイプ | 説明 |
|
| ログレベル。 |
|
| ログタグ。 |
|
| ログメッセージの内容。 |
|
| ログが生成されたタイムスタンプ。 |
|
| スレッド名。 |
|
| ソースファイル名。 |
|
| ソースコードの行番号。 |
|
| 関連するエラーオブジェクト (nil 許容)。 |
|
| フォーマットされたログ文字列を取得します。 |
LogObserver プロトコル
メソッド | 必須 | 説明 |
| はい ( | ログ出力コールバック。バックグラウンドシリアルキューで実行されます。 |