Правда ли, что нельзя использовать 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.