How to log formatted message, object array, exception? - java

What is the correct approach to log both a populated message and a stack trace of the exception?
logger.error(
"\ncontext info one two three: {} {} {}\n",
new Object[] {"1", "2", "3"},
new Exception("something went wrong"));
I'd like to produce an output similar to this:
context info one two three: 1 2 3
java.lang.Exception: something went wrong
stacktrace 0
stacktrace 1
stacktrace ...
My SLF4J version is 1.6.1.

As of SLF4J 1.6.0, in the presence of multiple parameters and if the last argument in a logging statement is an exception, then SLF4J will presume that the user wants the last argument to be treated as an exception and not a simple parameter. See also the relevant FAQ entry.
So, writing (in SLF4J version 1.7.x and later)
logger.error("one two three: {} {} {}", "a", "b",
"c", new Exception("something went wrong"));
or writing (in SLF4J version 1.6.x)
logger.error("one two three: {} {} {}", new Object[] {"a", "b",
"c", new Exception("something went wrong")});
will yield
one two three: a b c
java.lang.Exception: something went wrong
at Example.main(Example.java:13)
at java.lang.reflect.Method.invoke(Method.java:597)
at ...
The exact output will depend on the underlying framework (e.g. logback, log4j, etc) as well on how the underlying framework is configured. However, if the last parameter is an exception it will be interpreted as such regardless of the underlying framework.

In addition to #Ceki 's answer, If you are using logback and setup a config file in your project (usually logback.xml), you can define the log to plot the stack trace as well using
<encoder>
<pattern>%date |%-5level| [%thread] [%file:%line] - %msg%n%ex{full}</pattern>
</encoder>
the %ex in pattern is what makes the difference

The accepted answer is great. I'm just adding here my case that is now working thanks for the answer. This may help someone else.
I'm using SLF4J and logback with a JSON encoder. Furthermore, I use marker and arguments to enrich my output.
logger.error(getMarker("errorEvent"),
"An error occurred",
entries(mapOf("someKey" to "someValue")),
new Exception())
The output:
{
"level": "ERROR",
"event": "errorEvent",
"eventData": {
"someKey": "someValue"
},
"stacktrace": "...omitted...",
"message": "An error occurred"
}
Of course there is a lot of configuration of logstash behind the scenes, but I just wanted to show that the arguments passed as entries are shown in the configured eventData tag.

Related

Parsing Options using Apache Commons Library

This might be sort of a noob question, but I am using the import org.apache.commons.cli.*; to set up my command line parser application.
This is how I am setting up my option.
Option n = Option.builder().hasArg(true).option("n").build();
options.addOption(n);
In one of our scenarios, we have a particular case where the input looks something like this "-n", "2", "3". Now this is not a valid scenario, because n should fail if you provide more than one value, without the "-n" flag.
Invalid: "-n", "2", "3"
Valid: "-n", "2", "-n", "3"
I was able to get the valid scenario working, but I am unable to get the invalid scenario working. because when I use getOptionValues(), I only get back 2 and not 3. Does anyone know how I can grab 3 too, and fail the invalid scenario.
Thanks.
You can use CommandLine.getArgList() to check for such "trailing" arguments,
CommandLine cmd = parser.parse(options, new String[] {"-n", "2", "3"});
assertTrue(cmd.hasOption(n));
assertEquals("[3]", cmd.getArgList().toString());
So you can fail parsing if cmd.getArgList() is not empty to catch such cases.

java.util.logging.ErrorManager: WRITE_FAILURE when using the "logp" method

I am trying to log an INFO message as follows:
LOGGER.logp(Level.INFO, "class", "method", "foo {0}",
new Object[] { "bar" });
This is using this version of logp, using the parameter insertion technique outlined here. Doing this, the logger instead outputs an error:
java.util.logging.ErrorManager: WRITE_FAILURE
Error message - Exception occurred when writing to the output stream.
Exception - java.lang.NullPointerException
If I instead do this:
LOGGER.logp(Level.INFO, "class", "method", "foo",
new Object[] {});
I get no error, and "foo" is printed out correctly. This makes it seem like something is going wrong with the insertion of the parameters into the string.
Why am I seeing write errors when I try to use the logp method with parameters?

Take out the first line of a field and add it as a new field in Logstash 1.4.0

I have my ELK stack configured and running using log4j and everything is working fine. What I would like to be able to do is group all exceptions by their type, for example - create a terms graph and have a term for each exception type like FileNotFound, NullPointerException and so on. I already have a stack_trace field which includes the exception type at the first line, and then the complete stack trace. I found something online like this:
filter{
mutate {
gsub => [
"stack_trace", "\n.*", ""
]
}
}
but this would just override the stack_trace field with it's first line, which is not what I want. I want to add a new field that takes out the first line, the exception type, of the stack_trace field.
Make a copy of the stack trace field and perform your gsub on that
filter{
mutate {
add_field => {
"exception" => "%{stack_trace}"
}
}
mutate {
gsub => [
"exception", "\n.*", ""
]
}
}
EDIT: Thanks to #Alpha for pointing out this question, you may need to use two separate mutates.

How to catch errors during log4j2 initialization?

We don't consider our app running properly if log4j2 isn't configured properly as specified in the config file. How do i reliably detect whether errors have occurred during log4j2 initialization and been printed on the console from java? There are (at least) two sources of bad configuration:
The configuration file is entirely missing (or invalid?)
The configuration file is valid, but has a problem during setConfiguration().
This question is about the second point. It looks like log4j2 internally does try{...} catch(e) { LOGGER.error("bla bla", e) } during initialization making it impossible to detect such problems.
Errors printed to the console that no human is ever going to see is bad. We'd rather have our app crash!
I'm surprised that I can't find a way to programmatically ask log4j2: "Did I get the configuration I asked for?". Is there something I have overlooked? E.g. some way to detect whether anything has been logged yet?
Background
Here is the console output we're seeing. Yes, there is permission denied to /path/to/file - how do I detect that?
2015-10-15 17:43:50,539 main ERROR FileManager (/path/to/file) java.io.FileNotFoundException: /path/to/file (Permission denied)
2015-10-15 17:43:50,541 main ERROR Unable to invoke factory method in class class org.apache.logging.log4j.core.appender.FileAppender for element File. java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.build(PluginBuilder.java:136)
at org.apache.logging.log4j.core.config.AbstractConfiguration.createPluginObject(AbstractConfiguration.java:794)
at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:734)
at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:726)
at org.apache.logging.log4j.core.config.AbstractConfiguration.doConfigure(AbstractConfiguration.java:383)
at org.apache.logging.log4j.core.config.AbstractConfiguration.initialize(AbstractConfiguration.java:161)
at org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:173)
at org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:422)
at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:494)
at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:510)
at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:199)
at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:146)
at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:41)
at org.apache.logging.log4j.LogManager.getContext(LogManager.java:177)
at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:447)
at (our first call to org.apache.logging.log4j.LogManager.getLogger())
Looking in the code for LoggerContext.reconfigure(), I see:
private void reconfigure(final URI configURI) {
<snip>
final Configuration instance = ConfigurationFactory.getInstance().getConfiguration(name, configURI, cl);
setConfiguration(instance);
<snip>
}
And during the call of setConfiguration(), there is this in org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.build:
try {
<snip>
final Object plugin = factory.invoke(null, params);
<snip>
return plugin;
} catch (final Exception e) {
LOGGER.error("Unable to invoke factory method in class {} for element {}.", this.clazz, this.node.getName(),
e);
return null;
}
So the problem was not with getConfiguration() from the ConfigurationFactory, but with the subsequent setConfiguration(), and there seems to be no way to detect that there was a problem... :-(
About the first point: The configuration file is entirely missing (or invalid?)
We're setting -Dlog4j.configurationFile=<file> on the java command line, and there are already several posts on how to detect if <file> is missing. One of which suggests to -Dlog4j.configurationFactory with our own ConfigurationFactory. But no amount of fiddling with our own ConfigurationFactory is going to help during setConfiguration() to detect the second point ("2." above) as far as I can reason.
We're running log4j2 version 2.4 on openjdk 8 on debian stable/jessie.
It turns out the Log4j2 internals use a special StatusLogger, that has a getStatusData() method, so here's what to do:
import org.apache.logging.log4j.status.StatusData;
import org.apache.logging.log4j.status.StatusLogger;
...
StatusLogger statusLogger = StatusLogger.getLogger();
if (statusLogger.getStatusData().size() > 0) {
System.out.printf(
"Logged %d messages\n",
statusLogger.getStatusData().size()
);
// Investigate List<StatusData> if you want
for(StatusData data : statusLogger.getStatusData()) {
System.out.printf(
" Level %s message: %s\n",
data.getLevel(),
data.getMessage().getFormattedMessage()
);
}
System.err.println("exiting due to unexpected console status logs");
System.exit(1);
}

Java Exception appends ": null" to "Caused by" clause

There are some exceptions shown as follows in my log: (the text has been modified to conceal project information)
java.util.concurrent.ExecutionException:org.xxx.BBBException<br>
at ....<br>
...<br>
Caused by: org.xxx.BBBException: null<br>
at ....<br>
...<br>
Why is there "null" in the Caused by clause?
BBBException, which was made by us, extends Exception and does not override toString().
In some situations, FutureTask.setException(new BBBException("RPC timed out")) is called and BBBException is being expected in the log.
However, the exception's details message is not what we set in the program and the text in the first line and Caused by clause even do not match (there is no ": null" in the first line).
Anyone has a clue why this has happened? Thanks!
Environment: java 6, update 21, centos 64-bit, java 64-bit, mixed mode.
I suspect you've either actually created a new BBBException without a message, or your BBBException(String) constructor doesn't pass the message up to the super-constructor (which it should, via super(message)). Basically it's saying: this is an exception with no message.
It's hard to tell exactly what's wrong without seeing any of your code though.
org.xxx.BBBException: null was thrown with null message (using default constructor). Throw it with some message (if it supports it):
throw new BBBException("Danger! High Voltage")
If this doesn't work, it means the BBBException(String msg) constructor is poorly written. It should be something like:
BBBException(String msg) {
super(msg);
}

Categories

Resources