Is there a Logger that will easily log my stacktrace (what I get with
ex.printStackTrace())? I've searched the log4j docs and found nothing
about logging the stacktrace.
I can do this myself with
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw));
String stacktrace = sw.toString();
logger.error(stacktrace);
but I don't want to duplicate this code all over the place.
If log4j won't do this for me is there another logging package that will
log the stacktrace for me?
Thanks.
Using log4j this is done with:
logger.error("An error occurred", exception);
The first argument is a message to be displayed, the second is the exception (throwable) whose stacktrace is logged.
Another option is commons-logging, where it's the same:
log.error("Message", exception);
With java.util.logging this can be done via:
logger.log(Level.SEVERE, "Message", exception);
In java.util.logging you can initialize the logger with custom log formatter like here:
private Logger initTextLogger() throws SecurityException, IOException {
Logger logger = Logger.getLogger(YourClass.class.getName());
FileHandler fileHandler = new FileHandler(logFilePath, false);
SimpleFormatter logFormatter = new SimpleFormatter() {
#Override
public String format(LogRecord record) {
String stacktrace = "";
Throwable t = record.getThrown();
if(t!=null){
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
stacktrace = sw.toString();
}
return record.getLevel() + ": " + record.getMessage() + "\r\n"
+ stacktrace;
}
};
fileHandler.setFormatter(logFormatter);
logger.addHandler(fileHandler);
return logger;
}
Related
I wrote a separate class MyLogger with static method
static public void setup(String className, Exception e, Level level) {
System.out.println("className = " + className);
Logger logger = Logger.getLogger(className);
logger.setLevel(Level.INFO);
try {
fileTxt = new FileHandler("Logging.%u.%g.txt",1024 * 1024, 10, true );
// create a TXT formatter
formatterTxt = new SimpleFormatter();
fileTxt.setFormatter(formatterTxt);
logger.addHandler(fileTxt);
logger.log(level, e.toString(), e);
} catch (IOException | SecurityException ex) {
System.out.println("SecurityException ");
Logger logger2=Logger.getLogger(MyLogger.class.getName());
logger2.addHandler(new ConsoleHandler());
logger2.log(Level.SEVERE, null, ex);
}
}
This static method receives a string className which I put as a parameter into Logger.getLogger().
In another class I call MyLogger.setup(CommonFrame.class.getName(), e, Level.SEVERE).
Everything works. But the problem is that in the file I get "May 25, 2014 2:05:30 PM javagui.MyLogger setup" and I thought that it should be instead like this "May 25, 2014 2:05:30 PM javagui.CommonFrame" because I assigned that name to the logger.
Am I right? If yes, how can I fix it?
You have use the format property of the java.util.logging.SimpleFormatter to use the logger name instead of the source classname.
Modify your launch script to set the following system property:
-Djava.util.logging.SimpleFormatter.format="%1$tc %3$s%n%4$s: %5$s%6$s%n"
Or modify your logging.properties to include:
java.util.logging.SimpleFormatter.format=%1$tc %3$s%n%4$s: %5$s%6$s%n
Otherwise, you can create a custom formatter and use that extract the information you want to see in the log file.
You can use the log(LogRecord) method and specify the class name.
static public void setup(String className, Exception e, Level level) {
[...]
try {
[...]
logger.addHandler(fileTxt);
LogRecord record = new LogRecord(level, e.toString());
record.setThrown(e);
record.setSourceClassName(className);
record.setSourceMethodName("");
logger.log(record);
} catch (IOException | SecurityException ex) {
[...]
}
}
How do I print the entire stack trace using java.util.Logger? (without annoying Netbeans).
The question should've originally specified staying within Java SE. Omitting that requirment was an error on my part.
-do-compile:
[mkdir] Created dir: /home/thufir/NetBeansProjects/rainmaker/build/empty
[mkdir] Created dir: /home/thufir/NetBeansProjects/rainmaker/build/generated-sources/ap-source-output
[javac] Compiling 13 source files to /home/thufir/NetBeansProjects/rainmaker/build/classes
[javac] /home/thufir/NetBeansProjects/rainmaker/src/model/TelnetEventProcessor.java:44: error: 'void' type not allowed here
[javac] log.severe(npe.printStackTrace(System.out));
[javac] ^
[javac] 1 error
BUILD FAILED
code with the error:
package model;
import java.util.Observable;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TelnetEventProcessor extends Observable {
private static Logger log = Logger.getLogger(TelnetEventProcessor.class.getName());
private String string = null;
public TelnetEventProcessor() {
}
private void stripAnsiColors() {
Pattern regex = Pattern.compile("\\e\\[[0-9;]*m");
Matcher regexMatcher = regex.matcher(string);
string = regexMatcher.replaceAll(""); // *3 ??
}
public void parse(String string) {
this.string = string;
ifs();
}
// [\w]+(?=\.)
private void ifs() {
log.fine("checking..");
if (string.contains("confusing the hell out of")) {
Pattern pattern = Pattern.compile("[\\w]+(?=\\.)"); //(\w+)\.
Matcher matcher = pattern.matcher(string);
String enemy = null;
GameData data = null;
while (matcher.find()) {
enemy = matcher.group();
}
try {
data = new GameData.Builder().enemy(enemy).build();
log.fine("new data object\t\t" + data.getEnemy());
setChanged();
notifyObservers(data);
} catch (NullPointerException npe) {
log.severe(npe.printStackTrace(System.out));
}
} else if (string.contains("Enter 3-letter city code:")) {
log.fine("found enter city code");
} else {
}
}
}
see also:
https://stackoverflow.com/a/7100975/262852
The severe method is only used to log severe messages without associated throwable information. If you need to log throwable information then you should use the log method instead:
try {
data = new GameData.Builder().enemy(enemy).build();
log.fine("new data object\t\t" + data.getEnemy());
setChanged();
notifyObservers(data);
} catch (NullPointerException npe) {
log.log(Level.SEVERE, npe.getMessage(), npe);
}
Why don't you put the exception in the logger?
You can use this method :
logger.log(Level level, String msg, Throwable thrown)
Maybe a duplicated question? Java - Need a logging package that will log the stacktrace
Below the explanation from the given url
Using log4j
this is done with:
logger.error("An error occurred", exception);
The first argument is a message to be displayed, the second is the
exception (throwable) whose stacktrace is logged.
Another option is commons-logging,
where it's the same:
log.error("Message", exception);
With java.util.logging
this can be done via:
logger.log(Level.SEVERE, "Message", exception);
You don't explicitly print the stack trace; Throwables have stack traces attached to them, and you can pass a Throwable to the log methods:
log(Level level, String msg, Throwable thrown)
You could use Apache ExceptionUtils. In your case
try {
data = new GameData.Builder().enemy(enemy).build();
log.fine("new data object\t\t" + data.getEnemy());
setChanged();
notifyObservers(data);
} catch (NullPointerException npe) {
logger.info(**ExceptionUtils.getFullStackTrace(npe)**);
}
You should redirect the System.err to the logger, the process is not too simple but you can use this code:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LogOutputStream extends ByteArrayOutputStream {//java.io.OutputStream {
private String lineSeparator;
private Logger logger;
private Level level;
public LogOutputStream(Logger logger, Level level) {
super();
this.logger = logger;
this.level = level;
this.lineSeparator = System.getProperty("line.separator");
}
#Override
public void flush() throws IOException {
String record;
synchronized (this) {
super.flush();
record = this.toString();
super.reset();
if ((record.length() == 0) || record.equals(this.lineSeparator)) {
// avoid empty records
return;
}
this.logger.logp(this.level, "", "", record);
}
}
}
And The code to set this (that should called the when you first create the logger
Logger logger = Logger.getLogger("Exception");
LogOutputStream los = new LogOutputStream(logger, Level.SEVERE);
System.setErr(new PrintStream(los, true));
This will redirect the System.err stream to the logger.
You can also try to use ExceptionUtils from apache commons
The exception is due to the printstacktrace method being void, meaning it doesn't return anything. You are trying to do:
log.severe(npe.printStackTrace(System.out));
My guess is that the severe method needs a String and not void.
I want to get the output of this method myLogger.log(e) in a String variable to be able to test with Junit.
myLogger.log(e);
String s= ????
assertEqual(expected,s);
You can write a log4j appender that writes to a StringWriter. Look at this post:
How to read log4j output to a web page?
You can add an additional Appender to your Logger. Use a WriterAppender to write to a StringWriter then you change the content of your TextArea to the value of StringWriter#toString()
public static void main(String[] args) {
Logger logger = Logger.getLogger("logger");
Layout layout = new PatternLayout();
StringWriter stringWriter = new StringWriter();
WriterAppender writerAppender = new WriterAppender(layout, stringWriter);
logger.addAppender(writerAppender);
logger.error("test");
String string = stringWriter.toString();
System.out.println(string);
}
as described here.
I want to use default SLF4J + Logback configuration except setting org.springframework.data.document.mongodb logging level to DEBUG.
How can I do it with Java code?
I'm not using XML, and this decision made at runtime.
The following works for me but generally this is not a good idea. Your code will depend on Logback (you can't choose another logging framework behind SLF4J).
final org.slf4j.Logger logger =
org.slf4j.LoggerFactory.getLogger("test.package");
if (!(logger instanceof ch.qos.logback.classic.Logger)) {
return;
}
ch.qos.logback.classic.Logger logbackLogger =
(ch.qos.logback.classic.Logger) logger;
logbackLogger.setLevel(ch.qos.logback.classic.Level.TRACE);
logger.trace("some log");
Depending to logback-classic is not a good idea as #palacsint stated. You can achieve what you want using Java's Reflection API. Note that this approach puts some overhead to your program because of use of reflection.
Usage:
LogbackUtils.setLogLevel("com.stackoverflow.sample", "DEBUG")
Code:
public static final String LOGBACK_CLASSIC = "ch.qos.logback.classic";
public static final String LOGBACK_CLASSIC_LOGGER = "ch.qos.logback.classic.Logger";
public static final String LOGBACK_CLASSIC_LEVEL = "ch.qos.logback.classic.Level";
private static final Logger logger = LoggerFactory.getLogger(LogbackUtils.class);
/**
* Dynamically sets the logback log level for the given class to the specified level.
*
* #param loggerName Name of the logger to set its log level. If blank, root logger will be used.
* #param logLevel One of the supported log levels: TRACE, DEBUG, INFO, WARN, ERROR, FATAL,
* OFF. {#code null} value is considered as 'OFF'.
*/
public static boolean setLogLevel(String loggerName, String logLevel)
{
String logLevelUpper = (logLevel == null) ? "OFF" : logLevel.toUpperCase();
try
{
Package logbackPackage = Package.getPackage(LOGBACK_CLASSIC);
if (logbackPackage == null)
{
logger.info("Logback is not in the classpath!");
return false;
}
// Use ROOT logger if given logger name is blank.
if ((loggerName == null) || loggerName.trim().isEmpty())
{
loggerName = (String) getFieldValue(LOGBACK_CLASSIC_LOGGER, "ROOT_LOGGER_NAME");
}
// Obtain logger by the name
Logger loggerObtained = LoggerFactory.getLogger(loggerName);
if (loggerObtained == null)
{
// I don't know if this case occurs
logger.warn("No logger for the name: {}", loggerName);
return false;
}
Object logLevelObj = getFieldValue(LOGBACK_CLASSIC_LEVEL, logLevelUpper);
if (logLevelObj == null)
{
logger.warn("No such log level: {}", logLevelUpper);
return false;
}
Class<?>[] paramTypes = { logLevelObj.getClass() };
Object[] params = { logLevelObj };
Class<?> clz = Class.forName(LOGBACK_CLASSIC_LOGGER);
Method method = clz.getMethod("setLevel", paramTypes);
method.invoke(loggerObtained, params);
logger.debug("Log level set to {} for the logger '{}'", logLevelUpper, loggerName);
return true;
}
catch (Exception e)
{
logger.warn("Couldn't set log level to {} for the logger '{}'", logLevelUpper, loggerName, e);
return false;
}
}
// getFieldValue() method omitted for bravity from here,
// but available at GitHub link below.
Full code including tests: Github Gist.
i have a simple logging program ie:
public class LoggingExample1 {
public static void main(String args[]) {
try {
LogManager lm = LogManager.getLogManager();
Logger logger;
FileHandler fh = new FileHandler("log_test.txt");
logger = Logger.getLogger("LoggingExample1");
lm.addLogger(logger);
logger.setLevel(Level.INFO);
fh.setFormatter(new SimpleFormatter());
logger.addHandler(fh);
// root logger defaults to SimpleFormatter. We don't want messages
// logged twice.
//logger.setUseParentHandlers(false);
logger.log(Level.INFO, "test 1");
logger.log(Level.INFO, "test 2");
logger.log(Level.INFO, "test 3");
fh.close();
} catch (Exception e) {
System.out.println("Exception thrown: " + e);
e.printStackTrace();
}
}
}
i get this log:
Aug 1, 2011 5:36:37 PM LoggingExample1 main
INFO: test 1
Aug 1, 2011 5:36:37 PM LoggingExample1 main
INFO: test 2
Aug 1, 2011 5:36:37 PM LoggingExample1 main
INFO: test 3
but i want to remove the messages like: LoggingExample1 main and INFO
and only keep the data that are logged by code.
what can i do???
This is because you are using a SimpleFormatter which always logs the class name, method name etc. If you don't want this information, you can write your own formatter. Here is a simple example of a formatter which just outputs the log level and the log message:
import java.util.logging.*;
class MyFormatter extends Formatter{
/* (non-Javadoc)
* #see java.util.logging.Formatter#format(java.util.logging.LogRecord)
*/
#Override
public String format(LogRecord record) {
StringBuilder sb = new StringBuilder();
sb.append(record.getLevel()).append(':');
sb.append(record.getMessage()).append('\n');
return sb.toString();
}
}
Use this formatter in your file handler:
fh.setFormatter(new MyFormatter());
This will output:
INFO:test 1
INFO:test 2
INFO:test 3
Extend your class with formatter class as below:
public class MyFormatter extends SimpleFormatter{
#Override
public synchronized String format(LogRecord record){
record.setSourceClassName(MyFormatter.class .getName());
return String.format(
" [%1$s] :%2$s\n",
record.getLevel().getName(), formatMessage(record));
}
}
Then in you main class, set console handler and filehandler format as your class format object.
In my case ,it is consoleHandler.format(new MyFormatter);
filehandler.format(new Myformatter);
Most imp thing is you have to set
logger.setUseParentHandler(false);
now you are good to go!
If I understand you correctly, you want to remove INFO and only have test 1 and so on?
If that is the case, I don't think its possible. The reason is that you need to know what type of message this is.
ERROR
WARNING
INFO
DEBUG
TRACE
You can change the level to only display WARNING and up if you want.
Here is how I do this, simplified version of SimpleFormatter
package org.nibor.git_merge_repos;
import java.util.Date;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
public class CustomLogFormatter extends Formatter {
private final Date dat = new Date();
private static final String seperator = " : ";
public synchronized String format(LogRecord record) {
dat.setTime(record.getMillis());
return dat + seperator + record.getLevel().getName() + seperator + record.getMessage() + "\n";
}
}