-
Notifications
You must be signed in to change notification settings - Fork 571
Integrating NSLogger in your application
The NSLogger API can be used for both iOS (iPhone, iPad) and Mac OS X desktop applications. The client code is the same, and compiles and run without modification on both platforms.
1. Add files to your project
First and foremost, add the following files to your project:
LoggerCommon.h
LoggerClient.h
LoggerClient.m
Also, if your project does not already include them, add the required system frameworks:
CFNetwork.framework
SystemConfiguration.framework
2. Use NSLogger API calls to log stuff
You can now use all the NSLogger API calls to log text, images and data. Take advantage of tags to categorize the output, this will allow you to better filter out unwanted traces when you want to explore specific aspects of your application traces. Use log levels to further categorize traces if you need (log levels start a 0, the bigger the number, the more specific / detailed the trace is meant to be). For example:
- (void)sendData:(NSData *)data toServer:(NSURL *)aURL
{
LogMessage(@"network", 0, @"Going to send data to %@", aURL);
LogData(@"network", 1, data);
...
}
A good way to cleanly instrument your code, while keeping the option of disabling all calls to NSLogger in release builds, is to use macros in conjunction with a preprocessor macro. For debug builds, define the preprocessor macro DEBUG. Then prepare a header file that will contain your logging macros.
Here are some example macros. Note that we use variadic macros, which are a way to transfer the macro arguments from the macro usage to its replacement.
#ifdef DEBUG
#define LOG_NETWORK(level, ...) LogMessageF(__FILE__,__LINE__,__FUNCTION__,@"network",level,__VA_ARGS__)
#define LOG_GENERAL(level, ...) LogMessageF(__FILE__,__LINE__,__FUNCTION__,@"general",level,__VA_ARGS__)
#define LOG_GRAPHICS(level, ...) LogMessageF(__FILE__,__LINE__,__FUNCTION__,@"graphics",level,__VA_ARGS__)
#else
#define LOG_NETWORK(...) do{}while(0)
#define LOG_GENERAL(...) do{}while(0)
#define LOG_GRAPHICS(...) do{}while(0)
#endif
Now it suddenly becomes very easy and straightforward to add debug-only traces to your application. The previous code example would become:
- (void)sendData:(NSData *)data toServer:(NSURL *)aURL
{
LOG_NETWORK(0, @"Going to send data to %@", aURL);
LOG_NETWORK(1, @"-> data=%@", data);
...
}
And no code at all will be emitted when building the Release build of your application.
It is common (and good) practice to use assert()
in your code to verify that important conditions are met. One drawback is that a failed assertion immediately aborts the application, and all logs that have not been transmitted to the viewer could be lost. You can use the LoggerFlush()
API to ensure that all logs have been transmitted. Here is an example of assert() macro redefinition that does the following (the LoggerFlush() API is available starting with NSLogger 1.0b7):
- Log a message to the console informing the developer that an assertion is going to fail
- Ensure that all logs have been transmitted, waiting (if needed) for a connection to the viewer to be acquired
- Finally fail the assertion
#if defined(DEBUG) && !defined(NDEBUG)
#undef assert
#if __DARWIN_UNIX03
#define assert(e) \
(__builtin_expect(!(e), 0) ? (CFShow(CFSTR("assert going to fail, connect NSLogger NOW\n")), LoggerFlush(NULL,YES), __assert_rtn(__func__, __FILE__, __LINE__, #e)) : (void)0)
#else
#define assert(e) \
(__builtin_expect(!(e), 0) ? (CFShow(CFSTR("assert going to fail, connect NSLogger NOW\n")), LoggerFlush(NULL,YES), __assert(#e, __FILE__, __LINE__)) : (void)0)
#endif
#endif