How can I store custom data fields as part of a log message?

68 views
Skip to first unread message

dhil...@surgeforward.com

unread,
Mar 1, 2015, 3:46:24 PM3/1/15
to cocoalu...@googlegroups.com

Hi All,

Here's an issue that I've been trying to work around for a few days.

Here's my use-case:

I am using CocoaLumberjack to log data in my app. I have six different severity levels. Some of the data is trivial (severity 6), some is not (severity 1). I have two "loggers" such that all of the data gets logged to console and all of the data also gets sent to an API for storage. The API accepts log messages in JSON format. I've got the code that sends the logs to the API (a custom DDFileLoggerManager). So far, I have been using a custom DDLogFormatter to to turn my log messages into JSON objects that then get written to disk by the DDFileLogger. This custom format has thus far only been used by the file logger, so that the console messages are still fairly readable. (I wouldn't necessarily want to be piping thousands of JSON objects to the console.) This whole system has been working flawlessly so far.

Here's the problem:

For the version of the log data that's going to the API server, I sometimes need to also submit data statistics as part of the log message. I need to be able to attach key-value pairs to the log messages. My JSON objects will eventually look like this:

{
    'time': '2015-03-01T13:54:03-05:00', 
    'message': 'User logged in',
    'severity': 6,
    'data': {
        'username':'testuser',
        'deviceType': 'iPad Air 2'
    }
}

And finally, the Question

How can I get **CocoaLumberjack to allow me to store arbitrary key-value data as part of logging, and then retrieve this when doing custom formatting?**

The only solution I've come up with so far is to make a custom function that I use for all my logging needs. This function will take a data parameter (NSDictionary) and do all of the JSONification up-front:

+ (void) logWithSeverity: (uint) severity 
    data:(NSDictionary *) data 
    andFormat:(NSString *)formatString, ...
{
    va_list args;
    va_start(args, formatString);
    NSString *message = [[NSString alloc] initWithFormat:formatString 
        arguments:args];
    va_end(args);

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    NSLocale *enUSPOSIXLocale = 
        [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
    [dateFormatter setLocale:enUSPOSIXLocale];
    [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];

    if (data == nil) {
        data = @{};
    }

    // Since we are going to be using the pipe character as a data demarcation, 
    // we want to remove any that might naturally be occuring in the string
    NSDictionary *jsonDictionary = @{
     @"time": [dateFormatter stringFromDate:[NSDate date]],
     @"message": message,
     @"severity": [NSNumber numberWithUnsignedInt:severity],
     @"data": data
     };

    NSError* error;
    NSData *jsonData = [NSJSONSerialization 
        dataWithJSONObject:jsonDictionary options:0 error:&error];

    if(error) {
        NSLog(@"ERROR: We were unable to serialize JSON .");
        return;
    }


    NSString *jsonString = [[NSString alloc] initWithData:jsonData 
        encoding:NSUTF8StringEncoding];

    DDLogVerbose(jsonString);
}

I'll be the first to admit this is a terrible solution:

  • Since implementing it, I've had to turn off output to the console because everything being logged is now a big clunky JSON object that contains all sorts of data I have no desire to output to the console.
  • Since my severity level is now internal to the JSON object, I am no longer making use of the CocoaLumberjack logLevel concept. My logger will always be set to 'Verbose.' I'm thus getting rid of one of the biggest benefits of using a logging library at all.

Does anyone have a solution for me that is more elegant? In a perfect world, there would be a function like this: DDLogVerboseWithData(*someObject, @"User logged in."); and then that data would get stored as part of DDLogMessage and would be accessible via a custom formatter.

Thanks!
--Dave
Reply all
Reply to author
Forward
0 new messages