Чи правда, що не слід використовувати NSLog () у виробничому коді?


155

Мені про це говорили кілька разів на цьому самому сайті, але я хотів переконатися, що це дійсно так.

Я очікував, що зможу розпорошувати виклики функцій NSLog у всьому моєму коді, і що Xcode / gcc автоматично зніме ці виклики під час створення моїх версій / дистрибутивів.

Чи слід уникати використання цього? Якщо так, то які альтернативи є найбільш поширеними між досвідченими програмістами Objective-C?


7
Я знаю, що це питання зараз дуже давнє, але, якщо ви все ще можете, я би відзначив відповідь Марка Чарбоно як прийняту. Я змінив свою відповідь, щоб вказати на його, але його відповідь правильна.
e.James

5
NSLog () всередині частого циклу абсолютно вбиває вашу виставу, сказав він, дізнавшись важкий шлях.
willc2

Відповіді:


197

Препроцесорні макроси справді чудові для налагодження. З NSLog () немає нічого поганого, але просто визначити власну функцію журналу з кращою функціональністю. Ось який я використовую, він включає ім'я файлу та номер рядка для полегшення відстеження виписок журналу.

#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

Мені було легше помістити весь цей вислів у заголовок префікса, а не у його власний файл. Ви можете, якщо хочете, створити більш складну систему ведення журналів, встановивши DebugLog взаємодію із звичайними об'єктами Objective-C. Наприклад, у вас може бути клас реєстрації, який записує у свій власний файл журналу (або базу даних) і включає аргумент "пріоритет", який ви могли встановити під час виконання, тому повідомлення про налагодження не відображаються у версії випуску, але повідомлення про помилки ( якщо ви це зробили, ви можете зробити DebugLog (), WarningLog () тощо).

О, і майте на увазі, що #define DEBUG_MODEїх можна повторно використовувати в різних місцях вашої програми. Наприклад, у своїй програмі я використовую його для відключення перевірок ліцензійних ключів і дозволяю запускати програму лише у тому випадку, коли вона закінчується до певної дати. Це дозволяє мені поширювати обмежену часом повністю функціональну бета-копію з мінімальними зусиллями з мого боку.


8
+1 за відмінну відповідь. Я змінив свою, щоб вказати, що ваші #define макроси - це шлях, і я сподіваюся, що ОП перемикає прийняту відповідь (я залишив йому коментар). Я використовував фіктивну функцію, оскільки я не знав, що ви можете використовувати ... аргументи в макросі. Живи та навчайся!
e.James

15
Відмінна відповідь, хоча я рекомендую використовувати особистий префікс у вашому "DEBUG_MODE" визначенні, наприклад, називати його "JPM_DEBUG" тощо. Занадто часто я стикався з стороннім кодом, який також використовує DEBUG або DEBUG_MODE тощо, і іноді цей код не працює належним чином у режимі DEBUG. Якщо ви хочете ввімкнути налагодження сторонніх бібліотек, вам слід це зробити навмисно. (Звичайно, саме бібліотечні автори повинні префіксувати свої символи, але багато C і C ++ рамок цього не роблять, особливо для цього визначають).
Роб Нап'єр

1
чи є макрос заздалегідь визначений Xcode, який можна використовувати для включення цього лише тоді, коли для конфігурації встановлено налагодження? Я б краще не вручну встановлював цей макрос препроцесора в кожному проекті. чи можемо ми зробити щось на кшталт наступного псевдокоду #if XCODE_CONFIGURATION == DEBUG?
frankodwyer

1
#include <TargetConditionals.h>
slf

2
Цей підхід призводить до помилкових попереджень "невикористаних змінних" від компілятора в режимі випуску, коли оператори журналу використовують посередницькі змінні з єдиною метою обчислення значень для журналу. Який був би найрозумніший спосіб уникнути цього, якщо ви ненавидите попередження компілятора так само, як і я?
Жан-Деніс Муйс

78

Покладіть ці 3 рядки в кінець файлу -prefix.pch:

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

Вам не потрібно нічого визначати у своєму проекті, оскільки DEBUGце визначено у налаштуваннях збірки за замовчуванням під час створення проекту.


2
Далеко найкраще рішення. Вам потрібно додати prefix.pch вручну з XCode 6.
Тедді

Ще нам потрібно змінити налаштування збірки перед випуском, тобто налагодження до випуску
UserDev

25

Виклики NSLog можна залишити у виробничому коді, але вони повинні бути там лише для справді виняткових випадків або інформації, яка бажається, що буде внесена до системного журналу.

Програми, які засмічують системний журнал, дратують і трапляються як непрофесійні.


14
Вибачте - натрапите на кого непрофесійно? Хто, ймовірно, перевірятиме ваші журнали у випущеному додатку та оцінює ваш професіоналізм на основі цього? (Щоб було зрозуміло, я повністю погоджуюся, що ви не повинні тримати тонну NSLogs у версії випуску вашої заявки, але мене бентежить аргумент "професіоналізму".)
WendiKidd

4
Інші розробники будуть робити те, що ви робите, і роздратуєтесь. Android має подібну проблему, оскільки деякі розробники дуже погані. Plus.google.com/110166527124367568225/posts/h4jK38n4XYR
Роджер Біннс

24

Я не можу коментувати відповідь Марка Чарбоно , тому опублікую це як відповідь.

Крім додавання макросу до заздалегідь складеного заголовка, ви можете використовувати конфігурації збірки Target для управління визначаючим (або відсутністю визначення) DEBUG_MODE.

Якщо ви виберете " Налагодити " активну конфігурацію, DEBUG_MODEбуде визначено, і макрос розшириться до повного NSLogвизначення.

Вибір активної конфігурації " Випустити " не визначатиметься, DEBUG_MODEа ваш NSLogging буде випущено з версії версії

Кроки:

  • Ціль> Отримати інформацію
  • Вкладка "Збірка"
  • Шукати "Макроси препроцесора" (або GCC_PREPROCESSOR_DEFINITIONS)
  • Виберіть Конфігурація: Налагодження
  • Змініть визначення на цьому рівні
  • Додайте DEBUG_MODE=1
  • Виберіть Конфігурація: Відпустіть
  • підтвердження DEBUG_MODEне встановленоGCC_PREPROCESSOR_DEFINITIONS

якщо опустити символ '=' у визначенні, ви отримаєте помилку від препроцесора

Також вставте цей коментар (показаний нижче) над визначенням макросу, щоб нагадати, звідки DEBUG_MACROпоходить визначення;)

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1

1
Це цінна додаткова відповідь на питання. Заслуговує бути більше, ніж коментар.
ранкова

DEBUG_MODEі DEBUG_MACROє нетрадиційними. Я знайшов лише одне посилання DEBUG_MACROна сайт apple ( opensource.apple.com/source/gm4/gm4-15/src/m4.h?txt ). Можливо, більш стандартний DEBUGі NDEBUGкращий вибір? NDEBUGзадається Posix; при цьому DEBUGвикористовується умовно.
jww

+1 Так, це стара публікація, але в цьому справа ... У моїй версії Xcode (4 роки потому) пошук GCC_PREPROCESSOR_DEFINITIONS повертає іншу мову. Будь ласка, подумайте про оновлення цієї чудової відповіді для наочності.
Девід

11

EDIT: метод відправлений Марком Шарбонно , і звернув мою увагу на шо , набагато краще , ніж цей.

Я видалив частину своєї відповіді, яка запропонувала використовувати порожню функцію для відключення журналу, коли режим налагодження вимкнено. Частина, що стосується встановлення автоматичного макроса препроцесора, залишається актуальною, тому вона залишається. Я також відредагував ім'я макроса препроцесора, щоб воно краще відповідало відповіді Марка Чарбоно.


Для досягнення автоматичної (і очікуваної) поведінки в Xcode:

У налаштуваннях проекту перейдіть на вкладку «Збірка» та виберіть конфігурацію «Налагодження». Знайдіть розділ «Макроси препроцесора» та додайте макрос з назвою DEBUG_MODE.

...

РЕДАКТУВАННЯ: Дивіться відповідь Марка Шарбонно про правильний спосіб увімкнення та вимкнення журналу за допомогою DEBUG_MODEмакроса.


7

Я погоджуюся з Метью. Нічого поганого в NSLog у виробничому коді. Насправді це може бути корисно користувачеві. Це означає, що якщо ви використовуєте NSLog єдиною причиною, щоб допомогти налагоджувати, то, так, це слід видалити перед випуском.

Крім того, оскільки ви позначили це як питання iPhone, NSLog забирає ресурси, а це iPhone, що мало дорогоцінно. Якщо ви займаєтесь NSLogging що- небудь на iPhone, це забирає час роботи процесора у вашої програми. Використовуйте його розумно.


4

Проста правда полягає в тому, що NSLog просто повільний.

Але чому? Щоб відповісти на це питання, давайте дізнаємося, що робить NSLog, а потім як це робити.

Що саме робить NSLog?

NSLog робить 2 речі:

Він записує журнали повідомлень у систему Apple System Logging (asl). Це дозволяє відображати повідомлення журналу в Console.app. Він також перевіряє, чи надходить stderr потік програми до терміналу (наприклад, коли програма запущена через Xcode). Якщо так, він записує повідомлення журналу в stderr (щоб воно відображалося на консолі Xcode).

Написання в STDERR не звучить складно. Це можна досягти за допомогою fprintf та посилання на дескриптор файлу stderr. А як щодо Асл?

Найкраща документація, яку я знайшла про ASL, - це 10-часова публікація в блозі від Пітера Хосі: посилання

Не вдаючись до занадто багато деталей, головним моментом (що стосується продуктивності) є таке:

Щоб надіслати повідомлення журналу до об'єкту ASL, ви в основному відкриваєте підключення клієнта до демона ASL і відправляєте повідомлення. АЛЕ - кожен потік повинен використовувати окреме клієнтське з'єднання. Отже, щоб бути безпечним для потоку, кожен раз, коли викликається NSLog, він відкриває нове з'єднання клієнта asl, відправляє повідомлення і потім закриває з'єднання.

Ресурси можна знайти тут і тут .


Відредагував текст. Ресурси повинні бути лише в колонтитулі.
Йохан Карлссон

2

Як зазначено в інших відповідях, ви можете використовувати #define, щоб змінити, чи використовується NSLog під час компіляції чи ні.

Однак більш гнучким способом є використання бібліотеки лісозаготівлі, наприклад, Cocoa Lumberjack, що дозволяє вам змінювати, чи щось також входить під час виконання.

У своєму коді замініть NSLog DDLogVerbose або DDLogError тощо, додайте #import для визначення макросів тощо і налаштуйте реєстратори, часто методом applicationDidFinishLaunching.

Щоб мати той самий ефект, що і NSLog, є конфігураційний код

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];

2

З точки зору безпеки, це залежить від того, що реєструється. Якщо NSLog(або інші реєстратори) записують конфіденційну інформацію, слід видалити реєстратор у виробничому коді.

З точки зору аудиту, аудитор не хоче дивитись на кожне використання, NSLogщоб переконатися, що його не зберігається конфіденційна інформація. Він / вона просто скаже вам зняти лісоруб.

Я працюю з обома групами. Ми перевіряємо код, пишемо посібники з кодування тощо. У нашому посібнику потрібно вимкнути журнал у виробничому коді. Тож внутрішні команди знають, що не пробувати;)

Ми також відхилимо зовнішнє додаток, яке реєструється у виробництві, оскільки ми не хочемо приймати ризик, пов’язаний із випадковим витоком конфіденційної інформації. Нам все одно, що нам каже розробник. Нашого часу просто не варто досліджувати.

І пам’ятайте, ми визначаємо "чутливий", а не розробник;)

Я також сприймаю додаток, який виконує багато журналів, як додаток, готовий до імплодування. Є причина, через яку ведеться / потрібна велика кількість журналів, і зазвичай це не стабільність. Її праворуч там з "сторожовими" потоками, які перезапускають вивішені служби.

Якщо ви ніколи не проходили огляд архітектури безпеки (SecArch), це такі речі, які ми дивимося.


1

Вам не повинно бути багатослівне з printf або NSLog у коді випуску. Спробуйте робити тільки printf або NSLog, якщо з програмою трапилося щось погане, IE - невідправна помилка.


1

Майте на увазі, що NSLogs можуть сповільнити інтерфейс користувача / основний потік. Найкраще видаляти їх з версій версій, якщо це абсолютно не потрібно.


0

Я дуже рекомендую використовувати TestFlight для ведення журналу (безкоштовно). Їх метод замінить NSLog (за допомогою макросу) і дозволить увімкнути / вимкнути вхід на їх сервер, журнал Apple System і журнал STDERR для всіх існуючих викликів до NSLog. Приємно в тому, що ви все ще можете переглянути свої журнальні повідомлення на програми, розгорнуті до тестерів та додатки, розгорнуті в App Store, без журналів, які з’являються в системному журналі користувача. Найкраще з обох світів.


Слід врахувати накладні витрати, які додає TestFlight до програми. Чи можна лише додати тестову частину TestFlight?
Йохан Карлссон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.