Reputation: 2355
This is a follow up (though different) question to this thread: Java - Create anonymous Exception subclass with a certain name
I'm trying to send non-crash error reports to Crashlytics on Android and thanks to Rafael Winterhalter I now achieved that they get displayed in different rows in the Crashlytics UI. The requirement for this was to send different Exception subclasses for every issueType.
It looks like in the screenshot below now.
The next issue is that it still doesn't really show on one glance what the actual exception-type is. Instead it only shows the function-name that the error was sent from. Having now learnt about the magic of Byte Buddy, I thought - maybe it's even possible to create a static function in the custom Exception subclass, that directly calls the
Crashlytics.logException
function. I've seen that it's possible to use an interceptor class (and then call code from there), but I guess Crashlytics would then just always show the function name inside this interceptor.
Is it possible to create a static method with name issueType
and have it directly call Crashlytics.logException
?
Here's my updated code so far:
public static void craslyticsRecordError(String issueType, String errorMsg)
{
// byte buddy needs a private directory
File filesDir = GameApplication.getAppContext().getFilesDir();
String dirPath = filesDir.getAbsolutePath() + File.separator + "ByteBuddy";
File byteBuddyPrivateDirectory = new File(dirPath);
if (!byteBuddyPrivateDirectory.exists())
{
byteBuddyPrivateDirectory.mkdirs();
}
GameLog.d("ByteBuddyDir:" + byteBuddyPrivateDirectory);
try
{
// dynamically create an exception with 'issueType' as its class name and also create method
// that has this name, so that Crashlytics will show that name
Class<? extends Exception> dynamicType = new ByteBuddy()
.subclass(Exception.class)
.name(issueType)
.defineMethod(issueType, void.class, Ownership.STATIC, Visibility.PUBLIC)
.withParameters(Throwable.class)
.intercept(MethodCall.invoke(Crashlytics.class.getMethod("logException", Throwable.class)))
.make() // line of crash
.load(Fabric.class.getClassLoader(), new AndroidClassLoadingStrategy.Wrapping(byteBuddyPrivateDirectory))
.getLoaded();
Exception e = dynamicType.getConstructor(String.class).newInstance(errorMsg);
Method method = Crashlytics.class.getMethod(issueType, Exception.class);
method.invoke(null, e);
}
catch (Exception e1)
{
Exception e = new Exception(issueType + "-" + errorMsg);
Crashlytics.logException(e);
e1.printStackTrace();
}
}
Crash Stacktrace:
java.lang.IllegalStateException: public static void com.crashlytics.android.Crashlytics.logException(java.lang.Throwable) does not take 0 arguments
at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:1998)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:620)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:609)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:526)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:4159)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1633)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:174)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:155)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:2559)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2661)
at org.utils.Fabric.craslyticsRecordError(Fabric.java:69)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:323)
at android.os.Looper.loop(Looper.java:143)
at android.app.ActivityThread.main(ActivityThread.java:7224)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
Upvotes: 2
Views: 879
Reputation: 44077
Yes, of course this is possible and the documentation even contains an example. You can define the method as follows:
new ByteBuddy()
.subclass(Exception.class)
.defineMethod("issueType", void.class, Ownership.STATIC, Visibility.PUBLIC)
.withParameters(Exception.class)
.intercept(MethodCall.invoke(Crashalytics.getMethod("logException"))
.withArgument(0));
This would define a method
public static void issueType(Exception e) {
Crashalytics.logException(e);
}
within your generated class.
Upvotes: 1