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

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

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

Следует ли мне избегать этого? Если да, то какие альтернативы наиболее распространены среди опытных программистов на Objective-C?


person jpm    schedule 19.11.2008    source источник
comment
Я знаю, что этот вопрос сейчас очень старый, но, если вы еще можете, я бы отметил ответ Марка Шарбонно как принятый. Я изменил свой ответ, чтобы указать на его, но его ответ правильный.   -  person e.James    schedule 26.03.2009
comment
«NSLog () внутри частого цикла абсолютно убьет вашу производительность», - сказал он, выяснив на собственном горьком опыте.   -  person willc2    schedule 11.11.2009


Ответы (12)


Макросы препроцессора действительно хороши для отладки. В 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 можно повторно использовать в разных местах вашего приложения. Например, в моем приложении я использую его, чтобы отключить проверку лицензионных ключей и разрешить запуск приложения только до определенной даты. Это позволяет мне распространять ограниченную по времени полнофункциональную бета-версию с минимальными усилиями с моей стороны.

person Marc Charbonneau    schedule 19.11.2008
comment
+1 за отличный ответ. Я изменил свой, чтобы указать, что ваши макросы #define - это путь, и я надеюсь, что OP переключает принятый ответ (я оставил ему комментарий). Я использовал фиктивную функцию, потому что не знал, что вы можете использовать ... аргументы в макросе. Живи учись! - person e.James; 26.03.2009
comment
Отличный ответ, хотя я рекомендую использовать личный префикс в вашем определении DEBUG_MODE, например, назвать его JPM_DEBUG или тому подобное. Слишком часто я встречал сторонний код, который также использует DEBUG или DEBUG_MODE или тому подобное, и иногда этот код не работает правильно в режиме DEBUG. Если вы хотите включить отладку сторонней библиотеки, вы должны сделать это намеренно. (Конечно, именно разработчики библиотек должны добавлять префиксы к своим символам, но многие фреймворки C и C ++ этого не делают, особенно для этого определения). - person Rob Napier; 20.05.2009
comment
есть ли предопределенный макрос Xcode, который можно использовать, чтобы включить это, только когда конфигурация настроена на отладку? Я бы предпочел не устанавливать вручную этот макрос препроцессора в каждом проекте. можем ли мы сделать что-то вроде следующего псевдокода #if XCODE_CONFIGURATION == DEBUG? - person frankodwyer; 25.06.2009
comment
#include ‹TargetConditionals.h› - person slf; 05.02.2010
comment
comment
Этот подход приводит к ложным предупреждениям о неиспользуемых переменных от компилятора в режиме выпуска, когда операторы регистрации используют промежуточные переменные с единственной целью - вычислить значения для регистрации. Что было бы самым разумным способом избежать этого, если вы так же ненавидите предупреждения компилятора, как и я? - person Jean-Denis Muys; 13.09.2010
comment
Жан-Дени: включите неиспользуемые переменные в #ifdef DEBUG_MODE / * здесь ваши данные * / #endif - person Jonny; 16.11.2010
comment
+1 Одна придирка; использование NSLog() в производственном коде представляет собой реальный риск. Неоправданное использование NSLog() может привести как к значительному расходу памяти (много автоматически выпущенных строк), так и к циклу ЦП (много синтаксического анализа, форматирования и копирования байтов). Вы абсолютно не хотите нести такие расходы в производственной среде, особенно на встроенном устройстве! - person bbum; 26.05.2011
comment
Будьте осторожны с этим, если используете блоки! Поскольку макрос включает "self", вы можете получить циклы сохранения, если включите DebugLog в блок. - person Ben Clayton; 07.03.2013
comment
Этот подход заключается в том, чтобы не использовать NSLog (0 напрямую, но он по-прежнему не отвечает, следует ли нам использовать NSLog () в производственном коде и каковы последствия его использования. - person thesummersign; 12.08.2015

Поместите эти 3 строки в конец файла -prefix.pch:

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

Вам не нужно ничего определять в своем проекте, потому что DEBUG определяется в настройках сборки по умолчанию, когда вы создаете свой проект.

person roel    schedule 20.06.2013
comment
Наилучшее решение. Вам нужно добавить prefix.pch вручную из XCode 6. - person Teddy; 17.03.2015
comment
Тем не менее, нам нужно изменить настройку сборки перед выпуском, то есть отладить до выпуска - person UserDev; 26.08.2015

Вызовы NSLog можно оставить в производственном коде, но они должны присутствовать только в действительно исключительных случаях или желательной информации, которая будет занесена в системный журнал.

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

person Matthew Schinckel    schedule 19.11.2008
comment
Извините - кому показаться непрофессиональным? Кто, вероятно, будет проверять ваши журналы в выпущенном приложении и судить о вашем профессионализме на основе этого? (Для ясности: я полностью согласен с тем, что вы не должны хранить кучу журналов NSLogs в окончательной версии вашего приложения, но меня смущает аргумент «профессионализм».) - person WendiKidd; 03.09.2012
comment
Другие разработчики будут недовольны тем, что вы делаете. У Android похожая проблема: некоторые разработчики очень плохи plus.google.com/110166527124367568225/posts/h4jK38n4XYR - person Roger Binns; 02.03.2013

Я не могу комментировать Ответ Марка Шарбонно, поэтому я отправлю его в качестве ответа.

Помимо добавления макроса в предварительно скомпилированный заголовок, вы можете использовать конфигурации целевой сборки для управления определением (или отсутствием определения) DEBUG_MODE.

Если вы выберете активную конфигурацию «Отладка», будет определено DEBUG_MODE, а макрос развернется до полного NSLog определения.

При выборе активной конфигурации «Выпуск» не будет определяться DEBUG_MODE, и ваша NSLog привязка не будет включена в сборку выпуска.

Шаги:

  • Цель> Получить информацию
  • Вкладка "Сборка"
  • Найдите "Макросы препроцессора" (или 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
person ohhorob    schedule 28.09.2009
comment
Это ценный дополнительный ответ на вопрос. Заслуживает быть большего, чем комментарий. - person morningstar; 10.10.2011
comment
DEBUG_MODE и DEBUG_MACRO нетрадиционны. Я нашел только одну ссылку на DEBUG_MACRO на сайте Apple (opensource.apple.com/source/gm4/gm4-15/src/m4.h?txt). Возможно, более стандартные DEBUG и NDEBUG были бы лучшим выбором? NDEBUG определяется Posix; в то время как DEBUG используется по соглашению. - person jww; 16.02.2013
comment
+1 Да, это старый пост, но в этом суть ... В моей версии Xcode (4 года спустя) поиск GCC_PREPROCESSOR_DEFINITIONS возвращает другой язык. Пожалуйста, подумайте об обновлении этого отличного ответа для ясности. - person David; 25.09.2013

ИЗМЕНИТЬ: метод, опубликованный Марком Шарбонно и доведенный до моего сведения sho, намного лучше, чем этот.

Я удалил часть своего ответа, в которой предлагалось использовать пустую функцию для отключения ведения журнала при отключенном режиме отладки. Часть, которая касается установки макроса автоматического препроцессора, по-прежнему актуальна, поэтому остается. Я также отредактировал имя макроса препроцессора, чтобы оно лучше соответствовало ответу Марка Шарбонно.


Чтобы добиться автоматического (и ожидаемого) поведения в Xcode:

В настройках проекта перейдите на вкладку «Сборка» и выберите конфигурацию «Отладка». Найдите раздел «Макросы препроцессора» и добавьте макрос с именем DEBUG_MODE.

...

ИЗМЕНИТЬ: См. Ответ Марка Шарбонно о правильном способе включения и отключения ведения журнала с помощью макроса DEBUG_MODE.

person e.James    schedule 19.11.2008

Я согласен с Мэтью. В производственном коде нет ничего плохого в NSLog. На самом деле это может быть полезно пользователю. Тем не менее, если единственная причина, по которой вы используете NSLog, - это помощь в отладке, то да, это следует удалить перед выпуском.

Кроме того, поскольку вы отметили это как вопрос об iPhone, NSLog требует ресурсов, чего у iPhone очень мало. Если вы ведете NSLogging что-нибудь на iPhone, это отнимает процессорное время у вашего приложения. Использовать его с умом.

person August    schedule 19.11.2008

Простая правда в том, что NSLog просто медленный.

Но почему? Чтобы ответить на этот вопрос, давайте выясним, что делает NSLog, а затем как он это делает.

Что именно делает NSLog?

NSLog делает 2 вещи:

Он записывает сообщения журнала в средство ведения системного журнала Apple (asl). Это позволяет отображать сообщения журнала в Console.app. Он также проверяет, идет ли поток stderr приложения на терминал (например, когда приложение запускается через Xcode). Если это так, он записывает сообщение журнала в stderr (чтобы оно отображалось в консоли Xcode).

Запись в STDERR не кажется сложной. Это можно сделать с помощью fprintf и ссылки на дескриптор файла stderr. А как насчет asl?

Лучшая документация, которую я нашел об ASL, - это сообщение из 10 частей в блоге Питера Хози: ссылка

Не вдаваясь в подробности, главное (что касается производительности) таково:

Чтобы отправить сообщение журнала в средство ASL, вы в основном открываете клиентское соединение с демоном ASL и отправляете сообщение. НО - каждый поток должен использовать отдельное клиентское соединение. Итак, чтобы быть потокобезопасным, каждый раз, когда вызывается NSLog, он открывает новое клиентское соединение asl, отправляет сообщение, а затем закрывает соединение.

Ресурсы можно найти здесь и здесь.

person Andrew    schedule 19.07.2013
comment
Отредактировал текст. Ресурсы должны быть только в нижнем колонтитуле. - person Johan Karlsson; 26.11.2013

Как отмечено в других ответах, вы можете использовать #define, чтобы изменить, используется ли NSLog во время компиляции.

Однако более гибким способом является использование библиотеки журналов, например Cocoa Lumberjack, который позволяет вам также изменить, будет ли что-то регистрироваться во время выполнения.

В вашем коде замените NSLog на DDLogVerbose или DDLogError и т. Д., Добавьте #import для определений макросов и т. Д. И настройте логгеры, часто в методе applicationDidFinishLaunching.

Чтобы иметь тот же эффект, что и NSLog, код конфигурации

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
person mmmmmm    schedule 09.10.2012

С точки зрения безопасности это зависит от того, что регистрируется. Если NSLog (или другие регистраторы) записывают конфиденциальную информацию, вам следует удалить регистратор в производственном коде.

С точки зрения аудита, аудитор не хочет проверять каждое использование NSLog, чтобы убедиться, что конфиденциальная информация не регистрируется. Он просто скажет вам удалить регистратор.

Я работаю с обеими группами. Мы проверяем код, пишем руководства по кодированию и т. Д. Наше руководство требует, чтобы в производственном коде было отключено ведение журнала. Так что внутренние команды знают, что не стоит пробовать это;)

Мы также отклоним внешнее приложение, которое входит в производственную среду, потому что мы не хотим принимать на себя риск, связанный с случайной утечкой конфиденциальной информации. Нам все равно, что нам говорит разработчик. Просто не стоит тратить время на расследование.

И помните, мы определяем «чувствительный», а не разработчик;)

Я также воспринимаю приложение, которое выполняет много журналов, как приложение, готовое к взрыву. Есть причина, по которой выполняется / требуется так много журналов, и обычно это не стабильность. Это прямо там со «сторожевыми» потоками, которые перезапускают зависшие службы.

Если вы никогда не проходили обзор архитектуры безопасности (SecArch), мы рассмотрим такие вещи.

person jww    schedule 15.02.2013

Вы не должны излишне подробно рассказывать о printf или NSLog в коде выпуска. Попробуйте выполнить printf или NSLog только в том случае, если с приложением что-то случилось, т. Е. неисправимая ошибка.

person MaddTheSane    schedule 18.01.2013

Имейте в виду, что NSLogs может замедлить работу пользовательского интерфейса / основного потока. Лучше всего удалить их из сборок выпуска, если в этом нет крайней необходимости.

person psy    schedule 16.12.2013

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

person Joel    schedule 03.04.2012
comment
Следует учитывать накладные расходы, которые TestFlight добавляет приложению. Можно ли добавить только часть журнала TestFlight? - person Johan Karlsson; 26.11.2013