Sasha Grievus
Sasha Grievus

Reputation: 2686

How to catch stderr output and send messages to a remote log file via a POST request in objective c?

I have a simple webservice online listening for post requests. It writes to a local file strings sent to it as parameters in the body.

Now, I have a ios app and I want to send each message of the stderr of the app to the webservice to log errors on the online file and be able to see app errors in real time, remotely.

I know I can redirect stderr messages to a file (local to device) like this

+ (void) redirectNSLogToDocuments {
    NSArray *allPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [allPaths objectAtIndex:0];
    NSString *pathForLog = [documentsDirectory stringByAppendingPathComponent:@"errLog.txt"];
    freopen([pathForLog cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
}

I know i can send the post request like this, and so write the string 'errorString' to the remote log

    NSString *errorString=@"errorIOS";
    NSString *errorData =[NSString stringWithFormat:@"errorString=%@",errorString];
    NSData *postData = [errorData dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
    NSString *postLength = [NSString stringWithFormat:@"%d",[postData length]];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:@"http://mywebserviceurl"]];
    [request setHTTPMethod:@"POST"];
    [request setValue:postLength forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:postData];
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];

But if I want to redirect the stderr messages so that each time a message of error is raised it triggers the post request to my webservice and writes the message to the remote log instead of writing to the device local file?

Upvotes: 0

Views: 119

Answers (1)

Sasha Grievus
Sasha Grievus

Reputation: 2686

The stderr can be catched attaching a pipe with this code

NSPipe* pipe = [NSPipe pipe];
NSFileHandle* pipeReadHandle = [pipe fileHandleForReading];
dup2([[pipe fileHandleForWriting] fileDescriptor], fileno(stderr));
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [pipeReadHandle fileDescriptor], 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
dispatch_source_set_event_handler(source, ^{
    void* data = malloc(4096);
    ssize_t readResult = 0;
    do
    {
        errno = 0;
        readResult = read([pipeReadHandle fileDescriptor], data, 4096);
    } while (readResult == -1 && errno == EINTR);
    if (readResult > 0)
    {
        //AppKit UI should only be updated from the main thread
        dispatch_async(dispatch_get_main_queue(),^{
            NSString* stdOutString = [[NSString alloc] initWithBytesNoCopy:data length:readResult encoding:NSUTF8StringEncoding freeWhenDone:YES];
            NSAttributedString* stdOutAttributedString = [[NSAttributedString alloc] initWithString:stdOutString];
            [appFunctions postDataToRemoteLogWithMessage:stdOutAttributedString];
        });
    }
    else{free(data);}
});
dispatch_resume(source);

So adding the function, the result can be achieved

+ (NSString *) postDataToRemoteLogWithMessage:(NSString *)errorString{
    // Send post request to webservice
    NSString *errorData =[NSString stringWithFormat:@"errorString=%@",errorString];
    NSData *postData = [errorData dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
    NSString *postLength = [NSString stringWithFormat:@"%d",[postData length]];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:@"http://mywebserviceurl"]];
    [request setHTTPMethod:@"POST"];
    [request setValue:postLength forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:postData];
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    if(conn) {
        //NSLog(@"Connection Successful");
    } else {
        //NSLog(@"Connection could not be made");
    }
}

Upvotes: 2

Related Questions