Retour

[Tech] Lignes de log custom (alias de NSLog) #iOS #Objective-C

le 

Tous les développeurs connaissent l'importance du logging pour le débogage. Il facilite la recherche d'une anomalie et permet de situer rapidement sa source. Il permet par exemple de vérifier rapidement le contenu d'une donnée reçue par le réseau, dans notre cas, les retours de requête que l'on envoie sur notre architecture.
 
Deux interrogations se posent autour de cette problématique :
- Ces logs, indispensables en DEV, ralentissent l'application en PROD et dégradent de manière significative l'expérience utilisateur
- La multiplication des logs les rend très rapidement illisible, ce qui demande de la rigueur dans la rédaction de leurs libellés
 
Pour palier à ces deux problèmes nous avons fait le choix d'implémenter un système de logging adapté à nos besoins. L'idée n'est évidemment pas de réinventer la roue — le système standard proposé par le SDK iOS étant en soit très efficace — mais de se doter d'un outil simple à utiliser et garantissant lisibilité et performance.

// MFLog

La première étape a été de créer un alias de la méthode NSLog, le MFLog. De cette manière, il est très rapide de désactiver le logging au passage en PROD grâce à la suppression d'une instruction de précompilation.
 
En conséquence, étant libres de redéfinir l'alias comme nous le souhaitions, nous avons pu créer un format unique de log (sur le modèle [OBJET - FONCTIONS] LIBELLE), alimenté de manière automatique par le SDK, qui nous permet de rendre la console beaucoup plus lisible.
Voici la définition de cet alias :
#define MFDEBUG

#ifdef MFDEBUG
    #define MFLog(x,...) NSLog(@"%s - %@", __FUNCTION__, [NSString stringWithFormat:(x), ##__VA_ARGS__])
#else
    #define MFLog(x,...)
#endif
Voici un appel à la méthode de log :
    [...]
    MFLog(@"Hello GoodBarbers :) %d", i);
    [...]
Et voici un retour de console :

Anecdote : notre équipe de développement a pris l'habitude de systématiquement préfixer les noms de classes et autres méthodes utilitaires par GB (pour GoodBarber évidemment). Mais dans le cas de la commande de log custom, je l'avais mise en place bien avant le début du projet, et l'avais préfixé de mes initiales. Et c'est resté… :)

// MFImportantLog

Dans la même logique, nous avions également besoin d'un log qui ne soit pas désactivable, donc nous avons créé la variante MFImportantLog, exactement sur le même modèle. Ce log n'est jamais désactivé en PROD et est très utile pour chercher des bugs qui ne peuvent survenir en DEV.

// MEMLog

Dans notre projet iOS, nous avons fait le choix de ne pas utiliser ARC. Comme nous gérons notre mémoire "à la main", ce log nous permet à tout moment de connaître le "retainCount" d'un objet et rend ainsi le débuggage mémoire beaucoup plus aisé.
 
La mise en place de ce log est un peu plus complexe que les deux précédents. Nous avons du sous-classer un certain nombre d'objets natifs (NSObject, UIView, UIViewController, …) afin de redéfinir leurs fonctions mémoires (retain, release, init, dealloc). Ainsi, chaque fois qu'une de ces méthodes est appelées, un log indiquant le "retainCount" de l'objet est envoyé.
Ces logs sont bien sûrs activables/désactivables dans chaque objet via un simple booléen.

Voici la défintion de l'alias MEMLog qui assure une mise en forme correcte dans la sortie de la console :
#define MEMLog(x, ...) NSLog(@"-[%@ %@] - %@", [[[[NSString stringWithFormat:@"%@", self] componentsSeparatedByString:@":"] objectAtIndex:0] stringByReplacingOccurrencesOfString:@"<" withString:@""], [[[[NSString stringWithFormat:@"%s", __FUNCTION__] componentsSeparatedByString:@" "] objectAtIndex:1] stringByReplacingOccurrencesOfString:@"]" withString:@""], [NSString stringWithFormat:(x), ##__VA_ARGS__])
Objectivement, la mise en place du MEMLog est très longue pour une utilisation très ponctuelle. Mais à l'échelle d'un projet comme GoodBarber, nous ne comptons plus aujourd'hui le temps gagné au débuggage.
Conseils pour créer une app