Est-il vrai qu'il ne faut pas utiliser NSLog () sur le code de production?

155

On m'a dit cela à quelques reprises sur ce site même, mais je voulais m'assurer que c'était vraiment le cas.

Je m'attendais à pouvoir saupoudrer les appels de fonction NSLog dans tout mon code, et que Xcode / gcc supprimerait automatiquement ces appels lors de la construction de mes versions Release / Distribution.

Dois-je éviter de l'utiliser? Si oui, quelles alternatives sont les plus courantes entre les programmeurs Objective-C expérimentés?

jpm
la source
7
Je sais que cette question est maintenant très ancienne, mais, si vous le pouvez encore, je marquerais la réponse de Marc Charbonneau comme acceptée. J'ai modifié ma réponse pour pointer vers la sienne, mais sa réponse est la bonne.
e.James
5
NSLog () à l'intérieur d'une boucle fréquente tuera absolument votre performance, a-t-il dit, après l'avoir découvert à la dure.
willc2

Réponses:

197

Les macros de préprocesseur sont en effet parfaites pour le débogage. Il n'y a rien de mal avec NSLog (), mais il est simple de définir votre propre fonction de journalisation avec de meilleures fonctionnalités. Voici celui que j'utilise, il comprend le nom du fichier et le numéro de ligne pour faciliter le suivi des instructions de journal.

#define DEBUG_MODE

#ifdef DEBUG_MODE
    #define DebugLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
    #define DebugLog( s, ... ) 
#endif

J'ai trouvé plus facile de mettre toute cette déclaration dans l'en-tête du préfixe plutôt que dans son propre fichier. Vous pouvez, si vous le souhaitez, créer un système de journalisation plus complexe en faisant interagir DebugLog avec des objets Objective-C normaux. Par exemple, vous pouvez avoir une classe de journalisation qui écrit dans son propre fichier journal (ou base de données), et inclut un argument `` priorité '' que vous pouvez définir au moment de l'exécution, de sorte que les messages de débogage ne sont pas affichés dans votre version, mais les messages d'erreur sont ( si vous faisiez cela, vous pourriez créer DebugLog (), WarningLog (), et ainsi de suite).

Oh, et gardez à l'esprit qu'il #define DEBUG_MODEpeut être réutilisé à différents endroits de votre application. Par exemple, dans mon application, je l'utilise pour désactiver les vérifications de clé de licence et autoriser uniquement l'application à s'exécuter si elle est antérieure à une certaine date. Cela me permet de distribuer une version bêta entièrement fonctionnelle et limitée dans le temps avec un minimum d'effort de ma part.

Marc Charbonneau
la source
8
+1 pour une excellente réponse. J'ai changé la mienne pour indiquer que vos macros #define sont la voie à suivre, et j'espère que l'OP change la réponse acceptée (je lui ai laissé un commentaire). J'utilisais une fonction factice parce que je ne savais pas que vous pouviez utiliser ... des arguments dans une macro. Vivez et apprenez!
e.James
15
Une excellente réponse, bien que je recommande d'utiliser un préfixe personnel sur votre définition "DEBUG_MODE", comme l'appeler "JPM_DEBUG" ou autre. Trop souvent, j'ai rencontré du code tiers qui utilise également DEBUG ou DEBUG_MODE ou similaire, et parfois ce code ne fonctionnera pas correctement en mode DEBUG. Si vous souhaitez activer le débogage de bibliothèques tierces, vous devez le faire intentionnellement. (Bien sûr, ce sont les rédacteurs de bibliothèques qui devraient préfixer leurs symboles, mais de nombreux frameworks C et C ++ ne le font pas, en particulier pour cette définition).
Rob Napier
1
existe-t-il une macro prédéfinie Xcode qui peut être utilisée pour l'activer uniquement lorsque la configuration est définie sur débogage? Je préfère ne pas définir manuellement cette macro de préprocesseur moi-même dans chaque projet. pouvons-nous faire quelque chose comme le pseudocode suivant #if XCODE_CONFIGURATION == DEBUG?
frankodwyer
1
#include <TargetConditionals.h>
slf
2
Cette approche conduit à des avertissements parasites de «variables inutilisées» du compilateur en mode version lorsque les instructions de journalisation utilisent des variables intermédiaires dans le seul but de calculer les valeurs à enregistrer. Quel serait le moyen le plus intelligent d'éviter cela si vous détestez autant les avertissements du compilateur que moi?
Jean-Denis Muys
78

Mettez ces 3 lignes à la fin du fichier -prefix.pch:

#ifndef DEBUG
  #define NSLog(...) /* suppress NSLog when in release mode */
#endif

Vous n'avez pas besoin de définir quoi que ce soit dans votre projet, car il DEBUGest défini par défaut dans votre paramètre de construction lorsque vous créez votre projet.

Roel
la source
2
De loin la meilleure solution. Vous devez ajouter prefix.pch manuellement à partir de XCode 6.
Teddy
Avons-nous encore besoin de modifier les paramètres de construction avant la publication, c'est-à-dire le débogage en version
UserDev
25

Les appels NSLog peuvent être laissés dans le code de production, mais ne devraient être là que pour des cas vraiment exceptionnels ou des informations souhaitées qui seront consignées dans le journal système.

Les applications qui jonchent le journal système sont ennuyeuses et semblent non professionnelles.

Matthew Schinckel
la source
14
Pardon - paraître comme non professionnel pour qui? Qui est susceptible de vérifier vos journaux sur une application publiée et de juger votre professionnalisme en fonction de cela? (Pour être clair, je suis tout à fait d'accord que vous ne devriez pas garder une tonne de NSLogs dans la version finale de votre application, mais je suis confus par l'argument du `` professionnalisme ''.)
WendiKidd
4
Les autres développeurs feront ce que vous faites et s'énervent. Android a un problème similaire avec certains développeurs étant vraiment mauvais plus.google.com/110166527124367568225/posts/h4jK38n4XYR
Roger Binns
24

Je ne peux pas commenter la réponse de Marc Charbonneau , je vais donc l'afficher comme réponse.

En plus de l'ajout de la macro à votre en-tête pré-compilé, vous pouvez utiliser les configurations de construction Target pour contrôler la définition (ou l'absence de définition) du DEBUG_MODE.

Si vous sélectionnez " Déboguer " la configuration active, DEBUG_MODEsera définie, et la macro se développe à la NSLogdéfinition complète .

La sélection de la configuration active " Release " ne définira pas DEBUG_MODEet votre NSLogging est omis de la version de version.

Pas:

  • Cible> Obtenir des informations
  • Onglet Construire
  • Rechercher "Macros de préprocesseur" (ou GCC_PREPROCESSOR_DEFINITIONS)
  • Sélectionnez la configuration: Debug
  • Modifier la définition à ce niveau
  • Ajouter DEBUG_MODE=1
  • Sélectionnez la configuration: Release
  • confirmer DEBUG_MODEn'est pas défini dansGCC_PREPROCESSOR_DEFINITIONS

si vous omettez le caractère '=' dans la définition, vous obtiendrez une erreur du préprocesseur

Collez également ce commentaire (illustré ci-dessous) au-dessus de la définition de la macro pour vous rappeler d'où DEBUG_MACROvient la définition;)

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1
ohhorob
la source
1
C'est une réponse supplémentaire précieuse à la question. Mérite d'être plus qu'un commentaire.
morningstar
DEBUG_MODEet ne DEBUG_MACROsont pas conventionnels. Je n'ai trouvé qu'une seule référence DEBUG_MACROsur le site d'Apple ( opensource.apple.com/source/gm4/gm4-15/src/m4.h?txt ). Peut-être le plus standard DEBUGet NDEBUGserait-il de meilleurs choix? NDEBUGest spécifié par Posix; while DEBUGest utilisé par convention.
jww
+1 Oui, c'est un ancien post, mais c'est le but ... Dans ma version de Xcode (4 ans plus tard), une recherche sur GCC_PREPROCESSOR_DEFINITIONS renvoie un langage différent. Veuillez envisager de mettre à jour cette excellente réponse pour plus de clarté.
David
11

EDIT: La méthode publiée par Marc Charbonneau , et portée à mon attention par sho , est bien meilleure que celle-ci.

J'ai supprimé la partie de ma réponse qui suggérait d'utiliser une fonction vide pour désactiver la journalisation lorsque le mode débogage est désactivé. La partie qui traite de la définition d'une macro de préprocesseur automatique est toujours pertinente, elle le reste donc. J'ai également édité le nom de la macro du préprocesseur pour qu'elle corresponde mieux à la réponse de Marc Charbonneau.


Pour obtenir le comportement automatique (et attendu) dans Xcode:

Dans les paramètres du projet, allez dans l'onglet "Construire", et sélectionnez la configuration "Déboguer". Recherchez la section «Macros de préprocesseur» et ajoutez une macro nommée DEBUG_MODE.

...

EDIT: Voir la réponse de Marc Charbonneau pour la bonne façon d'activer et de désactiver la journalisation avec la DEBUG_MODEmacro.

e.James
la source
7

Je suis d'accord avec Matthew. Il n'y a rien de mal avec NSLog dans le code de production. En fait, cela peut être utile à l'utilisateur. Cela dit, si la seule raison pour laquelle vous utilisez NSLog est d'aider au débogage, alors oui, cela devrait être supprimé avant la publication.

De plus, puisque vous avez marqué cela comme une question iPhone, NSLog utilise des ressources, ce dont l'iPhone a très peu de ressources. Si vous enregistrez quelque chose sur l'iPhone, cela enlève du temps processeur à votre application. Fais-en bon usage.

août
la source
4

La simple vérité est que NSLog est tout simplement lent.

Mais pourquoi? Pour répondre à cette question, découvrons ce que fait NSLog, puis comment il le fait.

Que fait exactement NSLog?

NSLog fait 2 choses:

Il écrit les messages de journal dans la fonction Apple System Logging (asl). Cela permet aux messages du journal de s'afficher dans Console.app. Il vérifie également si le flux stderr de l'application va à un terminal (par exemple, lorsque l'application est exécutée via Xcode). Si tel est le cas, il écrit le message de journal dans stderr (pour qu'il apparaisse dans la console Xcode).

Ecrire sur STDERR ne semble pas difficile. Cela peut être accompli avec fprintf et la référence de descripteur de fichier stderr. Mais qu'en est-il de l'asl?

La meilleure documentation que j'ai trouvée sur ASL est un article de blog en 10 parties de Peter Hosey: lien

Sans entrer dans les détails, le point fort (en ce qui concerne les performances) est le suivant:

Pour envoyer un message de journal au service ASL, vous ouvrez essentiellement une connexion client au démon ASL et envoyez le message. MAIS - chaque thread doit utiliser une connexion client distincte. Par conséquent, pour être thread-safe, chaque fois que NSLog est appelé, il ouvre une nouvelle connexion client asl, envoie le message, puis ferme la connexion.

Les ressources peuvent être trouvées ici et ici .

Andrew
la source
Modifié le texte. Les ressources ne doivent être que dans le pied de page.
Johan Karlsson
2

Comme indiqué dans d'autres réponses, vous pouvez utiliser un #define pour modifier si NSLog est utilisé ou non au moment de la compilation.

Cependant, un moyen plus flexible consiste à utiliser une bibliothèque de journalisation comme Cocoa Lumberjack qui vous permet de modifier si quelque chose est également journalisé lors de l'exécution.

Dans votre code, remplacez NSLog par DDLogVerbose ou DDLogError etc., ajoutez un #import pour les définitions de macro, etc. et configurez les enregistreurs, souvent dans la méthode applicationDidFinishLaunching.

Pour avoir le même effet que NSLog, le code de configuration est

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
mmmmmm
la source
2

Du point de vue de la sécurité, cela dépend de ce qui est enregistré. Si NSLog(ou d'autres enregistreurs) écrit des informations sensibles, vous devez supprimer l'enregistreur dans le code de production.

Du point de vue de l'audit, l'auditeur ne souhaite pas examiner chaque utilisation NSLog pour s'assurer de ne pas enregistrer les informations sensibles. Il / elle vous dira simplement de retirer l'enregistreur.

Je travaille avec les deux groupes. Nous vérifions le code, écrivons les guides de codage, etc. Notre guide exige que la journalisation soit désactivée dans le code de production. Les équipes internes savent donc ne pas l'essayer;)

Nous rejetterons également une application externe qui se connecte en production, car nous ne voulons pas accepter le risque associé à une fuite accidentelle d'informations sensibles. Nous ne nous soucions pas de ce que le développeur nous dit. Il ne vaut tout simplement pas le temps d'enquêter.

Et rappelez-vous, nous définissons `` sensible '', et non le développeur;)

Je perçois également une application qui effectue beaucoup de journalisation comme une application prête à imploser. Il y a une raison pour laquelle tant de journalisation est effectuée / nécessaire, et ce n'est généralement pas la stabilité. C'est juste là-haut avec les threads de surveillance qui redémarrent les services bloqués.

Si vous n'avez jamais passé par un examen de l'architecture de sécurité (SecArch), voici le genre de choses que nous examinons.

jww
la source
1

Vous ne devriez pas être inutilement verbeux avec printf ou NSLog dans le code de version. Essayez de ne faire un printf ou NSLog que si quelque chose de mauvais lui est arrivé, c'est-à-dire une erreur irrécupérable.

MaddTheSane
la source
1

Gardez à l'esprit que les NSLogs peuvent ralentir l'interface utilisateur / le thread principal. Il est préférable de les supprimer des versions de version à moins que cela ne soit absolument nécessaire.

psy
la source
0

Je recommande fortement d'utiliser TestFlight pour la journalisation (gratuit). Leur méthode remplacera NSLog (à l'aide d'une macro) et vous permettra d'activer / désactiver la journalisation sur leur serveur, le journal système Apple et le journal STDERR, pour tous vos appels existants à NSLog. La bonne chose à ce sujet est que vous pouvez toujours consulter vos messages de journal pour les applications déployées sur les testeurs et les applications déployées dans l'App Store, sans que les journaux n'apparaissent dans le journal système de l'utilisateur. Le meilleur des deux mondes.

Joël
la source
Il faut tenir compte de la surcharge que TestFlight ajoute à l'application. Est-il possible d'ajouter uniquement la partie journalisation de TestFlight?
Johan Karlsson