views:

148

answers:

3

I'm making a crash reporter, and I can read the crash reporter files just fine within ~/Library/Logs/CrashReporter.

However, I want to also send along with the report any console (NSLog) information that was printed to the console (the stuff that one can see both in Xcode and in Console.app). However, I want to make sure I get only the logs for my app. I don't want logs from other apps.

How does one get the console logs for a specific app?

+2  A: 

NSLog messages reported to the console go via a facility called asl (Apple System Logger). You can run a query to only get messages recorded by your processes: have a look at the asl_set_query(3) API.

Graham Lee
+2  A: 

Use the Apple System Logger API to search the system log for messages sent by your application.

Jim Dovey has published a Cocoa wrapper for ASL under a BSD license.

Note that the system log normally goes back about 24 hours, and may include multiple sessions and multiple runs of your app. Search for messages whose sender name is your app's name and whose process ID is your process's (getpid()).

Peter Hosey
Ok the Cocoa wrapper sounds nice to me :) However, do you have any sample code that demonstrates how I would use it in my case?Also, are you sure it's a good idea that I match against he PID? I ask because usually when the logs will be sent to me it's on the next run of the app when it crashed earlier. The PID changes on successive app runs right?
Enchilada
No, I don't have sample code. Read my blog posts to understand the concepts at work, then read the headers for Dovey's wrapper. The API he gave it pretty much exactly matches the standard ASL API. And yes, the PID is different for successive processes; that's why you want to search for it, so you only get your last process's output. You could store your last PID in user defaults.
Peter Hosey
Ok, thanks very much. Seems to be working fine. For future reference, I will post some code here to show how I did the things I needed done.
Enchilada
A: 

To make the app store the last few PIDs in an array in the user defaults, I wrote the following updateLastPids method. First, make sure to do, say,

#define kMaxNumberOfPids 5 //should be integer greater than zero!
#define ENLastPidsUserDefault @"ENLastPidsUserDefault";

By the way, I'm using 5 because I'm gonna let the app send me the logs for the last 5 runs, just in case the crash was a result of something getting messed up even earlier than on the last run; you can change 5 to 1 if you wish.

- (void)updateLastPids
{
  NSInteger currentPid = [[NSProcessInfo processInfo] processIdentifier];
  NSNumber *currentPidNumber = [NSNumber numberWithInt:currentPid];
  NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

  NSMutableArray *lastPidsArray = [[userDefaults arrayForKey:ENLastPidsUserDefault]
                                   mutableCopy];
  if (!lastPidsArray)
  {
    NSArray *newLastPidsArray = [NSArray arrayWithObject:currentPidNumber];
    [userDefaults setObject:newLastPidsArray forKey:ENLastPidsUserDefault];
  }
  else
  {
    if ([lastPidsArray count] == kMaxNumberOfPids)
    {
      [lastPidsArray removeObjectAtIndex:0]; //get rid of the oldest PID
      [lastPidsArray addObject:currentPidNumber];
      NSAssert([lastPidsArray count] == kMaxNumberOfPids, @"invalid count");
    }
    //In case I decrease kMaxNumberOfPids later on. (Or some PITA user added
    //stuff into the array by himself or herself!)
    else if ([lastPidsArray count] > kMaxNumberOfPids) 
    {
      [lastPidsArray removeObjectAtIndex:0];
      while ([lastPidsArray count] >= kMaxNumberOfPids)
        [lastPidsArray removeLastObject];
      [lastPidsArray addObject:currentPidNumber];
      NSAssert([lastPidsArray count] == kMaxNumberOfPids, @"invalid count");
    }
    else
    {
      [lastPidsArray addObject:currentPidNumber];
      NSAssert([lastPidsArray count] <= kMaxNumberOfPids, @"invalid count");
    }

    [userDefaults setObject:lastPidsArray forKey:ENLastPidsUserDefault];
  }
}

In my next answer I will show how I used the ASL Cocoa wrapper to get the console logs from the last PIDs (the PIDs should now be in the user defaults after running the above method).

Enchilada
“In my next answer …” You could just edit the answer to include that, and have one complete answer.
Peter Hosey
`removeLastObject` removes the last object. If you want the array to contain the last five, not the four oldest plus the last, then you need to remove *the first* object(s) (as you do elsewhere in this code). Moreover, you can cut out most of this `if` tree and the loop. The solution is simply to check whether the count is more than the maximum, then, if so, use `removeObjectsInRange:` to lop off the difference from the start of the array.
Peter Hosey
Ok thanks :)However, it has come to my attention that the PIDs are not really unique. They could be resused any time, say after a restart or when the PID counter overflows.Searching for the PIDs like this thus doesn't seem like a very safe way and could end up me getting logs from other apps than my own app, which I don't want.I'm thus thinking about first searching for my app's name, and *then* filter those results to only show the greatest 5 PIDs of those. I think that's safer instead of storing the PIDs and relying on them later.What do you think?
Enchilada
Nope—other way around. The last PID for your app may be less than the previous one, for example, so you should remember the last N PIDs instead of sorting the PIDs and using the highest N.
Peter Hosey