firebase crash reporting does not logs normal logs without expection - android

I am using firebase crash in my cordova application and I want to capture all logs ( Log.e ) in my application as follows.
Login => {"userName" : "jhon" , "networkConnection": "on" ,
"status":"success"}
AddContact => {"userName" : "jhon" , "networkConnection": "on" ,
"status":"success", "contactId" :"3233"}
But I have noticed one thing is that if my app gets crashed then only I am able to see logs without that I can't see the normal logs when I app do not crash.
For example ,
In following code
If I explicitly call FirebaseCrash.report(new Exception(name)); then only FirebaseCrash.logcat(Log.ERROR, name, params + " "+message); appears in firebase crash report.
If I only write FirebaseCrash.report(new Exception(name)); then only FirebaseCrash.logcat(Log.ERROR, name, params + " "+message); the these logs are not coming to the server.
private void logCrash(final CallbackContext callbackContext, final String name, final String params,final String message){
cordova.getThreadPool().execute(new Runnable() {
#Override
public void run() {
FirebaseCrash.logcat(Log.ERROR, name, params + " "+message);
FirebaseCrash.report(new Exception(name));
}
});
callbackContext.success();
}

Related

Unknown field "a" appears in firestore when saving fcm token to database

I am grabbing the android device token and saving it to firestore. This works in most of the cases totally fine. Now I encountered a device where it does not work. Oddly enough I end up with attributes in firestore that my model does not reflect and I do not understand where those attributes and their values come from.
This is my method to retrieve the token:
public static void updateToken(String userId, FcmToken.Action action) {
if (StringUtils.checkNull(userId)) return;
//FCM
FirebaseMessaging.getInstance().getToken().addOnCompleteListener(task -> {
if (!task.isSuccessful()) {
Log.w(TAG, "Could not get firebase token. ", task.getException());
return;
}
String token = task.getResult();
Log.i(TAG, "[FCM TOKEN]: " + token);
//Do not ask firebase to write an existing token again,
//this only creates traffic and costs but does not change the database
String storedToken = FcmProvider.loadToken(false);
if (StringUtils.checkNull(storedToken) || !token.equals(storedToken) || action == FcmToken.Action.DELETE) {
FcmToken fcmToken = new FcmToken(token, userId, action);
FcmRepo.getInstance().setToken(fcmToken, documentId -> {
Log.d(TAG, "Token " + action + " successfully");
FcmProvider.saveToken(token);
}, e -> {
Log.d(TAG, "Failed to " + action + " token. " + e);
});
} else {
Log.d(TAG, "Stored token is identical. No update needed");
}
});
}
This is the FcmToken class (And I assume the way I use the field values here are the issue, yet I do not understand the outcome)
#Getter
#Setter
#IgnoreExtraProperties
public class FcmToken {
private String userId;
private FieldValue fcmToken;
#ServerTimestamp
private Date ut;
public enum Action {SET, DELETE}
public FcmToken(String fcmToken, String userId, Action action) {
this.userId = userId;
if (action == Action.SET) this.fcmToken = FieldValue.arrayUnion(fcmToken);
else if (action == Action.DELETE) this.fcmToken = FieldValue.arrayRemove(fcmToken);
}
}
For completness this is the method I use to set the token:
public void setToken(#NonNull FcmToken fcmToken, #Nullable OnSetSuccessListener callbackSuccess, #Nullable OnFailureListener callbackFailure) {
getFcmRef().document(fcmToken.getUserId()).set(fcmToken, SetOptions.merge()).addOnCompleteListener(task ->...);
}
Now this is what ends up in my firestore for that user only:
This HAS to to come from the app itself. There is no other way this document could have been created:
If I use another device or the emulator the result looks like expected. Where does the "a" field come from? Why is it assigned to userId and why is there no fcmToken field?
The problem is, that I do not have physical access to the phone that produces this behaviour (Samsung S21 Ultra, Android 12). I need to debug this without having access to the log outputs.
But I know that the task comes back successfully (I used another version to save the error in the token field and there was none).
Any ideas?
This typically means that you didn't configure ProGuard correctly, and it's minifying the classes that you're writing to the database.
You'll need to prevent ProGuard from modifying the classes that you use to interact with the database. See the Firebase documentation on configuring ProGuard (the link is for the Realtime Database, but the same applies to Firestore), or one of these previous questions about configuring ProGuard for Firebase.

How can a chat application user once online again get the messages that were sent to them when they were offline

I'm developing an android chat app, using Node Js and redis to stock messages and user information. I'm using socket io for communication, and Room to store message in local database. When the user is offline, I want them to receive their messages once online again. My problem is, that when user A is offline, and user B send him many messages (let's say for instance 5 messages ), when user A is online again, he only receives the first message, and the last message 4 times. Here is what I'm doing, once the user receives a message, I update the message status in Redis from "Sent" to "Delivered". In the case when the user is offline, I stock their messages in Redis with the status of message "Sent", and once online again, I check their messages received for example from user B, if their status is "Sent", I deliver it to the user, and then it will be updted to "Delivered", as shown in the code below:
//On this event, we update the socket ID of the sender in Redis so they can
receive private messages from their contacts
socket.on('sender', (sender, destinat) =>{
tempId = socket.id;
senderId = sender;
users[sender] = sender;
users [destinat] = destinat;
//We also update the user status: online
client.hset(senderId, 'lastSeen', 'Now', function(reply){
console.log( senderId + reply);
});
//Stocking to the user socket id
client.hset(users[sender], 'tempId', tempId, function(){
console.log("Welcome " + sender);
console.log("Welcome " + tempId);
});
//Getting all the messages of the sender from users
//If the sender has any messages that hasn't received yet, they'll be sent
here
//the id of each message is compsed of two parts: the phone number of the
receiver, and the id of the message itself
(receiverPhoneNumber:idMessage)
client.keys(users [sender] + ':*', function(err, results) {
results.forEach(function(key) {
client.hgetall(key, function(err, reply){
if(err)
console.log(err);
else if(reply){
//Compare the message status: if not sent, deliver it to receiver once online
if('Sent'.localeCompare(reply.status) == 0 && users
[destinat].localeCompare(reply.fromUser) == 0) {
io.to(tempId).emit('message', reply);
}
}
});
});
});
});
After receiving messages from the server, I use Async to store them in Room Database and then display them to the user, as shown in the following code
And here is the AsyncTask Class:
class AddMessage extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
//Creating a user account
m = new Message();
m.setContent( message );
m.setTime( time );
m.setUrl( url );
m.setStatus( status );
m.setFromUser( fromUser );
m.setToUser( toUser );
m.setUsername( receiver.getUsername() );
//adding to database
DatabaseClient.getInstance(getContext()).getAppDatabase()
.messageDao()
.insert(m);
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Toast.makeText( getContext(), "Added!", Toast.LENGTH_SHORT ).show();
}
}
I've checked that messages are received from the server to the android app correctly (by re-sending the messages again to the server once delivered to the app). I believe the problem has something to do with AsyncTask, but I just can't figure it out, any help is greatly appreciated, thank you so much.
//When receving a message
socket.on("message", new Emitter.Listener() {
#Override
public void call(final Object... args) {
if(getActivity() != null){
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
try {
//extract data from fired event
idMessage = data.getString( "idMessage" );
message = data.getString("message");
fromUser = data.getString( "fromUser" );
toUser = data.getString( "toUser" );
time = data.getString( "time" );
status = data.getString( "status" );
url = data.getString( "url" );
//Here we call asyncTask to Add it to Database
addMessage = new AddMessage();
addMessage.execute( );
//We emit this event to update the status of
the message to delivered
socket.emit( "sent", idMessage, userID );
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
}
});
I solved the problem by switching to RxJava instead of AsyncTask. The problem had something to do with AsyncTask as it sometimes influnces the chain of data, which is not the case with RxJava, as mentioned in this link : "Another issue with AsyncTasks is if you have more than one running at once. You have no guarantee what order they’ll complete in, resulting in complex logic to check when all the tasks have completed. Even worse is the assumption that one will finish before the other, until you hit an edge case that makes the first call slower, which makes them complete in the wrong order and undesired results."

Log.d doesn't print out message

I get an error java.lang.NullPointerException: println needs a message when using Log statement with the following code
for (Application app : mApplications) {
Log.d("LOG", "********");
Log.d("LOG", app.getName());
Log.d("LOG", app.getArtist());
Log.d("LOG", app.getReleaseDate());
}
but in second parameter, If I add another string between quotes the error is gone
for(Application app: mApplications){
Log.d("LOG", "******************");
Log.d("LOG","Name: " +app.getName());
Log.d("LOG","Artist: " +app.getArtist());
Log.d("LOG","ReleaseDate: " +app.getReleaseDate());
}
what is the difference between the two ?
The difference is that you've concatenated a string with the a null variable.
Log needs a non-null message string.
Alternatively, this also works because it will print "null" but it doesn't look clean
Log.d("LOG", String.valueOf(app.getName()));
Log.d("LOG", String.valueOf(app.getArtist()));
Log.d("LOG", String.valueOf(app.getReleaseDate()));
First of all if you are using Log.d it is generally use for debugging and this is the correct syntax for Log.d d(String tag, String msg, Throwable tr) For better understanding See this
In your case First you are using this
for (Application app : mApplications) {
Log.d("LOG", "********");
Log.d("LOG", app.getName());
Log.d("LOG", app.getArtist());
Log.d("LOG", app.getReleaseDate());
}
And generate this error java.lang.NullPointerException: println needs a message because you can't put message as per Log.d syntax rule.
I hope you understand...

ACRA not creating report

I've implemented ACRA 4.8.5 in my app and it is initialized and enabled and all but when I face a bug, it doesn't create a report... The only two related ACRA logs I have are:
I/ACRA: ACRA is enabled for com.mydomain.myapp, initializing...
and
E/ACRA: ACRA caught a RuntimeException for com.mydomain.myapp
I have this in my application class
#ReportsCrashes(reportSenderFactoryClasses = {ACRASenderFactory.class})
and
#Override
public void onCreate() {
super.onCreate();
ACRA.init(this);
}
Here is my ACRASenderFactory class
public class ACRASenderFactory implements ReportSenderFactory {
public ACRASenderFactory(){
Log.e("ACRA", "Create Sender Factory");
}
#NonNull
#Override
public ReportSender create(Context context, ACRAConfiguration acraConfiguration) {
Log.e("ACRA", "Return Report Sender");
return new ACRAReportSender();
}
}
and here is my ACRAReportSender class
public class ACRAReportSender implements ReportSender {
public ACRAReportSender(){
Log.e("ACRA", "Report Sender created");
}
#Override
public void send(Context context, CrashReportData crashReportData) throws ReportSenderException {
Log.e("ACRA", "Trying to send crash report");
String reportBody = createCrashReport(crashReportData);
// Send body using email
Intent emailIntent = new Intent(Intent.ACTION_SEND);
// Set type to "email"
emailIntent.setType("vnd.android.cursor.dir/email");
String to[] = {"me#mydomain.com"};
emailIntent.putExtra(Intent.EXTRA_EMAIL, to);
// Text
emailIntent.putExtra(Intent.EXTRA_TEXT, reportBody);
// Set the subject
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "ACRA Crash Report");
context.startActivity(Intent.createChooser(emailIntent, "Send crash to developpers by email ..."));
}
private String createCrashReport(CrashReportData crashReportData){
StringBuilder body = new StringBuilder();
body.append("Device : " + crashReportData.getProperty(ReportField.BRAND) + " - " + crashReportData.getProperty(ReportField.PHONE_MODEL))
.append("\n")
.append("Android Version : " + crashReportData.getProperty(ReportField.ANDROID_VERSION))
.append("\n")
.append("App Version : " + crashReportData.getProperty(ReportField.APP_VERSION_CODE))
.append("\n")
.append("STACK TRACE : \n" + crashReportData.getProperty(ReportField.STACK_TRACE));
return body.toString();
}
}
I really don't know why it's not working.. I've also allowed Internet in my Manifest and set my app name.
Any help would be really appreciated!
Thanks!
As discussed on the ACRA GitHub issue, ACRA has shipped as an AAR for some time now. So you need to build and include the AAR, not the JAR (which you must have dug out of the AAR).
ACRA requires Android services and resources in order to run.
My problem (I think) was that I am developping for API 15 and up. Using ACRA 4.8.5 might have been the problem since using ACRA 4.7.0 works just fine!

How to save Log message to a string in android?

Is there any way that I could directly redirect the log message in Android to a string?
For example, I have a log message in Android.
Log.v(TAG, "First Name" + firstName);
Log.v(TAG, "Last Name" + lastName);
I have a string
String nameMessage = "Please check your credetials\n" ;
I want to concatenate the log to the message.
One way is to concatenate manually.
I am looking If we could redirect the log to message.
I'm not sure if I am understanding your question right, but technically you could just create a function that appends the log message and then actually logs it?
Example:
private StringBuilder errorMessage;
public void log(String tag, String message) {
errorMessage.append(message).append(System.getProperty("line.separator"));
Log.v(tag, message);
}
public void getErrorMessage() {
return errorMessage.toString();
}
And then, you'd just replace all your calls to Log#v with a call to #log(tag, message).

Categories

Resources