How to send a stacktrace to log4j? - java

Say you catch an exception and get the following on the standard output (like, say, the console) if you do a e.printStackTrace() :
java.io.FileNotFoundException: so.txt
at java.io.FileInputStream.<init>(FileInputStream.java)
at ExTest.readMyFile(ExTest.java:19)
at ExTest.main(ExTest.java:7)
Now I want to send this instead to a logger like, say, log4j to get the following:
31947 [AWT-EventQueue-0] ERROR Java.io.FileNotFoundException: so.txt
32204 [AWT-EventQueue-0] ERROR at java.io.FileInputStream.<init>(FileInputStream.java)
32235 [AWT-EventQueue-0] ERROR at ExTest.readMyFile(ExTest.java:19)
32370 [AWT-EventQueue-0] ERROR at ExTest.main(ExTest.java:7)
How can I do this?
try {
...
} catch (Exception e) {
final String s;
... // <-- What goes here?
log.error( s );
}

You pass the exception directly to the logger, e.g.
try {
...
} catch (Exception e) {
log.error( "failed!", e );
}
It's up to log4j to render the stack trace.

If you want to log a stacktrace without involving an exception just do this:
String message = "";
for(StackTraceElement stackTraceElement : Thread.currentThread().getStackTrace()) {
message = message + System.lineSeparator() + stackTraceElement.toString();
}
log.warn("Something weird happened. I will print the the complete stacktrace even if we have no exception just to help you find the cause" + message);

You can also get stack trace as string via ExceptionUtils.getStackTrace.
See: ExceptionUtils.java
I use it only for log.debug, to keep log.error simple.

The answer from skaffman is definitely the correct answer. All logger methods such as error(), warn(), info(), debug() take Throwable as a second parameter:
try {
...
} catch (Exception e) {
logger.error("error: ", e);
}
However, you can extract stacktrace as a String as well. Sometimes it could be useful if you wish to take advantage of formatting feature using "{}" placeholder - see method void info(String var1, Object... var2); In this case say you have a stacktrace as String, then you can actually do something like this:
try {
...
} catch (Exception e) {
String stacktrace = TextUtils.getStacktrace(e);
logger.error("error occurred for usename {} and group {}, details: {}",username, group, stacktrace);
}
This will print parametrized message and the stacktrace at the end the same way it does for method: logger.error("error: ", e);
I actually wrote an open source library that has a Utility for extraction of a stacktrace as a String with an option to smartly filter out some noise out of stacktrace. I.e. if you specify the package prefix that you are interested in your extracted stacktrace would be filtered out of some irrelevant parts and leave you with very consized info. Here is the link to the article that explains what utilities the library has and where to get it (both as maven artifacts and git sources) and how to use it as well. Open Source Java library with stack trace filtering, Silent String parsing Unicode converter and Version comparison See the paragraph "Stacktrace noise filter"

Just because it happened to me and can be useful.
If you do this
try {
...
} catch (Exception e) {
log.error( "failed! {}", e );
}
you will get the header of the exception and not the whole stacktrace. Because the logger will think that you are passing a String.
Do it without {} as skaffman said

In Log4j 2, you can use Logger.catching() to log a stacktrace from an exception that was caught.
try {
String msg = messages[messages.length];
logger.error("An exception should have been thrown");
} catch (Exception ex) {
logger.catching(ex);
}

This answer may be not related to the question asked but related to title of the question.
public class ThrowableTest {
public static void main(String[] args) {
Throwable createdBy = new Throwable("Created at main()");
ByteArrayOutputStream os = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(os);
createdBy.printStackTrace(pw);
try {
pw.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
logger.debug(os.toString());
}
}
OR
public static String getStackTrace (Throwable t)
{
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
t.printStackTrace(printWriter);
printWriter.close(); //surprise no IO exception here
try {
stringWriter.close();
}
catch (IOException e) {
}
return stringWriter.toString();
}
OR
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for(StackTraceElement stackTrace: stackTraceElements){
logger.debug(stackTrace.getClassName()+ " "+ stackTrace.getMethodName()+" "+stackTrace.getLineNumber());
}

this would be good log4j error/exception logging - readable by splunk/other logging/monitoring s/w. everything is form of key-value pair.
log4j would get the stack trace from Exception obj e
try {
---
---
} catch (Exception e) {
log.error("api_name={} method={} _message=\"error description.\" msg={}",
new Object[]{"api_name", "method_name", e.getMessage(), e});
}

Try this:
catch (Throwable t) {
logger.error("any message" + t);
StackTraceElement[] s = t.getStackTrace();
for(StackTraceElement e : s){
logger.error("\tat " + e);
}
}

You can use bellow code:
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class LogWriterUtility {
Logger log;
public LogWriterUtility(Class<?> clazz) {
log = LogManager.getLogger(clazz);
}
public void errorWithAnalysis( Exception exception) {
String message="No Message on error";
StackTraceElement[] stackTrace = exception.getStackTrace();
if(stackTrace!=null && stackTrace.length>0) {
message="";
for (StackTraceElement e : stackTrace) {
message += "\n" + e.toString();
}
}
log.error(message);
}
}
Here you can just call : LogWriterUtility.errorWithAnalysis( YOUR_EXCEPTION_INSTANCE);
It will print stackTrace into your log.

Create this class:
public class StdOutErrLog {
private static final Logger logger = Logger.getLogger(StdOutErrLog.class);
public static void tieSystemOutAndErrToLog() {
System.setOut(createLoggingProxy(System.out));
System.setErr(createLoggingProxy(System.err));
}
public static PrintStream createLoggingProxy(final PrintStream realPrintStream) {
return new PrintStream(realPrintStream) {
public void print(final String string) {
logger.info(string);
}
public void println(final String string) {
logger.info(string);
}
};
}
}
Call this in your code
StdOutErrLog.tieSystemOutAndErrToLog();

Related

Java try catch block not catching an exception

I was making a command to clear messages using JDA.
I made this code
public class Main {
public static JDA jda;
public static void main(String[] args) throws LoginException {
jda = JDABuilder.createDefault("OTM0ODA4NTY1ODYzMDM5MDA3.Ye1eUg.JExQxPx8UUli8YQfN7TfdbzLHqI").build();
jda.addEventListener(new CommandExecutor());
} }
public class CommandExecutor extends ListenerAdapter {
public static final String prefix = "!-";
public void onMessageReceived(MessageReceivedEvent event) {
String[] args = event.getMessage().getContentRaw().split(" ");
if (args[0].equalsIgnoreCase(prefix + "clear"))
new Clear(event, args);
} }
public class Clear {
public Clear(MessageReceivedEvent event, String[] args) {
try {
int numberOfMessages = Integer.parseInt(args[1]);
List<Message> messages = event.getChannel().getHistory().retrievePast(numberOfMessages + 1).complete();
event.getChannel().purgeMessages(messages);
event.getChannel().sendMessage("Messages have been deleted!").queue(m -> m.delete().queueAfter(5, TimeUnit.SECONDS));
} catch (Exception e) {
e.printStackTrace();
} } }
The code inside the try block will clear the messages if a valid argument is passed. In case an invalid argument is passed, like a string, it should go to the catch block and print the details of the exception. However, this does not happen, and the error gets generated.
Here is the error generated
java.lang.NumberFormatException: For input string: "de"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:668)
at java.base/java.lang.Integer.parseInt(Integer.java:786)
at JDA.TelevisionBot.Commands.Clear.<init>(Clear.java:24)
at JDA.TelevisionBot.CommandExecutor.onMessageReceived(CommandExecutor.java:26)
at net.dv8tion.jda.api.hooks.ListenerAdapter.onEvent(ListenerAdapter.java:359)
at net.dv8tion.jda.api.hooks.InterfacedEventManager.handle(InterfacedEventManager.java:96)
at net.dv8tion.jda.internal.hooks.EventManagerProxy.handleInternally(EventManagerProxy.java:88)
at net.dv8tion.jda.internal.hooks.EventManagerProxy.handle(EventManagerProxy.java:70)
at net.dv8tion.jda.internal.JDAImpl.handleEvent(JDAImpl.java:164)
at net.dv8tion.jda.internal.handle.MessageCreateHandler.handleInternally(MessageCreateHandler.java:121)
at net.dv8tion.jda.internal.handle.SocketHandler.handle(SocketHandler.java:36)
at net.dv8tion.jda.internal.requests.WebSocketClient.onDispatch(WebSocketClient.java:952)
at net.dv8tion.jda.internal.requests.WebSocketClient.onEvent(WebSocketClient.java:839)
at net.dv8tion.jda.internal.requests.WebSocketClient.handleEvent(WebSocketClient.java:817)
at net.dv8tion.jda.internal.requests.WebSocketClient.onBinaryMessage(WebSocketClient.java:991)
at com.neovisionaries.ws.client.ListenerManager.callOnBinaryMessage(ListenerManager.java:385)
at com.neovisionaries.ws.client.ReadingThread.callOnBinaryMessage(ReadingThread.java:276)
at com.neovisionaries.ws.client.ReadingThread.handleBinaryFrame(ReadingThread.java:996)
at com.neovisionaries.ws.client.ReadingThread.handleFrame(ReadingThread.java:755)
at com.neovisionaries.ws.client.ReadingThread.main(ReadingThread.java:108)
at com.neovisionaries.ws.client.ReadingThread.runMain(ReadingThread.java:64)
at com.neovisionaries.ws.client.WebSocketThread.run(WebSocketThread.java:45)
Thanks in advance
it is normal that you get everything you indicated since e.printStackTrace(),It's a method on Exception instances that prints the stack trace of the instance to System.err.
It's a very simple, but very useful tool for diagnosing an exceptions. It tells you what happened and where in the code this happened.
try {
throw new NullPointerException();
}
catch (NullPointerException e) {
System.out.println(e);
}
try {
throw new IOException();
}
catch (IOException e) {
e.printStackTrace();
}
System.exit(0);
Calling println(e):
java.lang.NullPointerException
Calling e.printStackTrace():
java.io.IOException
at package.Test.main(Test.java:74)

remove last method from stacktrace before throwing exception

I have a utility method for timing and logging various queries all over the project.
The problem is, when looking at crashlytics now all unrelated crashes are joined together into one crash-instance.
Can I catch all exceptions on the utility method, and throw them after removing that method from the stack?
The environment is Android (Java)
UPDATE:
based on #Dhananjay's answer below, here's my code:
public static Cursor get(...) {
try {
// my utility code
} catch (RuntimeException e) {
throw cleanException(e);
}
}
private static RuntimeException cleanException(RuntimeException e) {
try {
StackTraceElement[] stackTrace = e.getStackTrace();
StackTraceElement[] subTrace = new StackTraceElement[stackTrace.length - 1];
System.arraycopy(stackTrace, 1, subTrace, 0, subTrace.length);
e.setStackTrace(subTrace);
return e;
} catch (Throwable ignored) {
return e;
}
}
This approach might solve your problem: Set the stacktrace of the exception in the utility logging method to exclude the utility method itself, and then throw the exception, here is a working example, you can modify it to eliminate any StackTraceElement you want to:
package test;
public class TestMain {
public static void main(String args[]) throws Exception {
try {
apiCall();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void apiCall() throws Exception {
logAndThrow();
}
public static void logAndThrow() throws Exception {
Exception e = new Exception();
StackTraceElement[] cleanedUpStackTrace = new StackTraceElement[e.getStackTrace().length -1];
// Eliminate this mehod i.e. logAndThrow's stack trace entry (i.e. the first one) in cleanedUpStackTrace
System.arraycopy(e.getStackTrace(), 1, cleanedUpStackTrace, 0, cleanedUpStackTrace.length);
for(StackTraceElement ste : cleanedUpStackTrace) {
System.out.println(ste.getMethodName());
}
e.setStackTrace(cleanedUpStackTrace);
throw e;
}
}
Here is the output of this program, the logAndThrow method is not present in stack trace now:
apiCall
main
java.lang.Exception
at test.TestMain.apiCall(TestMain.java:33)
at test.TestMain.main(TestMain.java:25)

Testing methods using JUnit

I am new to JUnit and I have to test a method using JUnit api. One method internall calls another. My test case goes inside the method but while catchign the exception it fails.
Method under test is
public void checkANDCondition( Map<String, Message> messagesMap ) throws EISClientException
{
List<String> codes = getMessageCodes();
if(isAllReturnedMessagesContainCodes(codes, messagesMap))
{
StringBuffer buff = new StringBuffer("All of the specified message codes matched returned errors.");
for(String code: codes )
{
Message message = messagesMap.get(code);
buff.append(message.getMessageCode() + ": " + message.getMessageType() + ": " + message.getMessageText() + " ");
}
throw new EISClientException(buff.toString());
}
}
public boolean isAllReturnedMessagesContainCodes(List<String> codes, Map<String, Message> messagesMap)
{
if(codes!=null)
{
for(String code: codes)
{
if(!messagesMap.containsKey(code))
{
return false;
}
}
}
return true;
}
What I have done so far is
#Test
public void testPostProcess() throws Exception {
clientResponse = mock(ClientResponse.class);
MessageToExceptionPostProcessFilter postProcessFilter = new MessageToExceptionPostProcessFilter();
RetrieveBillingServiceResponse serviceResponse = new RetrieveBillingServiceResponse();caughtException = false;
try {
postProcessFilter.setCondition(ConditionOperator.AND);
List<String> messagesCodes = new ArrayList<String>();
messagesCodes.add("200");
messagesCodes.add("400");
Message message = new Message();
message.setMessageCode("200");
message.setMessageType(MessageTypeEnum.MESSAGE_TYPE_INFO);
message.setMessageText("Service completed successfully");
serviceResponse.setMessages(Arrays.asList(message));
postProcessFilter.setMessageCodes(messagesCodes);
serviceResponse = postProcessFilter.postProcess(serviceResponse, clientResponse);
assertNotNull(serviceResponse.getMessages());
} catch (EISClientException ex) {
caughtException = true;
assertEquals("All of the specified message codes matched returned errors.", ex.getMessage());
}
assertTrue(caughtException);
}
How can I make it pass?
Thanks
#Test(expected = EISCLientException.class)
public void testPostProcess() throws Exception {
...
serviceResponse.getMessages();
fail("Shouldn't reach this point");
}
That way you don't need to catch, with expected if it does not get throw a EISClientException it will fail.
edit: There are two times I can think of where you wouldn't want to use this.
1) You are mocking exceptions that are thrown mock(exception.class);
this i believe then throws some Mockito excpetion and it will not match the expected exception.
2) You are wrapping caught exceptions in your code, and throwing a generic exception. Example of code:
try {
} catch (FileParseException e){
throw new (ProjectFailingException(e, "file is bad");
}
if you have multiple catches and are wrapping them as ProjectFailingExceptions then you may want to catch in the test like this...
#Test ( expected = FileParseException.class)
public void testProcess() {
try {
...
} catch (ProjectFailingException e){
throw e.getCause();
}
Then the proper exception is thrown and you can make sure that process isn't throwing an exception from a a different catch.

Throws own exception from validate in android simple framework

I am using Simple XML in my project and have following problem
Source code
#Root (name = "Test")
#Order (elements = { "UserName", .... })
public class Test
{
#Element
public String UserName;
#Validate
public void validate() throws Exception
{
if(UserName.length() > 10) {
throw new Exception("User ID is invalid");
}
}
};
In the main code I write something like this
try {
serializer.read(REQ.class, reader);
}
catch(Exception ex) {
Log.i(TAG, ex.getMessage()); <--- HERE I GET MESSAGE: null, not the one I throws.
}
Question
Look like I can't catch exception which I throws, like validate function get my exception and replace it with it's own one. So am I right and what I can do to throw my own exception?
Thank you G.BlakeMeike you were right, the solution is follow:
try {
serializer.read(REQ.class, reader);
}
catch(Exception ex) {
Log.i(TAG, ex.getCause().getMessage());
}

Why is the main method not covered?

main method:
public static void main(String[] args) throws Exception
{
if (args.length != EXPECTED_NUMBER_OF_ARGUMENTS)
{
System.err.println("Usage - java XFRCompiler ConfigXML PackageXML XFR");
}
String configXML = args[0];
String packageXML = args[1];
String xfr = args[2];
AutoConfigCompiler compiler = new AutoConfigCompiler();
compiler.setConfigDocument(loadDocument(configXML));
compiler.setPackageInfoDoc(loadDocument(packageXML));
// compiler.setVisiblityDoc(loadDocument("VisibilityFilter.xml"));
compiler.compileModel(xfr);
}
private static Document loadDocument(String fileName) throws Exception
{
TXDOMParser parser = (TXDOMParser) ParserFactory.makeParser(TXDOMParser.class.getName());
InputSource source = new InputSource(new FileInputStream(fileName));
parser.parse(source);
return parser.getDocument();
}
testcase:
#Test
public void testCompileModel() throws Exception
{
// construct parameters
URL configFile = Thread.currentThread().getContextClassLoader().getResource("Ford_2008_Mustang_Config.xml");
URL packageFile = Thread.currentThread().getContextClassLoader().getResource("Ford_2008_Mustang_Package.xml");
File tmpFile = new File("Ford_2008_Mustang_tmp.xfr");
if(!tmpFile.exists()) {
tmpFile.createNewFile();
}
String[] args = new String[]{configFile.getPath(),packageFile.getPath(),tmpFile.getPath()};
try {
// test main method
XFRCompiler.main(args);
} catch (Exception e) {
assertTrue(true);
}
try {
// test args length is less than 3
XFRCompiler.main(new String[]{"",""});
} catch (Exception e) {
//ignore
}
tmpFile.delete();
}
Coverage outputs displayed as the lines from String configXML = args[0]; in main method
are not covered.
assertTrue(true); is a pointless no-op
Remove the try/catch around the call to XFRCompiler.main(args);, since all it does is swallow excpetions and make debugging harder; most likely you will then see an exception that tells you where the problem is.
There should be a call to fail() after the call to XFRCompiler.main(new String[]{"",""}); since you expect it to throw an exception
Put the two calls in separate test methods.
I'm worried about all those assertTrue(true). If there can't be an exception, then the assert is not necessary. If there is an unexpected exception, then this code will swallow it and you will get the behavior you see right now.
Then, if you expect an exception, you should code like this:
try {
... code that will throw an exception ...
fail("No exception was thrown");
} catch (SpecficTypeOfException e) {
assertEquals("message", e.getMessage());
}
That way, wrong types of exception and the exception message will be checked.
PS: Don't post questions with "urgent". We already help as fast as we can.

Categories