Capturing NSLog Output with Firebase Crashlytics - ios

I used to use Crashlytics long ago and there was a simple macro that could be used to capture all output from NSLog() to send it along with the crash report to Crashlytics. Very very helpful so that if a user has a crash, I can see the related logging that led up to it. I even had this working after Google gobbled up Crashlytics for awhile.
But a few years (many?) later and I'm trying to use Crashlytics in a new project and none of these macros are working anymore. The best I've come up with is the following :
#define NSLog(...) [[FIRCrashlytics crashlytics] logWithFormat:(__VA_ARGS__)]
However this generates compile warnings for every NSLog call and it does not appear to be compatible with anything either than NSStrings as arguments to the format. For instance the following will not compile...
NSLog (#"My Float Value : %f", myFloat);
I realize this is Objective C so my apologies to the Swift folk out there. Also I'm not sure if switching over to OSLog would actually help either. I'm just looking to capture the logging output so I can get some more information past just the stack trace. Otherwise this is no better than what TestFlight gives me and is therefore useless to add in for my use case.

Remove the parenthesis around the __VA_ARGS__ from the macro:
#define NSLog(...) [[FIRCrashlytics crashlytics] logWithFormat: __VA_ARGS__]
After doing that, I was able to call these NSLogs:
NSLog (#"My Float Value %f", 1.0);
NSLog (#"My Float string %s", "some string");
NSLog (#"My decimal Value %d", 963);
And this is the result I got in the crash report:

In order to resolve this for a call that has both a format string and values for that format string (NSLog (#"My Value %d", myValue);) and the case for just a single string (NSLog (#"Hello");) then the solution I found was this...
#define NSLog(__FORMAT__, ...) { NSString * str = [NSString stringWithFormat:__FORMAT__, ##__VA_ARGS__]; [[FIRCrashlytics crashlytics] log:str];}
However I ended up with this instead.
#define NSLog(__FORMAT__, ...) rrlog(__FORMAT__, ##__VA_ARGS__)
And this Class...
#interface RRLogger : NSObject
OBJC_EXTERN void rrlog(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2);
#end
#implementation RRLogger
void rrlog(NSString* format, ...)
{
if (!format) return;
va_list args;
va_start(args, format);
NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
NSLogv(format, args);
va_end(args);
[[FIRCrashlytics crashlytics] log:msg];
}
#end
The reason for this is because the Firebase -FIRDebugEnabled launch argument in my launch scheme wasn't outputting everything to the console. I'm not sure why but it was missing some of the log output. Hence the call to NSLogv in the above function.

Related

No NSLog output in distributed app under iOS 10

In iOS 10 Apple has made a change that NSLog() output is not emitted in distributed apps (enterprise, app-store).
Note that when running from Xcode, NSLog() works fine.
Is there a way to force debug for all apps (very useful under beta testing phase)?
Some clues appear here: NSLog on devices in iOS 10 / Xcode 8 seems to truncate? Why?
However, can we have a clear implementation for this?
Our solution depends on two questions:
Are we compiling using Xcode 8 and up? If yes, the new os_log is recognized. If not, we must fall back to existing NSLog behavior.
Are we running under iOS-10 and up? If yes, we can use the new logger. If not, we must fall back to existing NSLog behavior.
We will find the answer to question [1] on compile time. For [2] we must test in runtime.
Here is the implementation:
mylog.h
//only used to force its +load() on app initialization
#interface MyLog:NSObject
#end
#if !__has_builtin(__builtin_os_log_format)
//pre Xcode 8. use NSLog
#else
//we need this include:
#import <os/log.h>
#endif
void myLog(NSString *format, ...);
#ifdef DEBUG
#define NSLog(f, ...) myLog(f, ## __VA_ARGS__)
#else
#define NSLog(f, ...) (void)0
#endif
mylog.m
#implementation MyLog
BOOL g_useNewLogger = NO;
+(void)load
{
NSOperatingSystemVersion os_ver = [[NSProcessInfo processInfo] operatingSystemVersion];
if (os_ver.majorVersion >= 10) {
g_useNewLogger = YES;
}
NSLog(#"Use new logger: %#", g_useNewLogger? #"YES":#"NO");
}
#end
void myLog(NSString *format, ...)
{
va_list args;
va_start(args, format);
#if !__has_builtin(__builtin_os_log_format)
//pre Xcode 8. use NSLog
NSLogv(format, args);
#else
//Xcode 8 and up
if (g_useNewLogger) { // >= iOS 10
NSString *nsstr = [[NSString alloc] initWithFormat:format arguments:args];
os_log(OS_LOG_DEFAULT, "%{public}s", [nsstr cStringUsingEncoding:NSUTF8StringEncoding]);
} else { // < iOS 10
NSLogv(format, args);
}
#endif
va_end(args);
}
If you want a drop-in solution to get the logs of your apps, I recommend you to take a look to a tool we have created. It's called Bugfender and what it does is that it send all the app logs to our server so you can check them on our site.
It's very useful while doing beta testing, because your users can test the app and if they find a problem you will get the information of everything they have done in the app.

FB.AppRequest Crash

So I'm not sure if this is an issue with Unity or with the Facebook Unity SDK, or something I might be doing? It only started appearing recently, it was working perfectly fine up until I had to update Unity for iOS9 font issues.
The point at which it crashes in Xcode is:
+ (instancetype)instanceWithRequestID:(int)requestID
{
FBUnitySDKDelegate *instance = [[FBUnitySDKDelegate alloc] init];
instance->_requestID = requestID;
[g_instances addObject:instance]; // Breaks on this line. instance is nil
return instance;
}
And the code I am using for the AppRequest is
public void RequestLivesFromFriends(string[] friendIds)
{
if(!FB.IsLoggedIn)
{
LoginToFacebook ();
return;
}
FB.AppRequest(
"Please send me a life!",
Facebook.Unity.OGActionType.ASKFOR,
livesIdValue,
friendIds,
"RequestLife",
"Request a life from your friends",
requestLifeCallback
);
}
Is there currently an issue with the SDK's? Or am I just doing something wrong?
Well, I found the solution myself in the end.
I had -fno-objc-arc set as the Compiler Flag on FBUnitySDKDelegate.m
Apparently, having that on with the more recent versions of the SDK (or maybe something else was causing it, I'm not exactly sure) causes the NSMutableArray g_instances to be converted to an NSString. So when the code tries to add the FBUnitySDKDelegate object 'instance' to g_instances, it is trying to call addObject on an NSString, passing in an FBUnitySDKDelegate, which obviously doesn't work.
So yeah, if you have this problem, check for Compiler Flags on the file.

Can NSLog be disabled from appearing in device's console? [duplicate]

This question already has answers here:
How do I disable NSLog?
(15 answers)
Closed 8 years ago.
I have a builded application that is running on device. I open device's console view in XCode's Organizer window. I assume (for the sake of this question) that NSLog(#"Some string") gets called.
Is there any way, may be an option in device, or application's settings, that would disable this log from appearing in console?
Edit: I'm not interested in replacing NSLog by other solution that can achieve this effect. The purpose of this question is to fully understand NSLog's functionality.
(Thanks to #MartinR for encouraging me to pull my finger out on this answer and to correctly identify that you cannot just close stdout/stderr, as the next open() will re-use those file descriptors, but to redirect stdout/stderr to the infamous /dev/null).
logControl.h:
#pragma once
extern void stopLogging();
extern void startLogging();
logControl.c:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
static int loggingStopped = 0;
static int oldStdout = -1;
static int oldStderr = -1;
void stopLogging() {
if (!loggingStopped) {
oldStdout = dup(1);
oldStderr = dup(2);
int devNull = open("/dev/null", O_WRONLY);
dup2(devNull, 1);
dup2(devNull, 2);
close(devNull);
loggingStopped = 1;
}
}
void startLogging() {
if (loggingStopped && oldStdout >= 0 && oldStderr >= 0) {
dup2(oldStdout, 1);
close(oldStdout);
oldStdout = -1;
dup2(oldStderr, 2);
close(oldStderr);
oldStderr = -1;
loggingStopped = 0;
}
}
This works at runtime, not compile time, which I believe is what you are asking. Simply call stopLogging() or startLogging() as required.
NOTE: There is no error-checking to speak of, so that could be improved perhaps.
Add this line given below in your .pch file in Xcode.
#define NSLog(...)
It will disable all NSLogs.
for more alternatives see the link
#if TARGET_IPHONE_SIMULATOR
//Simulator
#else
// Device
#define NSLog
#endif
Add this in your .pch file this will disable NSLog only for device alone not for simulator.
Add below code to .pch file
#ifdef DEBUG
# define NSLog(...) NSLog(__VA_ARGS__)
#else
# define NSLog(...) /* */
#endif
And in Build Settings, search "Preprocessor Macros" and remove "DEBUG=1" written in it.
and Thats it, you will not see any logs in your console now.

Advice on Debugging a UIAppFonts Issue on iPad

I recently found this post which seems to describe the same problem I am having with adding my own ttf. My app freezes. However, a few things about the post confuse me, and I'd love to get some help deciphering it.
The post is here: http://web.archiveorange.com/archive/v/nagQXB5eX3YsQpevIXMk
The relevant passages that I'm trying to understand are below:
Attaching the debugger and pausing
the application reveals a not
especially helpful call stack:
0 0xffff028f in __spin_lock
1 ??
Specific code that is causing the
problem:
CTFontCollectionRef collection =
CTFontCollectionCreateFromAvailableFonts(NULL);
CFArrayRef fonts =
CTFontCollectionCreateMatchingFontDescriptors(collection);
for(id fontDescRef in (NSArray
*)fonts) { CFStringRef fontName = CTFontDescriptorCopyAttribute((CTFontDescriptorRef)fontDescRef,
kCTFontNameAttribute); NSLog(#"%#",
fontName); CFRelease(fontName); }
CFRelease(fonts);
Execution never moves beyond the
second line.
Question: How did he figure out what line and function was causing the problem? Was this something having to do with displaying the disassembly, showing it in mixed mode, or looking up a hex value in a map file? I'd like to learn how this was done.
Having been asked to supply a demo and
hence having investigated further,
I've found that the problem manifests
if the code is in my
application:didFinishLaunchingWithOptions:
but not if I have the same stuff in
the viewDidLoad of my initial view
controller.
Question: what is 'the code' he's referring to here? The problem had to do with adding custom fonts through a plist value, so I'm not sure what he could be referring to or how I can workaround my issue.
Please Help!
Well, nothing on figuring out this post, but I did find a workaround posted by someone on the apple dev forums. Basically, calling this from the applicationDidFinishLaunching fn:
- (NSUInteger) loadFonts {
NSUInteger newFontCount = 0;
NSBundle *frameworkBundle = [NSBundle bundleWithIdentifier:#"com.apple.GraphicsServices"];
const char *frameworkPath = [[frameworkBundle executablePath] UTF8String];
if (frameworkPath) {
void *graphicsServices = dlopen(frameworkPath, RTLD_NOLOAD | RTLD_LAZY);
if (graphicsServices) {
BOOL (*GSFontAddFromFile)(const char *) = dlsym(graphicsServices, "GSFontAddFromFile");
if (GSFontAddFromFile)
for (NSString *fontFile in [[NSBundle mainBundle] pathsForResourcesOfType:#"ttf" inDirectory:nil])
newFontCount += GSFontAddFromFile([fontFile UTF8String]);
}
}
return newFontCount;
}
and making sure to include dlcfn.h and to cast the result of dlsym to the following:
(BOOL()(const char))
I did not alter the original post just in case this error was just something that affected me.

How to get IMEI on iPhone?

I want to get IMEI on iPhone. I try to use the following code:
#import "Message/NetworkController.h"
NetworkController *ntc=[[NetworkController sharedInstance] autorelease];
NSString *imeistring = [ntc IMEI];
But NetworkController is not found.
I also find that I can get uniqueIdentifier using:
UIDevice *myDevice = [UIDevice currentDevice];
NSString *identifier = myDevice.uniqueIdentifier;
But this cannot help me to get IMEI.
How to get IMEI on iPhone?
You can't get IMEI on iPhone anymore. You may have to use UDID instead. See other answers.
In the past, you could use the header file "Message/NetworkController.h" posted on ericasadun.com.
http://ericasadun.com/iPhoneDocs300/_network_controller_8h-source.html (It's been removed now)
You would add the NetworkController.h file and the private framework "Message.framework" to your project, then import that header file to use the original method I found to get the imei number:
NetworkController *ntc = [NetworkController sharedInstance];
NSString *imeistring = [ntc IMEI];
That hack doesn't work anymore. App will be rejected by Apple.
You can't find the IMEI programmatically on the iPhone.
There is no official way to get it, but in Apples private framework CoreTelephony.framework there is a method CTGetIMEI that might help you.
I'm sure there are reasons you can't do this easily. Auto-unlocker app? I don't think so. I'm sure Apple and AT&T wouldn't like it too much either.
*#06# is the way to get it manually.
I made a search only here in stackoverflow and the conclusion is that Apple dont allow anymore to find phone EMEI after iOS 6.
You can identify a device using UDID.
I found my answer here https://stackoverflow.com/a/19927376/2283308
add a head file "CoreTelephony.h" to your project
CoreTelephony.h
struct CTServerConnection
{
int a;
int b;
CFMachPortRef myport;
int c;
int d;
int e;
int f;
int g;
int h;
int i;
};
struct CTResult
{
int flag;
int a;
};
struct CTServerConnection * _CTServerConnectionCreate(CFAllocatorRef, void *, int *);
void _CTServerConnectionCopyMobileIdentity(struct CTResult *, struct CTServerConnection *, NSString **);
add the following code to your class
#import "CoreTelephony.h"
struct CTServerConnection *sc=NULL;
struct CTResult result;
void callback() { }
now, you can get imei easily
NSString *imei;
_CTServerConnectionCopyMobileIdentity(&result, sc, &imei);
https://www.telesign.com/products/phone-id
There is an SDK by a company called TeleSign that can get a device's IMEI number (in addition to a list of other things such as carrier, the home address of the device's owner, etc.). Some of the biggest apps in the store use this provider (like Tinder and Evernote). I don't work for the company or ever have, or ever want to, and I've never used any of their products. I also don't know if Apple would reject this API usage. But if you want a Swift/Objecive-C interface for getting an IMEI number, this is one.

Resources