36

I'm using CocoaLumberjack in an iPhone project, to log some information.

I've followed the Getting started guide, and everything works fine, but there is one thing that bugs me: there doesn't seem to be an elegant way to define a log level for the whole app. To make it work I need to define a constant in every source file, like this:

static const int ddLogLevel = LOG_LEVEL_VERBOSE;

So, is there a way to define a global log level for the application?

I found this article on the subject, but I still need to add an #import in every file...

Cameron Lowell Palmer
  • 17,859
  • 4
  • 102
  • 114
Marcos Crispino
  • 7,559
  • 4
  • 38
  • 58

12 Answers12

21

You could use an #include statement in your *.pch file so that it's automatically included in all your project's files.

FreeAsInBeer
  • 12,837
  • 5
  • 46
  • 79
  • Also - To customize on a per-file basis, change `ddLogLevel` to non-const, and set it in the specific file's class +initialize method (This works because each .m file gets its own static ddLogLevel variable). Note: Classes may call their superclass's +initialize method, so to avoid potential bugs use `if (self == [ClassName self])` in the method. – Danra Jun 23 '14 at 13:45
  • 4
    The problem being is that .pch files are no longer provided in Xcode projects by default. You should probably consider them deprecated. – Cameron Lowell Palmer Nov 13 '14 at 11:34
18

I didn't find a better way to do it than the one explained in the article I mentioned in the question.

Constant.h

extern int const ddLogLevel;

Constant.m

#import "Constants.h"
#import "DDLog.h"

int const ddLogLevel = LOG_LEVEL_VERBOSE;

Logger configuration

#import "DDLog.h"
#import "DDASLLogger.h"
#import "DDTTYLogger.h"
#import "DDFileLogger.h"

...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

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

 DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; 
 [DDLog addLogger:fileLogger];
 [fileLogger release];

...

Import your class

#import "DDLog.h"
#import "Constants.h"

...

- (void)someMethod {
 DDLogVerbose(@"Log this message");
}
Jeroen Vannevel
  • 41,258
  • 21
  • 92
  • 157
Marcos Crispino
  • 7,559
  • 4
  • 38
  • 58
  • Is there a way to do dynamic logging with this? – Tejaswi Yerukalapudi May 06 '13 at 20:12
  • 6
    Please, avoid answers where the only source of information is an external link. At least quote the essential points of the resource, in order to avoid link rotting. – Bakuriu Feb 27 '14 at 22:13
  • I see what you're doing @Marcos Crispino.. but I'm guessing this will only work if the only constant that's defined in `Constants.h` is the logging constant (ie as opposed to having all your global constants in that file).. in that case the name `Constants.h` is misleading, as it is specific to the log level.. i'd call it `GlobalLogLevel.h` or something that more accurately describes and limits the scope of this file – abbood Jan 12 '15 at 13:05
  • anyways I kinda created my own [solution](http://stackoverflow.com/a/27902859/766570) based on yours, which adds the benefit of being able to override the global log level on a per file basis – abbood Jan 12 '15 at 13:16
16

No more Prefix Headers, please.

You do not need the now deprecated .pch file, simply include a header file where needed.

Logger.h - CocoaLumberjack 1.9.x

#ifndef Project_Logger_h
#define Project_Logger_h

#if defined(__OBJC__)

#import <CocoaLumberjack/DDLog.h>
extern int ddLogLevel;

#endif

#endif

Logger.m

#import "Logger.h"

int ddLogLevel = LOG_LEVEL_VERBOSE;

Changes for CocoaLumberjack 2.x

#import <CocoaLumberjack/CocoaLumberjack.h>

int ddLogLevel = DDLogLevelVerbose;

If the syntax changes when 2.0 is out of beta please comment or edit.

Example usage in AppDelegate

#import "AppDelegate.h"

#import "Logger.h"

#import <CocoaLumberjack/DDFileLogger.h>
#import <CocoaLumberjack/DDASLLogger.h>
#import <CocoaLumberjack/DDTTYLogger.h>



@interface AppDelegate ()
@property (strong, nonatomic) DDFileLogger *fileLogger;
@end



@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [DDLog addLogger:[DDASLLogger sharedInstance]];
    [DDLog addLogger:[DDTTYLogger sharedInstance]];

    DDFileLogger *fileLogger = [[DDFileLogger alloc] init];
    fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling
    fileLogger.logFileManager.maximumNumberOfLogFiles = 7;

    [DDLog addLogger:fileLogger];
    self.fileLogger = fileLogger;

    DDLogDebug(@"%s", __PRETTY_FUNCTION__);

    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application
{
    DDLogDebug(@"%s", __PRETTY_FUNCTION__);
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    DDLogDebug(@"%s", __PRETTY_FUNCTION__);
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    DDLogDebug(@"%s", __PRETTY_FUNCTION__);
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    DDLogDebug(@"%s", __PRETTY_FUNCTION__);
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    DDLogDebug(@"%s", __PRETTY_FUNCTION__);
}
Cameron Lowell Palmer
  • 17,859
  • 4
  • 102
  • 114
  • 1
    In CocoaLumberjack 2.0, the type for the `ddLogLevel` is changed to `DDLogLevel` (https://github.com/CocoaLumberjack/CocoaLumberjack). So should it be `extern DDLogLevel ddLogLevel;` – franklsf95 Mar 17 '15 at 22:31
  • When exactly was the precompiled header deprecated? This seems a bit misleading. There is nothing wrong with them if they're used correctly. – Greg Feb 02 '17 at 16:28
  • They were removed from project defaults in Xcode 6 for good reason. I believe at the time Chris Lattner even mentioned the at WWDC that year as a bad idea that needed to go – Cameron Lowell Palmer Feb 02 '17 at 16:45
  • @Greg so in summary, as has been written elsewhere, PCH was an overused crutch, there is no need for them and Apple removed them from default projects to discourage/end their use. Modules resolved the one legitimate use case, importing Foundation. In the end, you can always establish a global header, but then you're in the business of globals. While you're at it, you can stuff significant amounts of code in AppDelegate. – Cameron Lowell Palmer Feb 03 '17 at 07:45
5

You can use this in your *.pch file to automatically get different global log levels depending upon your current build configuration.[for xcode 4+]

#ifdef DEBUG
  static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#else
  static const int ddLogLevel = LOG_LEVEL_WARN;
#endif

or If you need a different log level for every logger, you can easily achieve this using the DDLog +addLogger:withLogLevel: method.

[DDLog addLogger:[DDASLLogger sharedInstance] withLogLevel:LOG_LEVEL_INFO];
[DDLog addLogger:[DDTTYLogger sharedInstance] withLogLevel:LOG_LEVEL_DEBUG];

Defining a log level in every source file you mentioned has a benefit. You can use verbose logging level just for the part that you're currently working on. For rest part, you can use other level like info, warn, error.

DareDevil
  • 722
  • 6
  • 20
5

In order to dynamically inject log level (for example, from configuration file):

1) Create a new class named DDLogLevel with the following code:

#import "DDLogLevel.h"
#import "DDLog.h"

@implementation DDLogLevel

static int _ddLogLevel = LOG_LEVEL_VERBOSE;

+ (int)ddLogLevel
{
    return _ddLogLevel;
}

+ (void)ddSetLogLevel:(int)logLevel
{
    _ddLogLevel = logLevel;
}

@end

2) In DDLogLevel.h, find the row that contains the following statement:

#ifndef LOG_LEVEL_DEF
    #define LOG_LEVEL_DEF ddLogLevel
#endif

And replace it with:

#ifndef LOG_LEVEL_DEF
    #define LOG_LEVEL_DEF [DDLogLevel ddLogLevel]
#endif

3) Finally, call from your Initialization process (perhaps from appDelegate) to ddSetLogLevel with the desired level.

IdoT
  • 2,615
  • 1
  • 20
  • 33
4

For those using CocoaLumberjackSwift you can simply set the following global variable anywhere in your code:

dynamicLogLevel = .verbose

Discussion here

Jamie McDaniel
  • 1,709
  • 16
  • 14
3

Share my configuration for CocoaLumberjack 2.0.0 with global log level and optional local log level with preserved DynamicLogLevels feature.

My solution includes simple header file DSLogging.h (and it's counterpart) that import CocoaLumberjack.h and define convenience macros for setting up the files that use CocoaLumberjack log macros. Here is how you should use it:

  1. Import DSLogging.h header (two ways):
  2. Use DSLogLevelSetup... macros to set log level for file. Note: there should be macros in EACH source file that uses logging.

See documentation inside for more details. Download gist.

DSLogging.h header:

//
//  Created by DanSkeel on 23.04.15.

#import "CocoaLumberjack.h"

#define DS_LogScopeGlobal extern
#define DS_LogScopeLocal static
#define DS_LogMutableYes
#define DS_LogMutableNo const

#define DS_LogValueGlobal ;
#define DS_LogValueLocal(lvl) = lvl

#define DS_Setup_Log(scope, mutablility, value) scope mutablility DDLogLevel ddLogLevel value

/** To setup loggin enviroment for particular file use one of these macros
 *
 *  @note Use CocoaLumberjack as usual (https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/GettingStarted.md):
 *
 *  1. just import DSLoggin.h in source file instead of CocoaLumberjack.h
 *
 *  2. Use one of these macros to setup loggin enviroment for the file.
 *  Note: there should one of these macros in EACH file that uses CocoaLumberjack macroses.
 *  @example To enable logging for file with globally defined level you can make convinient snippet:
 *  @code
 *  #import "DSLogging.h"
 *  DSLogLevelSetupGlobal
 *  @endcode
 * 
 *  Use @b SetupGlobal to setup files that will use global level from @p DSLogging.m file
 *
 *  Use @b SetupMutableGlobal to be able to change global level at runtime (assign new level to ddLogLevel variable)
 *
 *  Use @b Setup(DDLogLevel) to set local log level
 *
 *  Use @b SetupMutable(DDLogLevel) to be able to modify local level at runtime ((assign new level to ddLogLevel variable))
 *
 *  This approach preserves a lot of CocoaLumberjack advantages. See SO https://stackoverflow.com/a/29837945/991816
 *
 *  @remarks details: these macros just help you define/reference ddLogLevel value. So if you
 *  see warning about <i> undeclared identifier </i> it should remind you to use one of these macros in this file.
 */
extern char optionClickMeToSeePrettyDoc;
#define DSLogLevelSetupMutableGlobal DS_Setup_Log(DS_LogScopeGlobal, DS_LogMutableYes, DS_LogValueGlobal)
#define DSLogLevelSetupGlobal        DS_Setup_Log(DS_LogScopeGlobal, DS_LogMutableNo,  DS_LogValueGlobal)
#define DSLogLevelSetupMutable(lvl)  DS_Setup_Log(DS_LogScopeLocal,  DS_LogMutableYes, DS_LogValueLocal(lvl))
#define DSLogLevelSetup(lvl)         DS_Setup_Log(DS_LogScopeLocal,  DS_LogMutableNo,  DS_LogValueLocal(lvl))

DSLogging.m source:

//
//  Created by DanSkeel on 23.04.15.

#import "DSLogging.h"

DDLogLevel ddLogLevel = DDLogLevelVerbose;

Why I think it's a good approach:

  1. It's a little better than just CocoaLumberjack

    • Global level (can be mutable)
    • Allows you to "override" global level by local level (can be mutable)
  2. It doesn't cut CocoaLumberjack functions

    • Uses variable to set level, so it can be used with advanced features of CocoaLumberjack.

I'm new to CocoaLumberjack and I can be too optimistic about my approach, would be glad to hear your critics if I lie at some point.

Community
  • 1
  • 1
DanSkeel
  • 3,536
  • 32
  • 53
0

The way I did it was inspired by this answer.. however This is how I did it differently so that I can have both a global level log level and be able to override the global log level within each file if I so chose:

  • Instead of calling the file Constants.h I called it GlobalDebugLevel.h. This is because it doesn't make sense to include any other global constants in this file, unless you really will always use the global debug level and have no use for file specific log levels.
  • In the files that I want to have it's own log level.. I simply comment out the `#import "GlobalLogLevel.h" and then include something like this:

static const int ddLogLevel = LOG_LEVEL_VERBOSE;

and everybody is happy :)

p.s. this is a .pch free solution.. inititally I tried that but then the compiler would complain that ddLogLevel is already defined whenever I wanted to override it at a file level

Community
  • 1
  • 1
abbood
  • 21,507
  • 9
  • 112
  • 218
0

There is a much easier way to solve this, you can set the log level at the Logger instantiation:

#ifdef DEBUG
  [DDLog addLogger:[DDTTYLogger sharedInstance] withLevel:DDLogLevelDebug];
#else
  [DDLog addLogger:[DDTTYLogger sharedInstance] withLevel:DDLogLevelError];
#endif

So there is no need for extra imports or .pch-file.

OliverD
  • 764
  • 5
  • 8
  • But still you will have this error: DDLogInfo(@"test info"); -> Use of undeclared identifier 'ddLogLevel' – LightMan Oct 08 '15 at 09:57
  • This works with Version 2.0.0, the update to 2.0.1 breaks this solution. This is discussed here: https://github.com/CocoaLumberjack/CocoaLumberjack/issues/542 – OliverD Oct 09 '15 at 10:43
0

Here is a dynamic logging example, which uses DanSkeels DSLogging code from below:

GFDPerson.h

#import <Foundation/Foundation.h>

@interface GFDPerson : NSObject{
@protected
    NSArray         *pLogLevelNames;
    NSArray         *pLogLevelKeys;
    NSDictionary    *pLogLevels;
}

-(void)logPerson;
-(void)setLogLevel:(NSUInteger)logLevel;

@end

GFDPerson.m

#import "GFDPerson.h"
#import "DSLogging.h"

DSLogLevelSetupMutable(DDLogLevelWarning);

@implementation GFDPerson

-(id)init{
    if (self = [super init]) {
        pLogLevelNames = [[NSArray alloc] initWithObjects:
                          @"no logging",
                          @"only errors",
                          @"errors and warnings",
                          @"errors, warnings and infos",
                          @"verbose",
                          nil];

        pLogLevelKeys = [[NSArray alloc] initWithObjects:
                         [[NSNumber numberWithInteger:DDLogLevelOff]stringValue],
                         [[NSNumber     numberWithInteger:DDLogLevelError]stringValue],
                         [[NSNumber     numberWithInteger:DDLogLevelWarning]stringValue],
                         [[NSNumber     numberWithInteger:DDLogLevelInfo]stringValue],
                         [[NSNumber     numberWithInteger:DDLogLevelVerbose]stringValue],
                         nil];

        pLogLevels = [[NSDictionary alloc]initWithObjects:pLogLevelNames
                                                  forKeys:pLogLevelKeys];

        return self;
    }
    return nil;
}

-(void)setLogLevel:(NSUInteger)logLevel{
    ddLogLevel = logLevel;
}

-(void)logPerson{

    NSLog(@"Person is logging with Loglevel: %@",[pLogLevels valueForKey:    [[NSNumber numberWithInteger:ddLogLevel]stringValue]]);
    DDLogVerbose(@"Person log verbose");
    DDLogInfo(@"Person log info");
    DDLogWarn(@"Person log warning");
    DDLogError(@"Person log error");
    DDLogDebug(@"Person log debug");
}

@end

main.m

#import <Foundation/Foundation.h>
#import "DSLogging.h"
#import "GFDPerson.h"

DSLogLevelSetupMutable(DDLogLevelError);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        [DDLog addLogger:[DDASLLogger sharedInstance]];
        [DDLog addLogger:[DDTTYLogger sharedInstance]];

        ddLogLevel = DDLogLevelWarning;
        NSLog(@"Warning:");
        DDLogWarn(@"WARNING LOG!");
        DDLogError(@"ERROR LOG!");
        DDLogVerbose(@"VERBOSE LOG!");

        ddLogLevel = DDLogLevelError;
        NSLog(@"Error:");
        DDLogWarn(@"WARNING LOG!");
        DDLogError(@"ERROR LOG!");
        DDLogVerbose(@"VERBOSE LOG!");

        ddLogLevel = DDLogLevelOff;
        NSLog(@"Off:");
        DDLogWarn(@"WARNING LOG!");
        DDLogError(@"ERROR LOG!");
        DDLogVerbose(@"VERBOSE LOG!");

        ddLogLevel = DDLogLevelVerbose;
        NSLog(@"Verbose:");
        DDLogWarn(@"WARNING LOG!");
        DDLogError(@"ERROR LOG!");
        DDLogVerbose(@"VERBOSE LOG!");

        ddLogLevel = DDLogLevelOff;
        GFDPerson *personA = [[GFDPerson alloc] init];
        [personA logPerson];

        [personA setLogLevel:DDLogLevelVerbose];
        [personA logPerson];

        [personA setLogLevel:DDLogLevelError];
        [personA logPerson];

    }
    return 0;
}

output of this code:

Warning:
WARNING LOG!
ERROR LOG!
Error:
ERROR LOG!
Off:
Verbose:
WARNING LOG!
ERROR LOG!
VERBOSE LOG!
Person is logging with Loglevel: errors and warnings
Person log warning
Person log error
Person is logging with Loglevel: verbose
Person log verbose
Person log info
Person log warning
Person log error
Person log debug
Person is logging with Loglevel: only errors
Person log error

Please comment, If I misunderstood or misused something...

user808667
  • 303
  • 3
  • 10
-1

As answered by FreeAsInBeer, you can define this constant in .pch file. You may do like this in .pch file.

// include Lumberjack header file 
#import <Lumberjack/Lumberjack.h>

// define ddLogLevel constant
static const int ddLogLevel = LOG_LEVEL_VERBOSE;

Im my implement, I create a new header file(e.g. mylog.h) for custom Lumberjack settings. in this way, I use #import statement in my .pch file for including mylog.h. This custom header file may like this.

// include Lumberjack header file
#import "Lumberjack.h" 

#undef ZEKit_LOG_LEVEL
#if defined (DEBUG) && (DEBUG == 1)
#define ZEKit_LOG_LEVEL LOG_LEVEL_VERBOSE
#else
#define ZEKit_LOG_LEVEL LOG_LEVEL_WARN
#endif

static const int ddLogLevel = ZEKit_LOG_LEVEL;

// ... Other custom settings
ZwEin
  • 104
  • 1
  • 3
-1

There's an example app included with CocoaLumberjack that shows how to set a global log level that you can find here https://github.com/robbiehanson/CocoaLumberjack/tree/master/Xcode/GlobalLogLevel

PaulCapestany
  • 278
  • 2
  • 10
  • 1
    Please, avoid answers where the only source of information is an external link. At least quote the essential points of the resource, in order to avoid link rotting. – electronix384128 Apr 22 '14 at 13:48