Le système de journalisation est un module fondamental d'AliPlayerKit. Il s'appuie sur un centre de journalisation unifié, LogHub, pour offrir des fonctionnalités essentielles telles que la sortie des journaux, le filtrage par niveau et un mécanisme d'observateur. Cela garantit la visibilité et la traçabilité des informations d'exécution du lecteur.
Concepts
Qu'est-ce que LogHub ?
LogHub constitue le point d'entrée unique pour toutes les sorties de journal dans AliPlayerKit. Conçu selon le modèle singleton, LogHub encapsule l'API de journalisation native d'iOS, NSLog, et ajoute les fonctionnalités principales suivantes :
|
Fonctionnalités |
Description |
|
Interrupteur de journalisation |
Contrôle l'activation ou la désactivation de la sortie des journaux (interrupteur principal). |
|
Interrupteur de console |
Contrôle l'affichage des journaux dans la console Xcode. |
|
Filtrage par niveau |
Affiche uniquement les journaux dont le niveau est égal ou supérieur au niveau spécifié. |
|
Mécanisme d'observateur |
Permet aux observateurs externes de recevoir les sorties de journal pour un traitement personnalisé, comme l'écriture dans un fichier. |
Qu'est-ce qu'un observateur de journal ?
Un observateur de journal (LogObserver) est un protocole de rappel utilisé pour recevoir les journaux depuis LogHub. En implémentant ce protocole, vous pouvez personnaliser le traitement des journaux. Par exemple :
Écrire les journaux dans un fichier afin que l'équipe de support technique puisse les utiliser pour le dépannage.
Télécharger les journaux vers un serveur distant pour activer la collecte à distance.
Afficher les journaux dans l'interface utilisateur pour créer un panneau de débogage.
Modèle de threads hybride
LogHub utilise un modèle de threads hybride :
|
Opération |
Thread |
|
Capture du nom du thread et de l'horodatage |
Exécution synchrone dans le thread appelant |
|
Sortie console (NSLog) |
Exécution synchrone dans le thread appelant |
|
Notification des observateurs |
Distribution asynchrone vers une file d'attente série en arrière-plan |
Le rappel LogObserver.onLog: s'exécute dans une file d'attente série en arrière-plan. Vous devez basculer vers le thread principal pour mettre à jour l'interface utilisateur.
Fonctionnalités
Fonctionnalités principales
|
Fonctionnalités |
Description |
|
Journalisation multiniveau |
Prend en charge cinq niveaux : Verbose, Debug, Info, Warn et Error. |
|
Triple contrôle |
Interrupteur global, filtrage par niveau et interrupteur de console pour un contrôle précis de la portée de sortie. |
|
Mécanisme d'observateur |
Vous permet d'enregistrer des observateurs personnalisés pour la persistance des journaux, la remontée à distance, etc. |
|
Macros d'aide |
Fournit les macros |
|
Panneau de journalisation |
Affiche les journaux en temps réel dans la vue du lecteur à l'aide de |
|
API de raccourci global |
Configurez rapidement les niveaux de journalisation et la sortie console à l'aide des méthodes de classe |
Composants principaux
|
Composant |
Type |
Description |
|
|
Singleton |
Centre de journalisation, point d'entrée unique pour la sortie des journaux. |
|
|
Énumération |
Définition du niveau de journalisation. |
|
|
Classe de données |
Modèle d'informations de journal qui encapsule toutes les informations d'une entrée de journal unique. |
|
|
Protocole |
Interface de l'observateur de journal. |
|
|
Emplacement (Slot) |
Panneau de journalisation intégré au lecteur qui affiche les journaux clés en temps réel. |
Utilisation
Utilisation de base : configuration rapide globale
Vous pouvez configurer rapidement le système de journalisation à l'aide des méthodes de classe recommandées AliPlayerKit :
// In AppDelegate, configure after setup
[AliPlayerKit setup];
// Set the log level
[AliPlayerKit setLogLevel:LogLevelVerbose];
// Enable or disable console logs
[AliPlayerKit enableConsoleLog:YES];
|
Méthode AliPlayerKit |
Description |
|
|
Définit le niveau de journalisation. Les journaux inférieurs à ce niveau sont ignorés. La valeur par défaut est |
|
|
Récupère le niveau de journalisation actuel. |
|
|
Active ou désactive la sortie console. Activé par défaut. |
|
|
Vérifie si la journalisation console est activée. |
|
|
Active ou désactive le mode débogage. |
|
|
Vérifie si le mode débogage est activé. |
|
|
Active ou désactive l'emplacement du panneau de journalisation. Désactivé par défaut. |
|
|
Vérifie si le panneau de journalisation est activé. |
Utilisation de base : sortie de journal
Utilisez les macros d'aide de la série SLOG pour générer des journaux à différents niveaux. Ces macros capturent automatiquement le nom du fichier source et le numéro de ligne :
#import <AliPlayerKit/LogMacros.h>
// Output logs at different levels (automatically captures __FILE_NAME__ and __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);
Liste des macros d'aide :
|
Macro |
Niveau |
Description |
|
|
Verbose |
Niveau de journal le plus détaillé. |
|
|
Debug |
Informations de débogage. |
|
|
Info |
Informations générales. |
|
|
Warn |
Problèmes potentiels. |
|
|
Error |
Messages d'erreur. |
Les macros SLOG appellent la méthode LogHub logLevel:file:line:message:error:. Elles capturent automatiquement l'emplacement du code source à l'aide de __FILE_NAME__ et __LINE__, vous n'avez donc pas besoin de transmettre manuellement un tag.
Vous pouvez également appeler directement les méthodes d'instance de LogHub et spécifier manuellement un tag :
// Use a tag to identify the log source
[[LogHub sharedHub] i:@"PlayerVC" msg:@"Start playback"];
[[LogHub sharedHub] e:@"MediaPlayer" msg:@"Playback failed" error:error];
Utilisation de base : configuration des niveaux de journalisation
Configurez les propriétés de journalisation directement via LogHub :
LogHub *logHub = [LogHub sharedHub];
#ifdef DEBUG
// Development environment: Verbose logs + console output
logHub.logEnabled = YES;
logHub.consoleLogEnabled = YES;
logHub.logLevel = LogLevelVerbose;
#else
// Production environment: Concise logs + console disabled
logHub.logEnabled = YES;
logHub.consoleLogEnabled = NO;
logHub.logLevel = LogLevelInfo;
#endif
Mécanisme de triple contrôle :
|
Couche de contrôle |
Propriété |
Description |
|
Interrupteur principal |
|
Lorsqu'il est désactivé, toute la journalisation est silencieuse, y compris les notifications aux observateurs. |
|
Filtrage par niveau |
|
Les journaux inférieurs à ce niveau sont ignorés. La valeur par défaut est |
|
Interrupteur de console |
|
Contrôle l'affichage des journaux dans la console Xcode via NSLog. |
Si vous désactivez consoleLogEnabled, les journaux sont toujours distribués via les notifications aux observateurs, mais la sortie console est supprimée. Si vous désactivez logEnabled, toutes les demandes de journalisation sont ignorées et les observateurs ne reçoivent aucune notification.
Utilisation avancée : enregistrer un observateur de journal
Personnalisez le traitement des journaux en implémentant le protocole LogObserver :
// Create and register a file log observer
FileLogObserver *fileObserver = [[FileLogObserver alloc] init];
[[LogHub sharedHub] addObserver:fileObserver];
// Remove the observer when it is no longer needed
[[LogHub sharedHub] removeObserver:fileObserver];
LogHub conserve une référence forte aux observateurs jusqu'à ce qu'ils soient explicitement supprimés à l'aide de removeObserver:. Vous devez appeler removeObserver: au moment approprié, par exemple dans dealloc ou viewWillDisappear:, pour éviter les références circulaires.
Utilisation avancée : panneau de journalisation
LogPanelSlot est un emplacement de débogage intégré qui affiche les informations de journal clés en temps réel dans la vue du lecteur :
// Enable the log panel (recommended only for debugging scenarios)
[AliPlayerKit setLogPanelEnabled:YES];
|
Interaction |
Description |
|
Cliquez sur la barre de titre |
Développe ou réduit la zone de contenu des journaux. |
|
Cliquez sur le bouton « Clear » |
Efface les journaux affichés. |
|
Appuyez longuement sur la zone de journalisation |
Copie le contenu du journal dans le presse-papiers. |
Le panneau de journalisation affiche uniquement les journaux dont le niveau est Debug ou supérieur. Vous devez appeler setLogPanelEnabled: avant d'appeler rebuildSlots. Le panneau n'est pas visible dans le scénario Minimal.
Développement personnalisé
Protocole LogObserver
@protocol LogObserver <NSObject>
@required
/// Log output callback
/// @param logInfo The log information object, which contains the level, tag, message, and timestamp.
/// @warning This callback is executed in a background serial queue. Dispatch to the main thread to update the UI.
- (void)onLog:(LogInfo *)logInfo;
@end
Modèle de données LogInfo
|
Propriété |
Type |
Description |
|
|
|
Niveau de journalisation (lecture seule). |
|
|
|
Tag de journal (lecture seule). |
|
|
|
Contenu du message de journal (lecture seule). |
|
|
|
Horodatage de génération du journal (lecture seule). |
|
|
|
Nom du thread ayant généré le journal (lecture seule). |
|
|
|
Nom du fichier source, par exemple |
|
|
|
Numéro de ligne du code source (lecture seule). |
|
|
|
Objet d'erreur associé, pouvant être nil (lecture seule). |
|
Méthode |
Description |
|
|
Récupère la chaîne de journal formatée. |
|
|
Initialiseur désigné. |
|
|
Initialiseur de commodité qui capture automatiquement l'horodatage et le nom du thread. |
Format de sortie formaté :
2024-04-22 10:30:00.123 I/AliPlayerKit [version] [FileName:line] [threadName] : message
Si une erreur existe, une ligne supplémentaire est ajoutée :
Error: domain=xxx code=xxx userInfo=xxx
Exemple : observateur de journal personnalisé pour fichier
Voici une implémentation complète d'un observateur de journal pour fichier :
@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) {
// Ignore write exceptions
}
});
}
- (void)dealloc {
[_fileHandle closeFile];
}
@end
Le rappel onLog: s'exécute dans une file d'attente série en arrière-plan. Toutefois, pour les opérations d'E/S de fichiers, nous vous recommandons d'utiliser une file d'attente d'écriture distincte afin d'éviter de bloquer la file d'attente de distribution des journaux. Un blocage de la file d'attente pourrait affecter d'autres observateurs.
Exemple : intégration complète
// AppDelegate.m
#import <AliPlayerKit/AliPlayerKit.h>
@interface AppDelegate ()
@property (nonatomic, strong) MyFileLogObserver *fileLogObserver;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1. Initialize AliPlayerKit
[AliPlayerKit setup];
// 2. Configure the log system
#ifdef DEBUG
[AliPlayerKit setLogLevel:LogLevelVerbose];
[AliPlayerKit enableConsoleLog:YES];
// Optional: Enable the log panel
[AliPlayerKit setLogPanelEnabled:YES];
#else
[AliPlayerKit setLogLevel:LogLevelInfo];
[AliPlayerKit enableConsoleLog:NO];
// 3. Register a file log observer (production environment only)
NSString *logDir = [NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES).firstObject
stringByAppendingPathComponent:@"PlayerLogs"];
self.fileLogObserver = [[MyFileLogObserver alloc] initWithLogDirectory:logDir];
[[LogHub sharedHub] addObserver:self.fileLogObserver];
#endif
return YES;
}
@end
Le module PlayerKitUsages/UsageLogSystem fournit un exemple complet d'utilisation du système de journalisation. L'exemple inclut un FileLogObserver et une interface utilisateur pour l'affichage des journaux.
Bonnes pratiques
Recommandations de configuration par environnement
|
Environnement |
Interrupteur principal |
Console |
Niveau |
Observateur |
Panneau de journalisation |
|
Développement et débogage |
OUI |
OUI |
Verbose |
Facultatif (panneau UI) |
Facultatif |
|
Tests internes |
OUI |
OUI |
Debug |
Journalisation dans un fichier |
NON |
|
Environnement de production |
OUI |
NON |
Info |
Journalisation dans un fichier + rapport à distance |
NON |
|
Scénarios sensibles |
NON |
— |
— |
— |
NON |
Erreurs courantes
Erreur 1 : Mise à jour directe de l'interface utilisateur dans le rappel de l'observateur
// ✗ Incorrect: onLog: is executed in a background serial queue. Updating the UI directly will cause a crash.
- (void)onLog:(LogInfo *)logInfo {
self.logLabel.text = [logInfo formattedString]; // Crash!
}
// ✓ Correct: Switch to the main thread to update the UI.
- (void)onLog:(LogInfo *)logInfo {
NSString *formatted = [logInfo formattedString];
dispatch_async(dispatch_get_main_queue(), ^{
self.logLabel.text = formatted;
});
}
Erreur 2 : Opérations de blocage synchrones dans l'observateur
// ✗ Incorrect: Synchronously writing to a file in the callback blocks the log dispatch queue.
- (void)onLog:(LogInfo *)logInfo {
NSString *log = [logInfo formattedString];
[log writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
// ✓ Correct: Asynchronously write to the file.
- (void)onLog:(LogInfo *)logInfo {
NSString *log = [logInfo formattedString];
dispatch_async(_writeQueue, ^{
// Asynchronous write...
});
}
Erreur 3 : Oubli de suppression de l'observateur, entraînant une référence circulaire
// ✗ Incorrect: LogHub strongly references the observer. Failure to remove it causes a circular reference.
- (void)viewDidLoad {
[[LogHub sharedHub] addObserver:self];
// Never released...
}
// ✓ Correct: Remove the observer at the end of the lifecycle.
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[LogHub sharedHub] removeObserver:self];
}
Contrairement au Dispatcher du système de notification, LogHub conserve une référence forte aux observateurs. Si vous n'appelez pas removeObserver:, l'observateur n'est pas libéré.
Référence API
Niveaux de journalisation (LogLevel)
|
Niveau |
Valeur d'énumération |
Valeur numérique |
Description |
|
Verbose |
|
0 |
Le plus détaillé, destiné au développement et au débogage. |
|
Debug |
|
1 |
Informations de débogage. |
|
Info |
|
2 |
Niveau par défaut, recommandé pour la production. |
|
Warn |
|
3 |
Problèmes potentiels. |
|
Error |
|
4 |
Messages d'erreur. |
|
None |
|
100 |
Désactive tous les journaux. |
LogLevel.h fournit également la fonction inline LogLevelToString(), qui convertit un niveau de journalisation en une chaîne abrégée (V, D, I, W, E ou N).
API principale de LogHub
|
Propriété / Méthode |
Type |
Description |
|
|
Méthode de classe |
Récupère l'instance singleton. |
|
|
|
Interrupteur principal de journalisation (par défaut : OUI). |
|
|
|
Niveau de journalisation (par défaut : |
|
|
|
Interrupteur de console (par défaut : OUI). |
|
|
Méthode d'instance |
Génère un journal Verbose. |
|
|
Méthode d'instance |
Génère un journal Debug. |
|
|
Méthode d'instance |
Génère un journal Info. |
|
|
Méthode d'instance |
Génère un journal Warn. |
|
|
Méthode d'instance |
Génère un journal Error. |
|
|
Méthode d'instance |
Génère un journal Error avec un |
|
|
Méthode d'instance |
Génère un journal au niveau spécifié. |
|
|
Méthode d'instance |
Méthode de journalisation principale (utilisée par les macros SLOG). |
|
|
Méthode d'instance |
Ajoute un observateur (référence forte, aucun effet en cas d'ajout répété). |
|
|
Méthode d'instance |
Supprime un observateur. |
|
|
Méthode de classe |
Convertit un niveau en chaîne (V/D/I/W/E/N). |
|
|
Méthode de classe |
Convertit une chaîne en niveau (insensible à la casse, renvoie |
API de raccourci de journalisation AliPlayerKit
|
Méthode |
Description |
|
|
Définit le niveau de journalisation (doit être appelé après |
|
|
Récupère le niveau de journalisation actuel. |
|
|
Active ou désactive la sortie des journaux dans la console. |
|
|
Vérifie si la journalisation console est activée. |
|
|
Active ou désactive le mode débogage. |
|
|
Vérifie si le mode débogage est activé. |
|
|
Active ou désactive l'emplacement du panneau de journalisation. |
|
|
Vérifie si le panneau de journalisation est activé. |
Macros d'aide SLOG
|
Macro |
Niveau |
Forme développée |
|
|
Verbose |
|
|
|
Debug |
Identique ci-dessus, avec le niveau Debug. |
|
|
Info |
Identique ci-dessus, avec le niveau Info. |
|
|
Warn |
Identique ci-dessus, avec le niveau Warn. |
|
|
Error |
Identique ci-dessus, avec le niveau Error. |
Propriétés LogInfo
|
Propriété |
Type |
Description |
|
|
|
Niveau de journalisation. |
|
|
|
Tag de journal. |
|
|
|
Contenu du message de journal. |
|
|
|
Horodatage de génération du journal. |
|
|
|
Nom du thread. |
|
|
|
Nom du fichier source. |
|
|
|
Numéro de ligne du code source. |
|
|
|
Objet d'erreur associé (nullable). |
|
|
Méthode |
Récupère la chaîne de journal formatée. |
Protocole LogObserver
|
Méthode |
Obligatoire |
Description |
|
|
Oui ( |
Rappel de sortie de journal, exécuté dans une file d'attente série en arrière-plan. |