프로덕션 코드에서 NSLog ()를 사용해서는 안된다는 것이 사실입니까?


155

나는이 사이트에서 이것을 몇 번 들었지만 이것이 사실인지 확인하고 싶었다.

내 코드 전체에 NSLog 함수 호출을 뿌릴 수있을 것으로 기대했으며 릴리스 / 배포 빌드를 빌드 할 때 Xcode / gcc가 해당 호출을 자동으로 제거합니다.

이것을 사용하지 않아야합니까? 그렇다면 숙련 된 Objective-C 프로그래머들 사이에서 가장 일반적인 대안은 무엇입니까?


7
나는이 질문이 지금 매우 오래되었다는 것을 알고 있지만 여전히 할 수 있다면 Marc Charbonneau의 대답을 수락 된 것으로 표시합니다. 그의 답변을 수정하여 그의 답변을 정했지만 그의 답변은 정답입니다.
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 객체와 상호 작용하여보다 복잡한 로깅 시스템을 구축 할 수 있습니다. 예를 들어, 자체 로그 파일 (또는 데이터베이스)에 기록하고 런타임에 설정할 수있는 'priority'인수를 포함하는 로깅 클래스를 가질 수 있으므로 디버그 메시지는 릴리스 버전에 표시되지 않지만 오류 메시지는 ( 이 작업을 수행하면 DebugLog (), WarningLog () 등을 만들 수 있습니다.

아, #define DEBUG_MODE응용 프로그램의 다른 곳에서 재사용 할 수 있습니다. 예를 들어, 내 응용 프로그램에서 라이센스 키 확인을 비활성화하고 특정 날짜 이전의 응용 프로그램 만 실행하도록 허용하는 데 사용합니다. 이를 통해 최소한의 노력으로 시간이 제한적이고 완전한 기능을 갖춘 베타 사본을 배포 할 수 있습니다.


8
탁월한 답변을 얻으려면 +1하십시오. 귀하의 #define 매크로가 갈 길을 나타 내기 위해 내 것을 변경했으며 OP가 수락 된 답변을 전환하기를 바랍니다 (나는 그에게 의견을 남겼습니다). 나는 매크로에서 인수를 사용할 수 있다는 것을 몰랐기 때문에 더미 함수를 사용하고있었습니다. 살고 배우십시오!
e.James

15
"JPM_DEBUG"등과 같은 "DEBUG_MODE"정의에 개인 접두사를 사용하는 것이 좋습니다. 너무 자주 DEBUG 또는 DEBUG_MODE 등을 사용하는 타사 코드가 발생하여 해당 코드가 DEBUG 모드에서 제대로 작동하지 않는 경우가 있습니다. 타사 라이브러리 디버깅을 켜려면 의도적으로 수행해야합니다. (물론, 기호 앞에 접두사를 붙여야하는 것은 라이브러리 작성자이지만 많은 C 및 C ++ 프레임 워크는 특히이 정의에서는 그렇지 않습니다).
Rob Napier

1
구성이 디버그로 설정된 경우에만이를 설정하는 데 사용할 수있는 Xcode 사전 정의 매크로가 있습니까? 차라리 모든 프로젝트에서이 전 처리기 매크로를 직접 설정하지는 않겠습니다. 의사 코드 #if XCODE_CONFIGURATION == DEBUG와 같은 것을 할 수 있습니까?
frankodwyer 2016 년

1
#include <TargetConditionals.h>
slf

2
이 접근 방식은 로깅 명령문이 로깅 할 값을 계산하기위한 목적으로 만 중개 변수를 사용할 때 릴리스 모드에서 컴파일러의 "사용되지 않는 변수"경고를 발생시킵니다. 컴파일러 경고가 마음에 들지 않으면이를 피하는 가장 현명한 방법은 무엇입니까?
Jean-Denis Muys

78

-prefix.pch 파일의 끝에이 3 행을 넣으십시오.

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

프로젝트 DEBUG를 만들 때 기본적으로 빌드 설정에 정의되어 있으므로 프로젝트에 아무것도 정의 할 필요가 없습니다 .


2
최고의 솔루션. XCode 6에서 prefix.pch를 수동으로 추가해야합니다.
Teddy

그럼에도 불구하고 우리가 릴리스마다 즉 디버그하기 전에 변경 빌드 설정해야합니까
UserDev

25

NSLog 호출은 프로덕션 코드에 남겨 둘 수 있지만 실제로 예외적 인 경우 나 시스템 로그에 기록 될 정보에 대해서만 있어야합니다.

시스템 로그를 어지럽히는 응용 프로그램은 성가 시며 전문가가 아닙니다.


14
죄송합니다. 누구에게나 비전문가로 오시겠습니까? 출시 된 앱에서 로그를 확인하고이를 기반으로 전문성을 판단하는 사람은 누구입니까? (분명히, 나는 당신이 당신의 응용 프로그램의 릴리스 버전에서 많은 NSLogs를 유지해서는 안된다는 것에 전적으로 동의합니다. 그러나 나는 '전문성'논쟁과 혼동됩니다.)
WendiKidd

4
다른 개발자들은 당신이하고있는 일을 짜증나게 할 것입니다. 안드로이드도 비슷한 문제를 겪는
Roger Binns

24

Marc Charbonneau의 답변 에 대해서는 언급 할 수 없으므로이 답변으로 게시 할 것입니다.

사전 컴파일 된 헤더에 매크로를 추가하는 것 외에도 Target 빌드 구성을 사용하여 정의 (또는 정의 부족)를 제어 할 수 있습니다 DEBUG_MODE.

" 디버그 "활성 구성 을 선택하면 DEBUG_MODE정의되고 매크로가 전체 NSLog정의로 확장됩니다 .

은 "선택 해제 "활성 구성하는 것은 정의하지 않습니다 DEBUG_MODENSLog카는 릴리스 빌드에서 생략된다.

단계 :

  • 대상> 정보 입수
  • 빌드 탭
  • "전 처리기 매크로"(또는 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
질문에 대한 유용한 추가 답변입니다. 의견 이상의 가치가 있습니다.
morningstar

DEBUG_MODEDEBUG_MACRO틀에 얽매이지입니다. DEBUG_MACRO애플 사이트 ( opensource.apple.com/source/gm4/gm4-15/src/m4.h?txt ) 에서 하나의 참조 만 찾았습니다 . 아마도 더 표준 DEBUG적이고 NDEBUG더 나은 선택일까요? NDEBUGPosix에 의해 지정됩니다. while DEBUG은 규칙에 의해 사용됩니다.
jww

+1 예, 이것은 오래된 게시물이지만 요점입니다 ... Xcode (4 년 후) 버전에서 GCC_PREPROCESSOR_DEFINITIONS를 검색하면 다른 언어가 반환됩니다. 명확성을 위해이 훌륭한 답변을 업데이트하십시오.
David

11

편집 : Marc Charbonneau가 게시하고 sho 가 주목 한 방법 은 이 방법 보다 훨씬 낫습니다.

디버그 모드가 비활성화되었을 때 빈 기능을 사용하여 로깅을 비활성화하도록 제안한 답변 부분을 삭제했습니다. 자동 전 처리기 매크로 설정을 다루는 부분은 여전히 ​​관련이 있으므로 그대로 유지됩니다. Marc Charbonneau의 대답에 더 잘 맞도록 전 처리기 매크로의 이름도 편집했습니다.


Xcode에서 자동 (및 예상) 동작을 달성하려면 다음을 수행하십시오.

프로젝트 설정에서 "빌드"탭으로 이동하여 "디버그"구성을 선택하십시오. "전 처리기 매크로"섹션을 찾아 이름이 매크로를 추가하십시오 DEBUG_MODE.

...

편집 : 매크로로 로깅을 활성화 및 비활성화하는 Marc Charbonneau의 답변 을 참조하십시오 DEBUG_MODE.


7

나는 매튜에 동의합니다. 프로덕션 코드에서 NSLog에는 아무런 문제가 없습니다. 실제로, 그것은 사용자에게 유용 할 수 있습니다. 즉, NSLog를 사용하는 유일한 이유가 디버그를 돕기 위해서라면 릴리스하기 전에 제거해야합니다.

또한이 질문을 iPhone 질문으로 태그 했으므로 NSLog는 리소스를 사용합니다. 이는 iPhone이 거의 소중하지 않은 것입니다. 있는 거 NSLogging의 당신이 만약 아무것도 귀하의 응용 프로그램에서 프로세서 시간을 빼앗아 아이폰에. 현명하게 사용하십시오.


4

간단한 사실은 NSLog가 느리다는 것입니다.

그런데 왜? 이 질문에 대답하기 위해 NSLog가 무엇을하고 어떻게하는지 알아 봅시다.

NSLog는 정확히 무엇을합니까?

NSLog는 두 가지 작업을 수행합니다.

Apple 시스템 로깅 (asl) 기능에 로그 메시지를 기록합니다. 이렇게하면 Console.app에 로그 메시지가 표시 될 수 있습니다. 또한 응용 프로그램의 stderr 스트림이 터미널로 가는지 확인합니다 (예 : 응용 프로그램이 Xcode를 통해 실행될 때). 그렇다면 로그 메시지를 stderr에 기록합니다 (Xcode 콘솔에 표시됨).

STDERR에 쓰는 것이 어렵지 않습니다. 이것은 fprintf와 stderr 파일 디스크립터 참조로 달성 할 수 있습니다. 그러나 asl은 어떻습니까?

내가 ASL에 대해 발견 한 가장 좋은 문서는 피터 Hosey에서 10 일부 블로그 게시물입니다 : 링크

너무 자세하게 설명하지 않으면 서 (성능과 관련된) 주요 내용은 다음과 같습니다.

ASL 기능에 로그 메시지를 보내려면 기본적으로 ASL 데몬에 대한 클라이언트 연결을 열고 메시지를 보냅니다. 그러나 각 스레드는 별도의 클라이언트 연결을 사용해야합니다. 따라서 스레드 안전을 위해 NSLog가 호출 될 때마다 새 asl 클라이언트 연결을 열고 메시지를 보낸 다음 연결을 닫습니다.

리소스는 여기여기 에서 찾을 수 있습니다 .


텍스트를 편집했습니다. 리소스는 바닥 글에만 있어야합니다.
Johan Karlsson

2

다른 답변에서 언급했듯이 #define을 사용하면 NSLog 사용 여부를 컴파일 타임에 변경할 수 있습니다.

그러나보다 유연한 방법은 Cocoa Lumberjack 과 같은 로깅 라이브러리를 사용하여 런타임시 무언가를 로깅할지 여부를 변경할 수 있습니다.

코드에서 NSLog를 DDLogVerbose 또는 DDLogError 등으로 바꾸고 매크로 정의 등을 위해 #import를 추가하고 종종 applicationDidFinishLaunching 메소드에서 로거를 설정하십시오.

NSLog와 동일한 효과를 얻으려면 구성 코드는

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

2

보안 관점에서, 기록되는 내용에 따라 다릅니다. 경우 NSLog(또는 다른 로거) 민감한 정보를 쓰고, 당신은 생산 코드에서 로거를 제거해야합니다.

감사의 관점에서 볼 때 감사인은 NSLog중요한 정보를 기록하지 않도록 각 용도를보고 싶지 않습니다. 로거를 제거하라고 간단히 지시 할 것입니다.

나는 두 그룹과 함께 일합니다. 우리는 코드를 감사하고 코딩 가이드를 작성합니다. 우리 가이드는 프로덕션 코드에서 로깅을 비활성화해야합니다. 내부 팀은 시도하지 않는 것을 알고 있습니다.)

또한 실수로 민감한 정보가 유출되는 위험을 감수하고 싶지 않기 때문에 프로덕션에 로그인하는 외부 앱도 거부합니다. 우리는 개발자가 우리에게 말하는 것을 신경 쓰지 않습니다. 조사 할 시간이 아닙니다.

그리고 우리는 개발자가 아니라 '민감한'을 정의한다는 것을 기억하십시오.)

또한 내포 할 준비가 된 앱으로 많은 로깅을 수행하는 앱을 인식합니다. 너무 많은 로깅이 수행 / 필요한 이유가 있으며 일반적으로 안정성이 없습니다. 중단 된 서비스를 다시 시작하는 'watchdog'스레드가 있습니다.

보안 아키텍쳐 (SecArch) 검토를 해 본 적이 없다면, 이것들이 우리가 살펴본 것들입니다.


1

릴리스 코드에서 printf 또는 NSLog를 불필요하게 자세하게 설명해서는 안됩니다. 앱에 문제가있는 경우 printf 또는 NSLog 만 수행하십시오. IE는 복구 할 수없는 오류입니다.


1

NSLogs는 UI / 메인 스레드 속도를 늦출 수 있습니다. 꼭 필요한 경우가 아니면 릴리스 빌드에서 제거하는 것이 가장 좋습니다.


0

로깅 (무료)에 TestFlight를 사용하는 것이 좋습니다. 이 방법을 사용하면 매크로를 사용하여 NSLog를 재정의하고 NSLog에 대한 기존의 모든 호출에 대해 서버, Apple 시스템 로그 및 STDERR 로그에 대한 로깅을 켜거나 끌 수 있습니다. 이에 대한 좋은 점은 사용자의 시스템 로그에 로그가 표시되지 않고 테스터에 배포 된 앱 및 App Store에 배포 된 앱에 대한 로그 메시지를 계속 검토 할 수 있다는 것입니다. 두 세계의 최고.


TestFlight가 애플리케이션에 추가하는 오버 헤드를 고려해야합니다. TestFlight의 로깅 부분 만 추가 할 수 있습니까?
Johan Karlsson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.