Reputation: 432
I am coding an Error Log Function.
So I need to log some information like operator,operate_time,error_message and api_name which CLASS or PROCEDURE the Exception be threw.
My problem is that I don't know how to get the api_name in java.
A part stacktrace of exception is(I hide name of my company):
com.xxxx.commons.database.DbException: {dbFlag: 101, dbMessage: ORA-01461}
at com.xxxx.commons.database.DbHelper.throwDbException(DbHelper.java:933)
at com.xxxx.commons.database.DbHelper.throwDbException(DbHelper.java:911)
at com.xxxx.commons.database.DbHelper.runProcScalar(DbHelper.java:363)
at com.xxxx.commons.database.DbHelper.runProcScalar(DbHelper.java:266)
at com.xxxx.commons.database.DbHelper.runProcScalar(DbHelper.java:253)
at com.xxxx.xxxxxx.xxxxxx.xxxx.xxxxxxx.saveErrorLog(xxxxxxxx.java:96)
at com.xxxx.xxxxx.xxxx.xxxx.xxxx.save(xxxxxxxx.java:457)//I need this line
at com.xxxx.xxx.xxxxxxxx.xxxxxxxx.finishActivity(xxxxxx.java:410)
at com.xxxx.xxxx.xxxx.command.FinishActivityCommand.execute(xxxxxxxx.java:70)
at com.xxxx.xxx.xxxxxx.xxxxxxxx.forward(xxxxxxx.java:79)
at com.xxxx.xxx.xxxxxxx.xxxxx.execute(FlowCtrlAction.java:50)
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431)
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1196)
at com.xxxx.xxx.xxxxxx.xxxxxxxx.process(xxxxxxx.java:33)
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432)
I need the className of the eighth line and my log function is the seventh line. The java code is :
try{
boolean r = store.xxxxxxx(caseId, runnerId, flowRecord);
if (r) {
return .........
}
}catch(DbException e){
saveLogDao.saveErrorLog(showType, businessUserId, caseId, stepCode, getExceptionStack(e), e.getMessage(), runnerId);
}
The function 'getExceptionStack(e)' is getting name of the exception class.
How to code it?Thank you very much.
Upvotes: 0
Views: 204
Reputation: 900
The procedure than throws the exception is the first one in stack. However, if I get your question right, you want to skip irrelevant methods and log some meaningful method into error log.
However, this might not be that trivial. If you have a method "getUserCredentials()", which calls "getUserName()", which calls "getDatabaseConnection()", which calls "connect()", which throws an exception - which method should be logged, when you call "getUserCredentials()"? Which method should be logged, when you call "getUserCredentials()" in "generateReport()", which was called in "main()" ?
However, there are some options, for example, analyzing annotations, especially if your code already uses some.
Here's an example where I annotate methods that should come up in a log when an exception happens deeper inside of them:
package stackoverflow;
import java.io.ObjectInputStream.GetField;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.Arrays;
public class StackTrace {
@Retention(RetentionPolicy.RUNTIME)
public static @interface TopLevelCallElement {
}
private void connect(){
throw new RuntimeException("some database error");
}
private void getDatabaseConnection(){
connect();
}
@TopLevelCallElement
private void getName(){
getDatabaseConnection();
}
@TopLevelCallElement
public void getCredentials(){
getName();
}
public static StackTraceElement getTopLevelFunction(Throwable exception){
// iterate stack trace in reverse order
StackTraceElement [] stackTrace = exception.getStackTrace();
for (int i=stackTrace.length-1;i>=0;i--){
StackTraceElement traceElement=stackTrace[i];
try {
Class<?> callerClass = Class.forName(traceElement.getClassName());
for (Method method:callerClass.getDeclaredMethods()){
if (method.getName().equals(traceElement.getMethodName())){
// we got a method in a class that might be (unless it's an overloaded method)
// a method that really threw exception.
if (method.getAnnotation(TopLevelCallElement.class)!=null){
return traceElement;
}
}
}
} catch (ClassNotFoundException e) {
// should not happen, we know we're callling Class.forName with existing classes
}
}
// in case no apropriate stack trace element was found, just return the most deepest one
return stackTrace[stackTrace.length-1];
}
public static void logExceptionElement(Throwable exception){
StackTraceElement element = getTopLevelFunction(exception);
System.err.println(String.format("Exception in class %s, method %s, line %d",
element.getClass().getName(),
element.getMethodName(),
element.getLineNumber(),
exception.getMessage()
));
}
public static void main(String[] args) {
StackTrace test = new StackTrace();
try{
test.getCredentials();
}
catch (RuntimeException err){
// should log "getCredentials()" as top level function exception point
logExceptionElement(err);
}
try{
test.getName();
}
catch (RuntimeException err){
// should log "getName()" as top level function exception point
logExceptionElement(err);
}
}
}
Annotating whole code like that just for logging purposes might be painful, however. You can, however, reuse some existing annotations, for example, Spring-based database application could track first "transactional" method in a stack. Other methods of analysis could involve skipping getters (any methods that begin with "get"), skipping certain classes by name/package name, etc.
Upvotes: 2
Reputation: 61705
If you really want to access the information in a stacktrace, you can use Throwable#getStackTrace(), and then look for the line after the saveErrorLog.
getStackTrace() returns an array of StackTraceElement, which contains the information you want.
I don't know how you're calling these methods, but a better option would be to pass in the name of the api from wherever you're calling this from.
Upvotes: 0