Правда ли, что нельзя использовать NSLog () в производственном коде?


155

Мне говорили об этом несколько раз на этом самом сайте, но я хотел убедиться, что это действительно так.

Я ожидал, что смогу распределить вызовы функций NSLog по всему коду, и что Xcode / gcc автоматически удалит эти вызовы при сборке моих сборок Release / Distribution.

Должен ли я избегать использования этого? Если да, то какие альтернативы наиболее распространены среди опытных программистов 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>
— 2010 г.

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определения.

Выбор активной конфигурации « Release » не определит, 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.

...

РЕДАКТИРОВАТЬ: См . Ответ Марка Charbonneau для правильного включения и отключения ведения журнала с помощью 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 может замедлить UI / основной поток. Лучше удалить их из релизных сборок, если в этом нет крайней необходимости.


0

Я настоятельно рекомендую использовать TestFlight для регистрации (бесплатно). Их метод переопределит NSLog (с помощью макроса) и позволит вам включить / выключить ведение журнала на их сервер, журнал системы Apple и журнал STDERR для всех ваших существующих вызовов NSLog. Приятно то, что вы по-прежнему можете просматривать сообщения журнала для приложений, развернутых в тестерах, и приложений, развернутых в App Store, и журналы не отображаются в системном журнале пользователя. Лучший из двух миров.


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