I have a project where all errors and warnings were written in logs in classes where they occurred. I started to use crashlytics. And tried to combine all the logic connected to logging and errors to a specific class. And force this class to decide where to report an error (error + warning) and where to log warnings. But when I made it I started to receive all the errors as they have occurred in this class. Is it possible to rename errors? For example, manually add activity name in the report so I will get different errors and not the same error all the time. Not like on the picture.
class AppLogs {
companion object CrashReport {
private fun reportToCrashlytics(
errorMessage: String,
user: String = "user id",
log: String = "log",
additionalInfoTitle: String = "title",
additionalInfoMessage: String = " message"
) {
FirebaseCrashlytics.getInstance().log(log)
FirebaseCrashlytics.getInstance().setUserId(user)
FirebaseCrashlytics.getInstance()
.setCustomKey(additionalInfoTitle, additionalInfoMessage)
FirebaseCrashlytics.getInstance().recordException(throw Exception(errorMessage))
}
private fun reportLog(errorMessage: String) {
Timber.e(errorMessage)
}
fun report(errorMessage: String, errorType: Char) {
if (errorType == 'e') {
reportLog(errorMessage)
reportToCrashlytics(errorMessage)
} else {
reportLog(errorMessage)
}
}
}
}
Answer for the actual issue
... manually add activity name in the report so I will get different errors and not the same error all the time ...
The answer is short - no it is not possible (without enormous effort).
But why?
Crashlytics uses the stack trace of the throwable object which was reported. Using this stack allows us to get reports that track the exception right to the line where it occurred.
It means that to create another issue record in the list of issues you have to modify the stack trace of the throwable object.
TrimmedThrowableData is the class that collects stack trace data to report. As you can see it gets unmodified stack trace of the throwable.
Most likely the only solution
If you want to get different stack trace - create Exception objects where reportToCrashlytics is used and pass these objects as arguments. Or throw and catch exceptions, and report exceptions that were caught.
Previous answer (bug found)
You are throwing the exception making the application crash. You should create Exception object and just pass it in as an argument.
If you open IDE you will see that the line where throw is used has recordException method highlighted and if you hover a cursor over it you will see the following:
It means that you never execute recordException because arguments are evaluated first and then passed into the function. But the function is never reached because throw crashes the application.
Do not use throw keyword:
FirebaseCrashlytics.getInstance().recordException(Exception(errorMessage))
You can change Your Stack trace like this :
public class CustomException extends Exception {
public CustomException(String message, int lineNumber) {
super(message);
StackTraceElement[] stackTrace = getStackTrace();
StackTraceElement[] newStackTrace = new StackTraceElement[stackTrace.length + 1];
System.arraycopy(stackTrace, 0, newStackTrace, 1, stackTrace.length);
newStackTrace[0] = new StackTraceElement("className", "methodName", "fileName", lineNumber);
setStackTrace(newStackTrace);
}
}
Related
Following code works fine when there is data in the Firestore database, but, my app crashes when there is no data with the exception NullPointerException. How can I handle it? This function is called when the user types in a Zip Code. The document name is ZipCode and the user may enter an invalid Zip and that is when the app crashes.
fun getZipDetails(zipCode: String, activity: AddressActivity) {
mFireStore.collection("zip_codes")
.document(zipCode)
.get()
.addOnSuccessListener { document ->
val details = document.toObject(ZipDetails::class.java)!!
activity.successGetZipDetails(details)
}
.addOnFailureListener { e ->
Log.e(
activity.javaClass.simpleName,
"Error while getting user details.",
e
)
}
}
It crashes because you use the Assert operator !! on the result of the toObject method. This method can return null, if the query has no result. Expect this result in your code with either a try catch block, or check if the result of the query exists:
val details = document.toObject(ZipDetails::class.java)
if(details != null) {
activity.successGetZipDetails(details) //you might need to write details!!, I'm not sure
} else {
//handle that this zip code was not valid
}
If you see the documentation of toObject method, it states
Returns the contents of the document converted to a POJO or null if
the document doesn't exist.
So in your case when there is no zip code document.toObject(ZipDetails::class.java) returns null, and then you add non-null assertion (!!), which throws null points exception.
To solve this simply remove the non-null assertion(!!) and update the successGetZipDetails to except nullable parameter as successGetZipDetails(details:ZipDetails?).
Since you are using the Kotlin programming language, a more idiomatic way for handling NullPointerException is to use safe calls:
To perform a certain operation only for non-null values, you can use the safe call operator together with let
In your particular case, that would be:
document.toObject(ZipDetails::class.java)?.let {
activity.successGetZipDetails(it)
}
Can't insert a new conversation to Telephony.Sms.Conversations.CONTENT_URI.
Keep getting a Caused by: java.lang.NullPointerException: Uri must not be null exception.
Even though the uri has a value of "content://sms/conversations".
Situation - logic flow
I receive an SMS message from an unknown number.
I insert a conversation for the unknown number (if one not found).
I insert the message and associate it with the newly created conversation.
Dev setup
For learning purposes, I am creating an Android SMS application with Kotlin.
Android Emulator with Pixel XL API 26.
The application I'm working on is set as the default SMS app.
Can successfully send, receive and insert (code below) individual messages.
createMessage() - works
Below is the working code I wrote to insert a message when the phone receives an SMS.
fun createMessage(
resolver: ContentResolver,
threadId: Number,
body: String,
sentByUs: Boolean
): Message? {
val messageType = if (sentByUs) Telephony.Sms.MESSAGE_TYPE_SENT else Telephony.Sms.MESSAGE_TYPE_INBOX
val values = ContentValues()
values.put(Telephony.Sms.THREAD_ID, threadId.toInt())
values.put(Telephony.Sms.BODY, body)
values.put(Telephony.Sms.TYPE, messageType)
val result = resolver.insert(Telephony.Sms.CONTENT_URI, values)
return this.getMessage(resolver, result)
}
createConversation() - doesn't work
Below is the code I'm working on which tries to insert a new conversation.
fun createConversation(
resolver: ContentResolver,
senderPhoneNumber: String,
latestMessageText: String,
latestMessageTimestamp: Long,
latestMessageIsOurs: Boolean,
latestMessageWasRead: Boolean
): Conversation? {
val wasRead = if (latestMessageWasRead) 1 else 0
val isOurs = if (latestMessageIsOurs) Telephony.Sms.Conversations.MESSAGE_TYPE_SENT else Telephony.Sms.Conversations.MESSAGE_TYPE_INBOX
val values = ContentValues()
values.put(Telephony.Sms.Conversations.ADDRESS, senderPhoneNumber)
values.put(Telephony.Sms.Conversations.BODY, latestMessageText)
values.put(Telephony.Sms.Conversations.DATE, latestMessageTimestamp)
values.put(Telephony.Sms.Conversations.TYPE, isOurs)
values.put(Telephony.Sms.Conversations.READ, wasRead)
// -------------------------------------------------------------------------
// ------ Throws java.lang.NullPointerException: Uri must not be null ------
// -------------------------------------------------------------------------
val result = resolver.insert(Telephony.Sms.Conversations.CONTENT_URI, values)
return this.getConversation(resolver, result)
}
While executing the resolver.insert() the application crashes with the following error message:
Caused by: java.lang.NullPointerException: Uri must not be null
With the debugger attached I can see that the uri does have a value.
Telephony.Sms.Conversations.CONTENT_URI is "content://sms/conversations"
How does Google do it?
Found out that Google open sources its common Android apps.
Here's the code for the Messaging application:
https://android.googlesource.com/platform/packages/apps/Messaging/
While analyzing DatabaseHelper.java I came to the conclusion that they create a whole separate database from scratch.
And then work with that troughout the lifetime of the application.
Which confused me even more - why don't they use resolver.insert()?
I may be wrong, the program was overwhelming for a new guy like me.
Question
If Telephony.Sms.Conversations.CONTENT_URI has a value of "content://sms/conversations", why do I get the exception?
To investigate such issue you need to turn off logcat filtering for your app. Then you'll find an SQLiteException that will help find out what's actually wrong.
The NPE refers to the fact that the insert failed and return a null Uri instead of the appropriate Uri path of the new element.
Cannot find the reason why I am getting "Null pointer exception" . I could give an explicit check to see if "getStorageState is null" but that doesnt explain why I am getting this error.
The error is :
java.lang.NullPointerException
at android.os.Environment.getStorageState(Environment.java:719)
at android.os.Environment.getExternalStorageState(Environment.java:694)
at com.ciqual.android.insight.sessionService.RemoveFiles(SessionService.java:664)
com.vyshas.android.sessionService.onEndSession(SessionService.java:460)
at : (the line that error points to is this :)(seen in jellybean 4.3 and kitkat)
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//
}
Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState) wouldnt cause a NPE while Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) does cause NPE and crashes the application.
I still dont know why getExternalStorageState is null eventhough I have permissions set but the solutions atleast wouldn't crash the application.
I have similar repot from Android 4.3 .
Source code is:
/**
* Gets the current state of the primary "external" storage device.
*
* #see #getExternalStorageDirectory()
*/
public static String getExternalStorageState() {
try {
IMountService mountService = IMountService.Stub.asInterface(ServiceManager
.getService("mount"));
final StorageVolume primary = getPrimaryVolume();
return mountService.getVolumeState(primary.getPath());
} catch (RemoteException rex) {
Log.w(TAG, "Failed to read external storage state; assuming REMOVED: " + rex);
return Environment.MEDIA_REMOVED;
}
}
I have nothing to do but try catch it.
I would like to have the whole stacktrace in Google Analytic's report's for my mobile application.
I wrote class that print's the stacktrace and put's it into string, but it doesn't work.
My custom ExceptionParser:
#Override
public String getDescription(String threadName, Throwable throwable) {
return threadName + " " + getStackTrace(throwable);
}
private String getStackTrace(Throwable throwable) {
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
throwable.printStackTrace(printWriter);
return result.toString();
}
And I set it like this:
EasyTracker.getTracker().setExceptionParser(new StacktraceExceptionParser());
The method below combines the entire stack trace into a single comma separated line, which may help in case Analytics returns just the first line. But there still may be a length limit so it may be prudent to do filtering to eliminate items you do not need (see comment)
public String getCombinedStackTrace(Throwable aThrowable) {
final StringBuilder result = new StringBuilder();
result.append(aThrowable.toString());
result.append(',');
String oneElement;
for (StackTraceElement element : aThrowable.getStackTrace() ) {
// you can do some filtering here, selecting only the elements you need
oneElement = element.toString();
result.append( oneElement );
result.append( ",");
}
return result.toString();
}
I second Nikolay's comment about using an error reporting library. I found it to be tremendously helpful.
I know this thread is old but I am trying to figure out how to get this working, but just for completeness there is a useful method on Log that does what you want
String stackTraceStr = Log.getStackTraceString(exception);
EDIT: In response to the 100 char limit comment
I could never get EasyTracker.getTracker().setExceptionParser(...) working, infact I do not think it works, so I followed the blog post here http://dandar3.blogspot.co.uk/2013/03/google-analytics-easytracker-detailed.html
The important point in the blog post is to make sure you set your ExceptionParser on the GA exception handler:
// Make sure you set the context on EasyTracker first
EasyTracker.getInstance().setContext(this);
// As in in the blog post, we have to get the ExceptionReporter
// in order to set the ExceptionParser
Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
if (uncaughtExceptionHandler instanceof ExceptionReporter) {
ExceptionReporter exceptionReporter = (ExceptionReporter) uncaughtExceptionHandler;
exceptionReporter.setExceptionParser(new AnalyticsExceptionParser());
}
This worked for me and logs more than 100 chars.
Analytics may be limiting the size of messages you can send. I suggest using a real error reporting library like ACRA or BugSense instead. You will get extra features such as device info, configuration details and better error reporting (combining multiple exceptions if they have the same trace, etc.).
I would like to log error reports for my app to the Android Market error console; it looks like I can use Log.wtf for this.
The documentation for Log.wtf says:
What a Terrible Failure: Report a condition that should never happen. The error will always be logged at level ASSERT with the call stack. Depending on system configuration, a report may be added to the DropBoxManager and/or the process may be terminated immediately with an error dialog.
In my case, I can catch these exceptions and recover from them by showing an error message; I don't want my app to crash, but I do want the report to be sent to the error console.
Under what circumstances will Log.wtf terminate my app? Is it possible to get an error report without causing the app to crash?
It depends on your system settings (certain options can be enabled for debugging but are disabled on normal devices). They are settings enabled when android is compiled for the device and possibly the kernel.
I would suggest using Log.e() with a prefix instead of Log.wtf() to avoid any problems e.g. WTF: Something terrible happened
Here is what happens when you call a Log.wtf()
-> Log.java
/**
* What a Terrible Failure: Report an exception that should never happen.
* Similar to {#link #wtf(String, Throwable)}, with a message as well.
* #param tag Used to identify the source of a log message.
* #param msg The message you would like logged.
* #param tr An exception to log. May be null.
*/
public static int wtf(String tag, String msg, Throwable tr) {
TerribleFailure what = new TerribleFailure(msg, tr);
int bytes = println_native(LOG_ID_MAIN, ASSERT, tag, getStackTraceString(tr));
sWtfHandler.onTerribleFailure(tag, what);
return bytes;
}
-> Log.java
private static TerribleFailureHandler sWtfHandler = new TerribleFailureHandler() {
public void onTerribleFailure(String tag, TerribleFailure what) {
RuntimeInit.wtf(tag, what);
}
};
-> RuntimeInit.java
/**
* Report a serious error in the current process. May or may not cause
* the process to terminate (depends on system settings).
*
* #param tag to record with the error
* #param t exception describing the error site and conditions
*/
public static void wtf(String tag, Throwable t) {
try {
if (ActivityManagerNative.getDefault()
.handleApplicationWtf(mApplicationObject, tag,
new ApplicationErrorReport.CrashInfo(t))) {
// The Activity Manager has already written us off -- now exit.
Process.killProcess(Process.myPid());
System.exit(10);
}
} catch (Throwable t2) {
Slog.e(TAG, "Error reporting WTF", t2);
}
}
-> ActivityManagerNative.java
public boolean handleApplicationWtf(IBinder app, String tag,
ApplicationErrorReport.CrashInfo crashInfo)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(app);
data.writeString(tag);
crashInfo.writeToParcel(data, 0);
mRemote.transact(HANDLE_APPLICATION_WTF_TRANSACTION, data,
reply, 0);
reply.readException();
boolean res = reply.readInt() != 0;
reply.recycle();
data.recycle();
return res;
}
Following nebkat information. Beware using WTF: the API level of the device must be 8 or higher.