Czy to prawda, że ​​nie należy używać NSLog () na kodzie produkcyjnym?

155

Powiedziano mi to kilka razy w tej samej witrynie, ale chciałem się upewnić, że tak jest naprawdę.

Spodziewałem się, że będę w stanie obsypać wywołania funkcji NSLog w całym kodzie i że Xcode / gcc automatycznie usunie te wywołania podczas budowania moich kompilacji Release / Distribution.

Czy powinienem tego unikać? Jeśli tak, jakie alternatywy są najczęstsze między doświadczonymi programistami Objective-C?

jpm
źródło
7
Wiem, że to pytanie jest teraz bardzo stare, ale jeśli nadal możesz, oznaczyłbym odpowiedź Marca Charbonneau jako zaakceptowaną. Zmodyfikowałem swoją odpowiedź, aby wskazywała na jego, ale jego odpowiedź jest prawidłowa.
James
5
NSLog () wewnątrz częstej pętli absolutnie zamorduje twoje działanie, powiedział, odkrywając na własnej skórze.
willc2

Odpowiedzi:

197

Makra preprocesora są rzeczywiście świetne do debugowania. Nie ma nic złego w NSLog (), ale łatwo jest zdefiniować własną funkcję logowania z lepszą funkcjonalnością. Oto ten, którego używam, zawiera nazwę pliku i numer wiersza, aby ułatwić śledzenie wyciągów dziennika.

#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

Okazało się, że łatwiej było umieścić całą instrukcję w nagłówku prefiksu niż we własnym pliku. Możesz, jeśli chcesz, zbudować bardziej skomplikowany system logowania poprzez interakcję DebugLog z normalnymi obiektami Objective-C. Na przykład, możesz mieć klasę rejestrowania, która zapisuje do swojego własnego pliku dziennika (lub bazy danych) i zawiera argument `` priorytet '', który możesz ustawić w czasie wykonywania, więc komunikaty debugowania nie są wyświetlane w Twojej wersji, ale komunikaty o błędach to ( jeśli to zrobiłeś, możesz zrobić DebugLog (), WarningLog () i tak dalej).

Aha, i pamiętaj, że #define DEBUG_MODEmożna go ponownie wykorzystać w różnych miejscach w aplikacji. Na przykład w mojej aplikacji używam go do wyłączania sprawdzania kluczy licencyjnych i zezwalania aplikacji na działanie tylko przed określoną datą. To pozwala mi rozpowszechniać ograniczoną czasowo, w pełni funkcjonalną wersję beta przy minimalnym wysiłku z mojej strony.

Marc Charbonneau
źródło
8
+1 za doskonałą odpowiedź. Zmieniłem moje, aby wskazać, że twoje #define makra są drogą do zrobienia, i mam nadzieję, że OP zmieni zaakceptowaną odpowiedź (zostawiłem mu komentarz). Używałem funkcji fikcyjnej, ponieważ nie wiedziałem, że możesz użyć ... argumentów w makrze. Nauka życia!
James
15
Doskonała odpowiedź, chociaż polecam użycie osobistego przedrostka w definicji DEBUG_MODE, na przykład nazwanie go „JPM_DEBUG” lub tym podobne. Zbyt często napotykałem kod strony trzeciej, który również używa DEBUG lub DEBUG_MODE lub podobnego, a czasami ten kod nie działa poprawnie w trybie DEBUG. Jeśli chcesz włączyć debugowanie bibliotek innych firm, powinieneś to zrobić celowo. (Oczywiście to autorzy bibliotek powinni poprzedzać swoje symbole, ale wiele frameworków C i C ++ tego nie robi, zwłaszcza w tym celu).
Rob Napier
1
czy istnieje wstępnie zdefiniowane makro Xcode, którego można użyć do włączenia tej funkcji tylko wtedy, gdy konfiguracja jest ustawiona na debugowanie? Wolałbym nie ustawiać ręcznie tego makra preprocesora w każdym projekcie. czy możemy zrobić coś takiego jak pseudokod #if XCODE_CONFIGURATION == DEBUG?
frankodwyer
1
#include <TargetConditionals.h>
slf
2
Takie podejście prowadzi do fałszywych ostrzeżeń o „nieużywanych zmiennych” z kompilatora w trybie zwolnienia, gdy instrukcje rejestrowania używają zmiennych pośrednich wyłącznie w celu obliczenia wartości do zarejestrowania. Jaki byłby najmądrzejszy sposób na uniknięcie tego, jeśli nienawidzisz ostrzeżeń kompilatora tak samo jak ja?
Jean-Denis Muys
78

Umieść te 3 wiersze na końcu pliku -prefix.pch:

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

Nie musisz niczego definiować w swoim projekcie, ponieważ DEBUGjest to domyślnie definiowane w ustawieniach kompilacji podczas tworzenia projektu.

roel
źródło
2
Zdecydowanie najlepsze rozwiązanie. Musisz ręcznie dodać prefix.pch z XCode 6.
Teddy
Nadal musimy zmienić ustawienia kompilacji przed wydaniem, tj. Debugować do wydania
UserDev
25

Wywołania NSLog można pozostawić w kodzie produkcyjnym, ale powinny one występować tylko w naprawdę wyjątkowych przypadkach lub w informacjach, które są wymagane, aby były rejestrowane w dzienniku systemowym.

Aplikacje, które zaśmiecają dzienniki systemowe, są denerwujące i postrzegane jako nieprofesjonalne.

Matthew Schinckel
źródło
14
Przepraszam - kto jest nieprofesjonalny? Kto prawdopodobnie będzie sprawdzał Twoje dzienniki w wydanej aplikacji i na tej podstawie oceniał Twój profesjonalizm? (Żeby było jasne, całkowicie się zgadzam, że nie powinieneś przechowywać wielu NSLogów w wydanej wersji swojej aplikacji, ale jestem zdezorientowany argumentem
``
4
Inni programiści będą robić to, co robisz i denerwują się. Android ma podobny problem, ponieważ niektórzy programiści są naprawdę źli plus.google.com/110166527124367568225/posts/h4jK38n4XYR
Roger Binns
24

Nie mogę komentować odpowiedzi Marca Charbonneau , więc opublikuję to jako odpowiedź.

Oprócz dodania makra do wstępnie skompilowanego nagłówka, możesz użyć konfiguracji kompilacji docelowej, aby kontrolować definiowanie (lub brak definiowania) DEBUG_MODE.

W przypadku wybrania opcji „ Debuguj ” aktywna konfiguracja DEBUG_MODEzostanie zdefiniowana, a makro rozwinie się do pełnej NSLogdefinicji.

Wybranie aktywnej konfiguracji „ Wydanie ” nie spowoduje zdefiniowania, DEBUG_MODEa twoje NSLogging zostanie pominięte w kompilacji wydania.

Kroki:

  • Cel> Uzyskaj informacje
  • Zakładka Build
  • Wyszukaj „PreProcessor Macros” (lub GCC_PREPROCESSOR_DEFINITIONS)
  • Wybierz Konfiguracja: Debuguj
  • Edytuj definicję na tym poziomie
  • Dodaj DEBUG_MODE=1
  • Wybierz Konfiguracja: Wydanie
  • potwierdzenie DEBUG_MODEnie jest ustawioneGCC_PREPROCESSOR_DEFINITIONS

jeśli pominiesz znak „=” w definicji, otrzymasz błąd z preprocesora

Wklej również ten komentarz (pokazany poniżej) nad definicją makra, aby przypomnieć Ci, skąd DEBUG_MACROpochodzi definicja;)

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1
ohhorob
źródło
1
To cenna dodatkowa odpowiedź na pytanie. Zasługuje na coś więcej niż komentarz.
Morningstar
DEBUG_MODEi DEBUG_MACROsą niekonwencjonalne. Znalazłem tylko jedno odniesienie do w DEBUG_MACROwitrynie Apple ( opensource.apple.com/source/gm4/gm4-15/src/m4.h?txt ). Może bardziej standardowy DEBUGi NDEBUGbyłby lepszym wyborem? NDEBUGjest określony przez Posix; while DEBUGjest używany przez konwencję.
jww
+1 Tak, to jest stary post, ale o to chodzi ... W mojej wersji Xcode (4 lata później), wyszukiwanie GCC_PREPROCESSOR_DEFINITIONS zwraca inny język. Dla jasności rozważ zaktualizowanie tej doskonałej odpowiedzi.
David
11

EDIT: metoda wysłane przez Marc Charbonneau i zwrócił moją uwagę przez sho , jest o wiele lepszy niż ten.

Usunąłem część mojej odpowiedzi, która sugerowała użycie pustej funkcji do wyłączenia rejestrowania, gdy tryb debugowania jest wyłączony. Część, która dotyczy ustawienia automatycznego makra preprocesora, jest nadal aktualna, więc pozostaje. Zmieniłem również nazwę makra preprocesora, aby lepiej pasowała do odpowiedzi Marca Charbonneau.


Aby osiągnąć automatyczne (i oczekiwane) zachowanie w Xcode:

W ustawieniach projektu przejdź do zakładki „Build” i wybierz konfigurację „Debug”. Znajdź sekcję „Preprocessor Macros” i dodaj makro o nazwie DEBUG_MODE.

...

EDYCJA: Zobacz odpowiedź Marca Charbonneau, aby dowiedzieć się, jak prawidłowo włączać i wyłączać rejestrowanie za pomocą DEBUG_MODEmakra.

e.James
źródło
7

Zgadzam się z Matthew. Nie ma nic złego w NSLog w kodzie produkcyjnym. W rzeczywistości może to być przydatne dla użytkownika. To powiedziawszy, jeśli jedynym powodem używania NSLog jest pomoc w debugowaniu, to tak, powinno to zostać usunięte przed wydaniem.

Co więcej, ponieważ oznaczyłeś to jako pytanie dotyczące iPhone'a, NSLog pobiera zasoby, których iPhone ma niewiele. Jeśli nie rejestrujesz niczego na iPhonie, zabiera to czas procesora Twojej aplikacji. Używaj rozważnie.

sierpień
źródło
4

Prosta prawda jest taka, że ​​NSLog jest po prostu wolny.

Ale dlaczego? Aby odpowiedzieć na to pytanie, dowiedzmy się, co robi NSLog i jak to robi.

Co dokładnie robi NSLog?

NSLog robi 2 rzeczy:

Zapisuje komunikaty dziennika w narzędziu Apple System Logging (asl). Umożliwia to wyświetlanie komunikatów dziennika w Console.app. Sprawdza również, czy strumień stderr aplikacji trafia do terminala (na przykład, gdy aplikacja jest uruchamiana przez Xcode). Jeśli tak, zapisuje komunikat dziennika na stderr (tak, aby pojawił się w konsoli Xcode).

Pisanie do STDERR nie wydaje się trudne. Można to osiągnąć za pomocą fprintf i odwołania do deskryptora pliku stderr. Ale co z asl?

Najlepszą dokumentacją dotyczącą ASL, jaką znalazłem, jest 10-częściowy post na blogu autorstwa Petera Hoseya: link

Bez wchodzenia w szczegóły, najważniejsza (jeśli chodzi o wydajność) jest:

Aby wysłać komunikat dziennika do narzędzia ASL, po prostu otwierasz połączenie klienta z demonem ASL i wysyłasz wiadomość. ALE - każdy wątek musi używać osobnego połączenia klienta. Tak więc, aby zapewnić bezpieczeństwo wątków, za każdym razem, gdy wywoływany jest NSLog, otwiera nowe połączenie klienta asl, wysyła wiadomość, a następnie zamyka połączenie.

Zasoby można znaleźć tutaj i tutaj .

Andrzej
źródło
Edytował tekst. Zasoby muszą znajdować się tylko w stopce.
Johan Karlsson
2

Jak zauważono w innych odpowiedziach, możesz użyć #define, aby zmienić, czy NSLog jest używany, czy nie w czasie kompilacji.

Jednak bardziej elastycznym sposobem jest użycie biblioteki rejestrowania, takiej jak Cocoa Lumberjack , która pozwala zmienić, czy coś jest również rejestrowane w czasie wykonywania.

W kodzie zamień NSLog na DDLogVerbose lub DDLogError itp., Dodaj #import dla definicji makr itp. I skonfiguruj rejestratory, często w metodzie applicationDidFinishLaunching.

Aby mieć taki sam efekt jak NSLog, kod konfiguracyjny to

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
mmmmmm
źródło
2

Z punktu widzenia bezpieczeństwa zależy to od tego, co jest rejestrowane. Jeśli NSLog(lub inne rejestratory) zapisują poufne informacje, należy usunąć rejestrator w kodzie produkcyjnym.

Z punktu widzenia audytu audytor nie chce patrzeć na każde użycie, NSLogaby upewnić się, że nie rejestruje poufnych informacji. On / ona po prostu powie ci, żebyś usunął rejestrator.

Pracuję z obiema grupami. Audytujemy kod, piszemy przewodniki po kodowaniu, itp. Nasz przewodnik wymaga wyłączenia logowania w kodzie produkcyjnym. Więc wewnętrzne zespoły wiedzą, żeby tego nie próbować;)

Odrzucimy również zewnętrzną aplikację, która loguje się w środowisku produkcyjnym, ponieważ nie chcemy zaakceptować ryzyka związanego z przypadkowym wyciekiem poufnych informacji. Nie obchodzi nas, co powie nam programista. Po prostu nie warto poświęcać nam czasu na badanie.

I pamiętaj, że definiujemy „wrażliwy”, a nie dewelopera;)

Postrzegam również aplikację, która wykonuje wiele operacji logowania, jako aplikację gotową do implodowania. Jest powód, dla którego rejestrowanie jest wykonywane / potrzebne, a zwykle nie jest to stabilne. Jest tam z wątkami „watchdog”, które ponownie uruchamiają zawieszone usługi.

Jeśli nigdy nie przeszedłeś przez przegląd architektury bezpieczeństwa (SecArch), to są rodzaje rzeczy, którym się przyglądamy.

jww
źródło
1

Nie powinieneś być niepotrzebnie gadatliwy z printf lub NSLog w kodzie wydania. Spróbuj wykonać printf lub NSLog tylko wtedy, gdy aplikacja ma coś złego, IE jest to nieodwracalny błąd.

MaddTheSane
źródło
1

Pamiętaj, że NSLogs może spowolnić interfejs użytkownika / główny wątek. Najlepiej jest usunąć je z kompilacji wydania, chyba że jest to absolutnie konieczne.

psy
źródło
0

Gorąco polecam korzystanie z TestFlight do logowania (za darmo). Ich metoda zastąpi NSLog (przy użyciu makra) i umożliwi włączenie / wyłączenie logowania do ich serwera, dziennika systemu Apple i dziennika STDERR dla wszystkich istniejących wywołań NSLog. Zaletą tego jest to, że nadal możesz przeglądać komunikaty dziennika pod kątem aplikacji wdrożonych na testerach i aplikacji wdrożonych w App Store, bez dzienników pojawiających się w dzienniku systemowym użytkownika. Najlepsze z obu światów.

Joel
źródło
Należy wziąć pod uwagę obciążenie, które TestFlight dodaje do aplikacji. Czy można dodać tylko część rejestrującą TestFlight?
Johan Karlsson