O sistema de logs é um módulo fundamental do AliPlayerKit. Ele utiliza um centro de logs unificado, o LogHub, para oferecer recursos essenciais como saída de logs, filtragem por nível e mecanismo de observer. Isso garante visibilidade e rastreabilidade das informações de execução do player.
Conceitos
O que é o LogHub?
O LogHub é o ponto de entrada unificado para toda a saída de logs no AliPlayerKit. Seu design segue o padrão singleton. O LogHub encapsula a API nativa de logs do iOS, NSLog, e adiciona os seguintes recursos principais:
|
Capacidades |
Descrição |
|
Chave geral de logs |
Controla se a saída de logs está ativada (chave mestra). |
|
Chave do console |
Define se os logs são enviados ao Xcode console. |
|
Filtragem por nível |
Emite apenas logs de um nível especificado ou superior. |
|
Mecanismo de observer |
Permite que observers externos recebam a saída de logs para processamento personalizado, como gravação em arquivo. |
O que é um log observer?
Um log observer (LogObserver) é um protocolo de callback usado para receber logs do LogHub. Ao implementar esse protocolo, você personaliza o processamento dos logs. Por exemplo:
Grave logs em arquivo para que a equipe de suporte técnico os utilize na resolução de problemas.
Envie logs a um servidor remoto para viabilizar a coleta remota.
Exiba logs na interface para criar um painel de depuração.
Modelo híbrido de threads
O LogHub adota um modelo híbrido de threads:
|
Operação |
Thread |
|
Captura do nome da thread e timestamp |
Execução síncrona na thread chamadora |
|
Saída no console (NSLog) |
Execução síncrona na thread chamadora |
|
Notificação de observers |
Despacho assíncrono para uma fila serial em background |
O callback LogObserver.onLog: é executado em uma fila serial em background. Para atualizar a interface, mude para a main thread.
Recursos
Recursos principais
|
Capacidades |
Descrição |
|
Logs multinível |
Suporta cinco níveis: Verbose, Debug, Info, Warn e Error. |
|
Controle triplo |
Combina chave global, filtragem por nível e chave do console para controlar com precisão o escopo de saída. |
|
Mecanismo de observer |
Permite registrar observers personalizados para persistência de logs, relatórios remotos, entre outros. |
|
Macros auxiliares |
Oferece as macros |
|
Painel de logs |
Exibe logs em tempo real dentro da visualização do player por meio do |
|
API de atalho global |
Configure rapidamente níveis de log e saída no console usando métodos de classe do |
Componentes principais
|
Componente |
Tipo |
Descrição |
|
|
Singleton |
Centro de logs; ponto de entrada unificado para saída de logs. |
|
|
Enumeração |
Definição dos níveis de log. |
|
|
Classe de dados |
Modelo de informações de log que encapsula todos os dados de uma única entrada. |
|
|
Protocolo |
Interface do log observer. |
|
|
Slot |
Painel de logs embutido no player que exibe logs importantes em tempo real. |
Uso
Uso básico: configuração rápida global
Configure rapidamente o sistema de logs usando os métodos de classe recomendados do AliPlayerKit:
// In AppDelegate, configure after setup
[AliPlayerKit setup];
// Set the log level
[AliPlayerKit setLogLevel:LogLevelVerbose];
// Enable or disable console logs
[AliPlayerKit enableConsoleLog:YES];
|
Método do AliPlayerKit |
Descrição |
|
|
Defina o nível de log. Logs abaixo desse nível são ignorados. O padrão é |
|
|
Obtém o nível de log atual. |
|
|
Ative ou desative a saída no console. Ativado por padrão. |
|
|
Verifica se o log no console está ativado. |
|
|
Ative ou desative o modo de depuração. |
|
|
Verifica se o modo de depuração está ativado. |
|
|
Ative ou desative o slot do painel de logs. Desativado por padrão. |
|
|
Verifica se o painel de logs está ativado. |
Uso básico: saída de logs
Use a família de macros auxiliares SLOG para emitir logs em diferentes níveis. Essas macros capturam automaticamente o nome do arquivo de origem e o número da linha:
#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);
Lista de macros auxiliares:
|
Macro |
Nível |
Descrição |
|
|
Verbose |
Nível de log mais detalhado. |
|
|
Debug |
Informações de depuração. |
|
|
Info |
Informações gerais. |
|
|
Warn |
Problemas potenciais. |
|
|
Error |
Mensagens de erro. |
As macros SLOG chamam o método logLevel:file:line:message:error: do LogHub. Elas capturam automaticamente a localização no source usando __FILE_NAME__ e __LINE__, eliminando a necessidade de passar uma tag manualmente.
Também é possível chamar diretamente os métodos de instância do LogHub e especifique uma tag manualmente:
// Use a tag to identify the log source
[[LogHub sharedHub] i:@"PlayerVC" msg:@"Start playback"];
[[LogHub sharedHub] e:@"MediaPlayer" msg:@"Playback failed" error:error];
Uso básico: configuração de níveis de log
Configure as propriedades de log diretamente pelo 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
Mecanismo de controle triplo:
|
Camada de controle |
Propriedade |
Descrição |
|
Chave mestra |
|
Quando desativada, silencia todos os logs, inclusive as notificações aos observers. |
|
Filtragem por nível |
|
Logs abaixo desse nível são ignorados. O padrão é |
|
Chave do console |
|
Controla se os logs são enviados ao Xcode console via NSLog. |
Ao desativar consoleLogEnabled, os logs continuam sendo despachados pelas notificações dos observers, mas a saída no console é suprimida. Se você desativar logEnabled, todas as solicitações de log serão ignoradas e os observers não receberão notificações.
Uso avançado: registro de um log observer
Personalize o processamento de logs implementando o protocolo 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];
O LogHub mantém uma referência forte aos observers até que sejam removidos explicitamente com removeObserver:. Chame removeObserver: no momento adequado, como em dealloc ou viewWillDisappear:, para evitar referências circulares.
Uso avançado: painel de logs
O LogPanelSlot é um slot de depuração integrado que exibe informações importantes de log em tempo real na visualização do player:
// Enable the log panel (recommended only for debugging scenarios)
[AliPlayerKit setLogPanelEnabled:YES];
|
Interação |
Descrição |
|
Clique em na barra de título |
Expande ou recolhe a área de conteúdo dos logs. |
|
Clique em no botão "Clear" |
Limpa os logs exibidos. |
|
Toque longo na área de logs |
Copia o conteúdo dos logs para a área de transferência. |
O painel de logs exibe apenas logs com nível Debug ou superior. É necessário chamar setLogPanelEnabled: antes de chamar rebuildSlots. O painel não fica visível no cenário Minimal.
Desenvolvimento personalizado
Protocolo 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
Modelo de dados LogInfo
|
Propriedade |
Tipo |
Descrição |
|
|
|
Nível do log (somente leitura). |
|
|
|
Tag do log (somente leitura). |
|
|
|
Conteúdo da mensagem de log (somente leitura). |
|
|
|
Timestamp de geração do log (somente leitura). |
|
|
|
Nome da thread que gerou o log (somente leitura). |
|
|
|
Nome do arquivo de origem, como |
|
|
|
Número da linha no source (somente leitura). |
|
|
|
Objeto de erro associado, podendo ser nil (somente leitura). |
|
Método |
Descrição |
|
|
Obtém a string de log formatada. |
|
|
Inicializador designado. |
|
|
Inicializador de conveniência que captura automaticamente o timestamp e o nome da thread. |
Formato de saída formatada:
2024-04-22 10:30:00.123 I/AliPlayerKit [version] [FileName:line] [threadName] : message
Se houver um erro, uma linha extra é anexada:
Error: domain=xxx code=xxx userInfo=xxx
Exemplo: log observer personalizado para arquivo
A seguir, uma implementação completa de um log observer para arquivo:
@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
O callback onLog: é executado em uma fila serial em background. No entanto, para operações de I/O de arquivo, recomendamos usar uma fila de escrita separada para evitar o bloqueio da fila de despacho de logs. O bloqueio da fila pode afetar outros observers.
Exemplo: integração completa
// 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
O módulo PlayerKitUsages/UsageLogSystem fornece um exemplo completo de uso do sistema de logs. O exemplo inclui um FileLogObserver e uma interface para exibição dos logs.
Melhores práticas
Recomendações de configuração por ambiente
|
Ambiente |
Chave mestra |
console |
Nível |
Observer |
Painel de logs |
|
Desenvolvimento e depuração |
YES |
YES |
Verbose |
Opcional (painel de UI) |
Opcional |
|
Testes internos |
YES |
YES |
Debug |
Log em arquivo |
NO |
|
Ambiente de produção |
YES |
NO |
Info |
Log em arquivo + relatório remoto |
NO |
|
Cenários sensíveis |
NO |
— |
— |
— |
NO |
Erros comuns
Erro 1: atualize a interface diretamente no callback do observer
// ✗ 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;
});
}
Erro 2: operações de bloqueio síncronas no observer
// ✗ 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...
});
}
Erro 3: esquecer de remover o observer, causando referência circular
// ✗ 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];
}
Diferentemente do Dispatcher do sistema de notificações, o LogHub mantém uma referência forte aos observers. Se você não chamar removeObserver:, o observer não será liberado.
Referência da API
Níveis de log (LogLevel)
|
Nível |
Valor da enumeração |
Valor numérico |
Descrição |
|
Verbose |
|
0 |
Mais detalhado; destinado a desenvolvimento e depuração. |
|
Debug |
|
1 |
Informações de depuração. |
|
Info |
|
2 |
Nível padrão, recomendado para produção. |
|
Warn |
|
3 |
Problemas potenciais. |
|
Error |
|
4 |
Mensagens de erro. |
|
None |
|
100 |
Desativa todos os logs. |
O arquivo LogLevel.h também oferece a função inline LogLevelToString(), que converte um nível de log em uma string abreviada (V, D, I, W, E ou N).
API principal do LogHub
|
Propriedade / Método |
Tipo |
Descrição |
|
|
Método de classe |
Obtém a instância singleton. |
|
|
|
Chave mestra de logs (padrão: YES). |
|
|
|
Nível de log (padrão: |
|
|
|
Chave do console (padrão: YES). |
|
|
Método de instância |
Emite um log Verbose. |
|
|
Método de instância |
Emite um log Debug. |
|
|
Método de instância |
Emite um log Info. |
|
|
Método de instância |
Emite um log Warn. |
|
|
Método de instância |
Emite um log Error. |
|
|
Método de instância |
Emite um log Error com um |
|
|
Método de instância |
Emite um log no nível especificado. |
|
|
Método de instância |
Método principal de log (usado pelas macros SLOG). |
|
|
Método de instância |
Adiciona um observer (referência forte; sem efeito se adicionado repetidamente). |
|
|
Método de instância |
Remove um observer. |
|
|
Método de classe |
Converte um nível em string (V/D/I/W/E/N). |
|
|
Método de classe |
Converte uma string em nível (insensível a maiúsculas/minúsculas; retorna |
API de atalho de logs do AliPlayerKit
|
Método |
Descrição |
|
|
Defina o nível de log (deve ser chamado após |
|
|
Obtém o nível de log atual. |
|
|
Ative ou desative a saída de logs no console. |
|
|
Verifica se o log no console está ativado. |
|
|
Ative ou desative o modo de depuração. |
|
|
Verifica se o modo de depuração está ativado. |
|
|
Ative ou desative o slot do painel de logs. |
|
|
Verifica se o painel de logs está ativado. |
Macros auxiliares SLOG
|
Macro |
Nível |
Forma expandida |
|
|
Verbose |
|
|
|
Debug |
Igual à anterior, com nível Debug. |
|
|
Info |
Igual à anterior, com nível Info. |
|
|
Warn |
Igual à anterior, com nível Warn. |
|
|
Error |
Igual à anterior, com nível Error. |
Propriedades do LogInfo
|
Propriedade |
Tipo |
Descrição |
|
|
|
Nível do log. |
|
|
|
Tag do log. |
|
|
|
Conteúdo da mensagem de log. |
|
|
|
Timestamp de geração do log. |
|
|
|
Nome da thread. |
|
|
|
Nome do arquivo de origem. |
|
|
|
Número da linha no source. |
|
|
|
Objeto de erro associado (nullable). |
|
|
Método |
Obtém a string de log formatada. |
Protocolo LogObserver
|
Método |
Obrigatório |
Descrição |
|
|
Sim ( |
Callback de saída de log, executado em uma fila serial em background. |