Write stderr on iPhone to both file and console - iphone

I'm following the suggestion in the answer here for redirecting NSLog output on an iOS device to a file, which works great. The problem is that it no longer shows up in the console on the device. What I'd really like is a way to tee the stderr stream to both the console and the file. Does anyone have an idea how to do that?

I found an acceptable answer on another thread (NSLog() to both console and file).
The solution provided there is to only redirect to a file if a debugger is not detected, like this:
if (!isatty(STDERR_FILENO))
{
// Redirection code
}
Thanks to Sailesh for that answer.

Once you freopen() the file descriptor, you can read from it and do as you please with the data. Some ideas from this will be useful to you.
You could either write it back out to stdout, or try to write directly to /dev/console. I've never tried to open /dev/console on an iPhone, but I'm guessing it's possible despite being outside of the sandbox. I'm not sure how the app review process will treat it.

Or you can redirect to a TCP socket and view on a remote telnet client. No need for XCode this way!
Basically:
Create a standard C function which calls an Obj-C static method:
void tcpLogg_log(NSString* fmt, ...)
{
va_list args;
va_start(args, fmt);
[TCPLogger tcpLog:fmt :args];
va_end(args);
}
The static Obj-C method:
(void)tcpLog:(NSString*)fmt :(va_list)args
{
NSLogv(fmt, args);
if(sharedSingleton != nil && sharedSingleton.socket != nil)
{
NSString *time = [sharedSingleton.dateFormat stringFromDate:[NSDate date]];
NSString *msg = [[NSString alloc] initWithFormat:fmt arguments:args];
mach_port_t tid = pthread_mach_thread_np(pthread_self());
NSString *str = [NSString stringWithFormat:#"%#[%X]: %#\r\n", time, tid, msg];
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
[sharedSingleton.socket writeData:data
withTimeout:NETWORK_CLIENT_TIMEOUT_PERIOD
tag:0];
}
}
Then in your .pch file, add the following lines to override NSLog()
define NSLog(...) tcpLogg_log(__VA_ARGS__);
void tcpLogg_log(NSString* fmt, ...);
Of course more details are required to handle the TCP Socket. Working source code is available here:
https://github.com/driedler/iOS-TCP-Logger/wiki/About

Related

Incorrect NSStringEncoding value 0x0000 detected

I am using ASIHttpRequest library in iphone and when i try to do a GET request on the below URL i get this message and the request fails, please anyone if he/she has encountered this problem or know a possible solution to it please let me know. Secondly if i paste the link in the browser it works perfectly fine.
[URL]
http://www.rugsale.com/iphone/app/?type=product_list&params=id%3D334&ver=1.0&key=kaoud
[ERROR]
Incorrect NSStringEncoding value 0x0000 detected. Assuming NSStringEncodingASCII. Will stop this compatiblity mapping behavior in the near future.
Thx in advance
Regards
Syed Arsalan Pervez
www.saplogix.net
This notably happens if you call [asiHttpRequest getResponseString] before a valid response has been returned and the request encoding has been set (i.e. couldn't connect to server).
The easiest way to workaround this warning is by editing the ASIHTTPRequest class, remove the #synthesize responseEncoding and adding a simple custom getter/setter so you can return the default encoding if the response encoding isn't set:
- (NSStringEncoding) responseEncoding
{
return responseEncoding || self.defaultResponseEncoding;
}
- (void) setResponseEncoding:(NSStringEncoding)_responseEncoding
{
responseEncoding = _responseEncoding;
}
There's also a more specific workaround for the getResponseString method, which I think is the only place that uses the encoding without checking for a value - since the encoding should be set for any non-zero length response:
- (NSString *)responseString
{
NSData *data = [self responseData];
if (!data) {
return nil;
}
// --- INSERT THIS BLOCK ---
// If the 'data' is present but empty, return a simple empty string
if (data.length == 0) {
return #"";
}
//assert(self.responseEncoding); // if you're into runtime asserts, uncomment this
// --- END OF BLOCK TO INSERT ---
return [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:[self responseEncoding]] autorelease];
}
For me the problem was timeout wasn't enough. So it returns an empty response. So it gives this error because you'r trying to do something with an empty response. I increased the time for it. So it solved my problem.[request setTimeOutSeconds:200];
this is just saying that the encoding was nil, when a value was expected. Try doing a search for "encoding:nil" or "encoding:NULL" in your source code and replace it with a valid encoding, for example NSStringEncodingASCII...
This looks like something that's cropped up in the 4.2 SDK. I didn't see this until I updated my development environment myself.
All-Seeing Interactive will probably have to update the code to ensure compatibility.
The error is where you are creating a string. You don't supply an encoding which is required.

Using va_list and getting EXC_BAD_ACCESS

Similar to how NSLog takes variable argument list, I want to create my own method.
I have my method declared like this but when I try to access the 'args' variable, I get an EXEC_BAD_ACCESS. What is that I'm not doing correctly here?
- (void)info:(NSString *)formatString, ...
{
va_list args;
va_start(args, formatString);
NSLog(#"formatString value: %#", formatString);
// The following line causes the EXEC_BAD_ACCESS
NSLog(#"args value: %#", args);
// This is what I'm trying to do:
NSLog(formatString, args);
va_end(args);
}
I was following the 'va_list in Cocoa' section from this blog:
http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html
There are a couple of mistakes in your code. Firstly, args cannot be printed directly as is. It is a reference to several arguments, and trying to print it using NSLog("%#") will not work. What you can do is use NSLogv() to print it (e.g. NSLogv(format, args);) instead.
Or you can do what I do, and use this function:
void print (NSString *format, ...) {
va_list args;
va_start(args, format);
fputs([[[[NSString alloc] initWithFormat:format arguments:args] autorelease] UTF8String], stdout);
va_end(args);
}
The "%#" format directive takes an argument, interprets it as an Objective-C object and sends it the "description" selector. That needs to return a NSString which is printed.
So you code is trying to execute ‘[args description]‘, but args is not a Objective-C object, it's of type ‘va_list‘. Hence the exception.
See your link, the implementation of "setContentByAppendingStrings:" shows how do get the arguments out of your va_list.
Morning lads,
I just have been confronted with the very similar issue. Here is what I was doing:
+ (void) l:(D3LogLevel)p_logLevel s:(NSString *)p_format, ...
{
if (p_logLevel >= logLevel) {
va_list v_args;
va_start(v_args, p_format);
NSLog(#"[%d] %#", p_logLevel, [NSString stringWithFormat:p_format, v_args]);
va_end(v_args);
}
}
Which, as DarkDust here has described accurately, uses v_args as an Objective-C object when it is not. Here is the culprit call:
[NSString stringWithFormat:p_format, v_args]
Hence the modification to take a va_list:
[[NSString alloc] initWithFormat:p_format arguments:v_args]
Making use of the appropriate method initWithFormat which signature is:
- (id)initWithFormat:(NSString *)format arguments:(va_list)argList
We can see that the type is right and everything becomes crystal clear. The full rewritten method is then:
+ (void) l:(D3LogLevel)p_logLevel s:(NSString *)p_format, ...
{
if (p_logLevel >= logLevel) {
va_list v_args;
va_start(v_args, p_format);
NSLog(#"[%d] %#", p_logLevel, [[NSString alloc] initWithFormat:p_format arguments:v_args]);
va_end(v_args);
}
}
It works like a charm!

if-query: if (Nslog isEqualtoString #"...") - How can I make this?

I want my app to do something when the last NSLog has a certain string. I thought I could realize this with an if-query and isEqualtoString, but how can I make this?
Sorry for my bad English ;)
Maybe I don't understand what you're trying to do, but you can just create the string somewhere, log it, and then test it:
NSInteger _someInt = 2;
NSString *_someString = #"bananas";
NSString *_stringToBeLogged = [NSString stringWithFormat:#"%d %#", _someInt, _someString];
NSLog(#"%#", _stringToBeLogged);
if ([_stringToBeLogged isEqualToString:#"2 bananas"]) {
NSLog(#"I logged two bananas...");
}
You could consider creating your own custom log function which calls NSLog() after checking for your string constant. This would keep your code a bit cleaner if you want this functionality in multiple places and also allows you to easily extend the logging function further if desired.

How to get rid of all this garbage in NSLog?

When I use
NSLog(#"fooBar")
it prints out a lot of stuff I don't want:
2009-09-03 13:46:34.531 MyApp[3703:20b] fooBar
Is there a way to print something to the console without this big prefix?
I want to draw a table and some other things in the console so that space is crucial...
This is from Mark Dalrymple at borkware.com
http://borkware.com/quickies/single?id=261
A Quieter NSLog (General->Hacks) [permalink]
// NSLog() writes out entirely too much stuff. Most of the time I'm
// not interested in the program name, process ID, and current time
// down to the subsecond level.
// This takes an NSString with printf-style format, and outputs it.
// regular old printf can't be used instead because it doesn't
// support the '%#' format option.
void QuietLog (NSString *format, ...)
{
va_list argList;
va_start (argList, format);
NSString *message = [[[NSString alloc] initWithFormat: format
arguments: argList] autorelease];
printf ("%s", [message UTF8String]);
va_end (argList);
} // QuietLog
This is a variation on the borkware quickies: http://cocoaheads.byu.edu/wiki/a-different-nslog . It prints the file and line number of where the log takes place. I use it all the time.
NSLog just prints to strerr. Use fprintf instead.
fprintf(stderr, "foobar");

iPhone SDK - NSStreamEventHasBytesAvailable / appendBytes: crashing

Disclaimer: I am an Xcode / iPhone SDK Noob.
I am trying to establish a client-side TCP/IP connection to an existing server. Upon connection, I expect to receive some data about the server (version, etc.).
When my connection is made, the NSStreamEventOpenCompleted event fires, so I know the connection is made. Next the NSStreamEventHasBytesAvailable event fires and I am executing the following code. The value 71 (int) is stored in len, which I believe is correct. However, the line
[data appendBytes:&buffer length:len];
is crashing (I think). There is no actual error thrown but I do see __TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION__ even though I have clearly added error catching:
case NSStreamEventHasBytesAvailable:
{
NSMutableData *data=[[NSMutableData alloc] init];
uint8_t *buffer[1024];
unsigned int len=0;
len=[(NSInputStream *)stream read:buffer maxLength:1024];
if(len>0){
#try{
[data appendBytes:&buffer length:len];
}
#catch(NSException *ex){
NSLog(#"Fail: %#", ex);
}
[statusLabel setText:[data stringValue]];
//[bytesRead setIntValue:[bytesRead intValue]+len];
}else{
NSLog(#"No Buffer");
}
break ;
}
This is a problem down at the C level: you're confused about buffers and pointers.
This code:
uint8_t *buffer[1024];
gives you a stack buffer of 1024 pointers to uint8_ts, which is almost certainly not what you want. Instead:
uint8_t buffer[1024];
Later on, you're passing the address of your pointer on the stack to -[NSMutableData appendBytes:length:], which again is not what you want: as in the documentation, pass the first element:
[data appendBytes:buffer length:len];
There's a very thorough programming guide with complete code for what you're trying to do, you may want to reference it.
As for __TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION__, memory corruption and bad pointer dereferencing isn't something you can catch with an Objective-C #try/#catch; it's much lower-level. On the other hand, you can still catch this in the debugger if you turn debugging on.
declare your buffer as:
uint8_t buffer[1024];
and do the append as:
[data appendBytes:buffer length:len];
For completeness read:maxLength: returns an NSInteger and will be negative if there was an error. Assigning it to an unsigned int discards this and can cause a crash.