Is it true that one should not use NSLog() on production code?
Asked Answered
I

12

156

I was told this a few times in this very site, but I wanted to make sure this is really the case.

I was expecting to be able to sprinkle NSLog function calls throughout my code, and that Xcode/gcc would automatically strip those calls out when building my Release/Distribution builds.

Should I avoid using this? If so, what alternatives are most common between experienced Objective-C programmers?

Inchoative answered 19/11, 2008 at 0:37 Comment(2)
I know this question is now very old, but, if you still can, I would mark Marc Charbonneau's answer as accepted. I have modified my answer to point to his, but his answer is the correct one.Pasley
NSLog() inside of a frequent loop will absolutely murder your performance, he said, having found out the hard way.Foulup
H
197

Preprocessor macros are indeed great for debugging. There's nothing wrong with NSLog(), but it's simple to define your own logging function with better functionality. Here's the one I use, it includes the file name and line number to make it easier to track down log statements.

#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

I found it easier to put this entire statement in the prefix header rather than its own file. You could, if you wanted, build a more complicated logging system by having DebugLog interact with normal Objective-C objects. For instance, you could have a logging class that writes to its own log file (or database), and includes a 'priority' argument you could set at runtime, so debug messages are not shown in your release version, but error messages are (if you did this you could make DebugLog(), WarningLog(), and so on).

Oh, and keep in mind #define DEBUG_MODE can be re-used in different places in your application. For example, in my application I use it to disable license key checks and only allow the application to run if it's before a certain date. This lets me distribute a time limited, fully functional beta copy with minimal effort on my part.

Herndon answered 19/11, 2008 at 15:25 Comment(10)
+1 for an excellent answer. I've changed mine to indicate that your #define macros are the way to go, and I hope the OP switches the accepted answer (I left him a comment). I was using a dummy function becuase I didn't know that you could use ... arguments in a macro. Live & learn!Pasley
An excellent answer, though I recommend using a personal prefix on your "DEBUG_MODE" define, such as calling it "JPM_DEBUG" or the like. Far too often I've encountered third party code that also uses DEBUG or DEBUG_MODE or the like, and sometimes that code will not work correctly in DEBUG mode. If you want to turn on third-party library debugging, you should do that intentionally. (Of course, it's library writers who should be prefixing their symbols, but many C and C++ frameworks do not, especially for this define).Spurling
is there an Xcode predefined macro that can be used to turn this on only when the configuration is set to debug? I'd rather not manually set this preprocessor macro myself in every project. can we do something like following pseudocode #if XCODE_CONFIGURATION==DEBUG ?Burlingame
#include <TargetConditionals.h>Sandfly
#2017608Sandfly
This approach leads to spurious "unused variables" warnings from the compiler in release mode when the logging statements use intermediary variables for the sole purpose of computing the values to log. What would be the smartest way to avoid that if you hate compiler warnings as much as I do?Revelry
Jean-Denis: include the unused variables inside #ifdef DEBUG_MODE /* your stuff here */ #endifFerromanganese
+1 One quibble; there absolutely is a real risk with using NSLog() in production code. Egregious use of NSLog() can incur both a significant memory use hit (lots o' autoreleased strings) and CPU cycle hit (lots o' parsing, formatting, and byte copying). You absolutely do not want to incur such costs in a production environment, especially not on an embedded device!Gorse
Be careful with this if you're using blocks! As the macro includes 'self' you can end up with retain cycles if you include an DebugLog in a block.Rubella
This approach how not to use NSLog(0 directly but it still doesn't answers whether or not we should NSLog() in production code and what are the consequences of using it.Lindquist
A
78

Put this 3 lines at the end of -prefix.pch file:

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

You don't need to define anything into your project, because DEBUG is defined in your build setting by default when you create your project.

Alinealinna answered 20/6, 2013 at 5:58 Comment(2)
Far the best solution. You need to add prefix.pch manually from XCode 6.Dowser
Still do we need to change build setting before release i.e. debug to releaseKoweit
D
25

NSLog calls can be left in production code, but should only be there for truly exceptional cases, or information that it is desired that will be logged to the system log.

Applications which litter the system log are annoying, and come across as unprofessional.

Desta answered 19/11, 2008 at 0:47 Comment(2)
Sorry--come across as unprofessional to who? Who's likely to be checking your logs on a released app and judging your professionalism based on that? (To be clear, I totally agree that you shouldn't keep a ton of NSLogs in the release version of your application, but I'm confused by the 'professionalism' argument.)Gasholder
Other developers will what you are doing and get annoyed. Android has a similar problem with some developers being really bad plus.google.com/110166527124367568225/posts/h4jK38n4XYRChloromycetin
R
24

I can't comment on Marc Charbonneau's answer, so I'll post this as an answer.

Further to adding the macro to your pre-compiled header, you can use the Target build configurations to control the defining (or lack of defining) the DEBUG_MODE.

If you select "Debug" active configuration, DEBUG_MODE will be defined, and the macro expands to the full NSLog definition.

Selecting the "Release" active configuration will not define DEBUG_MODE and your NSLogging is omitted from the release build.

Steps:

  • Target > Get Info
  • Build tab
  • Search for "PreProcessor Macros" (or GCC_PREPROCESSOR_DEFINITIONS)
  • Select Configuration: Debug
  • Edit Definition at this Level
  • Add DEBUG_MODE=1
  • Select Configuration: Release
  • confirm DEBUG_MODE is not set in GCC_PREPROCESSOR_DEFINITIONS

if you omit the '=' character in the definition, you will get an error from the preprocessor

Also, paste this comment (shown below) above the macro definition to remind you where the DEBUG_MACRO define comes from ;)

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1
Rumilly answered 28/9, 2009 at 0:10 Comment(3)
It's a valuable additional answer to the question. Deserves to be more than a comment.Geometrid
DEBUG_MODE and DEBUG_MACRO are unconventional. I only found one reference to DEBUG_MACRO on apple's site (opensource.apple.com/source/gm4/gm4-15/src/m4.h?txt). Perhaps the more standard DEBUG and NDEBUG would be better choices? NDEBUG is specified by Posix; while DEBUG is used by convention.Enstatite
+1 Yes this is an old post, but that's the point... In my version of Xcode (4 years later), a search for GCC_PREPROCESSOR_DEFINITIONS returns some different language. Please consider updating this excellent answer for clarity.Ramin
P
11

EDIT: The method posted by Marc Charbonneau, and brought to my attention by sho, is far better than this one.

I have deleted the portion of my answer which suggested using an empty function to disable logging when debug mode is disabled. The portion that deals with setting an automatic preprocessor macro is still relevant, so it remains. I have also edited the name of the preprocessor macro so that it fits better with Marc Charbonneau's answer.


To achieve the automatic (and expected) behaviour in Xcode:

In the project settings, go to the "Build" tab, and select the "Debug" configuration. Find the "Preprocessor Macros" section, and add a macro named DEBUG_MODE.

...

EDIT: See Marc Charbonneau's answer for the proper way to enable and disable logging with the DEBUG_MODE macro.

Pasley answered 19/11, 2008 at 1:50 Comment(0)
C
7

I agree with Matthew. There's nothing wrong with NSLog in production code. In fact, it can be useful to the user. That said, if the only reason you're using NSLog is to help debug, then, yes, that should be removed before you release.

Furthermore, since you've tagged this as an iPhone question, NSLog takes resources, which is something the iPhone has precious little of. If you're NSLogging anything on the iPhone, that takes away processor time from your app. Use it wisely.

Chantress answered 19/11, 2008 at 5:7 Comment(0)
V
4

The simple truth is that NSLog is just plain slow.

But why? To answer that question, let's find out what NSLog does, and then how it does it.

What does NSLog do exactly?

NSLog does 2 things:

It writes log messages to the Apple System Logging (asl) facility. This allows log messages to show up in Console.app. It also checks to see if the application's stderr stream is going to a terminal (such as when the application is being run via Xcode). If so it writes the log message to stderr (so that it shows up in the Xcode console).

Writing to STDERR doesn't sound difficult. That can be accomplished with fprintf and the stderr file descriptor reference. But what about asl?

The best documentation I've found about ASL is a 10 part blog post from Peter Hosey: link

Without going into too much detail, the highlight (as it concerns performance) is this:

To send a log message to the ASL facility, you basically open a client connection to the ASL daemon and send the message. BUT - each thread must use a separate client connection. So, to be thread safe, every time NSLog is called it opens a new asl client connection, sends the message, and then closes the connection.

Resources could be found here & here.

Valais answered 19/7, 2013 at 4:19 Comment(1)
Edited the text. The resources does only need to be in the footer.Freudberg
A
2

As noted in other answers you can use a #define to alter whether NSLog is used or not at compile time.

However a more flexible way is to use a logging library like Cocoa Lumberjack thatallows you to change whether something is logged at runtime as well.

In your code replace NSLog by DDLogVerbose or DDLogError etc, add a #import for the macro definitions etc and setup the loggers, often in the applicationDidFinishLaunching method.

To have the same effect as NSLog the configuration code is

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
Alva answered 9/10, 2012 at 11:20 Comment(0)
E
2

From a security point of view, it depends on what is being logged. If NSLog (or other loggers) is writing sensitive information, then you should remove the logger in production code.

From an auditing point of view, the auditor does not want to look at each use of NSLog to ensure its not logging sensitive information. He/she will simply tell you to remove the logger.

I work with both groups. We audit code, write the coding guides, etc. Our guide requires that logging is disabled in production code. So the internal teams know not to try it ;)

We will also reject an external app that logs in production because we don't want to accept the risk associated with accidentally leaking sensitive information. We don't care what the developer tells us. Its simply not worth our time to investigate.

And remember, we define 'sensitive', and not the developer ;)

I also perceive an app which performs lots of logging as an app ready to implode. There's a reason so much logging is performed/needed, and its usually not stability. Its right up there with 'watchdog' threads that restart hung services.

If you have never been through a Security Architecture (SecArch) review, these are the sorts of things we look at.

Enstatite answered 15/2, 2013 at 21:18 Comment(0)
C
1

You shouldn't be needlessly verbose with printf or NSLog in release code. Try only doing a printf or NSLog if the app has something bad happen to it, I.E. an unrecoverable error.

Cohesion answered 18/1, 2013 at 0:59 Comment(0)
G
1

Keep in mind that NSLogs can slow down the UI / main thread. It is best to remove them from release builds unless absolutely necessary.

Gimp answered 16/12, 2013 at 23:17 Comment(0)
G
0

I would highly recommend using TestFlight for logging (free). Their method will override NSLog (using a macro) and allow you to turn on/off logging to their server, Apple System log and STDERR log, for all your existing calls to NSLog. The nice thing about this is you can still review your log messages for apps deployed to testers and apps deployed in the App Store, without the logs appearing on the user's system log. The best of both worlds.

Gynandromorph answered 3/4, 2012 at 19:14 Comment(1)
One should consider the overhead that TestFlight adds to the application. Is it possible to only add the logging part of TestFlight?Freudberg

© 2022 - 2024 — McMap. All rights reserved.