Java util logging wrong format after overriding the formatter - java

I want to change the format of the log but after I overrided the formatter, it still outputs in the old way.
private void initializeLogger() {
logger = Logger.getLogger("gameLogger");
try {
Handler handler = new FileHandler("/home/bobby/IdeaProjects/GoFishBobby/src/logs/log1.txt");
MyFormatter myFormatter = new MyFormatter();
handler.setFormatter(myFormatter);
logger.addHandler(handler);
for (Handler parentHandler : logger.getParent().getHandlers())
logger.removeHandler(parentHandler);
}catch(IOException e){
System.out.println("IO exception");
}
logger.info("Game starting : ");
}
I overrided the default loggerFormatter
public class MyFormatter extends Formatter{
public String format(LogRecord record){
return record.getMessage();
}
}
and the output still contains the info line with the information I dont need
SEVERE: NativePlayer : 0 Scored A Book of rank 3
Mar 05, 2017 1:15:44 PM Game logAfterPlay
INFO: The transfer hand is:
4D 4H 4S
Edit
I just found out the log in the file is correct, but why its still like this on the console? how do I get rid of it on the console. I already removed all the parent logger.

You can locate all handlers by using the following method to walk all currently created loggers.
public static void printLoggerTree() {
final LogManager manager = LogManager.getLogManager();
synchronized (manager) {
final Enumeration<String> e = manager.getLoggerNames();
while (e.hasMoreElements()) {
final Logger l = manager.getLogger(e.nextElement());
if (l != null) {
for (Handler h : l.getHandlers()) {
System.out.println(l.getName() + "=" + h);
}
}
}
}
}
The logger tree changes over time when different parts of your code requests new loggers. So it matters when you call this method.
A better practice is to setup your own logging.properties file and configure your project to use that on startup. You also don't need to create your own formatter as you can change the format pattern of the SimpleFormater on the command line by using the following:
-Djava.util.logging.SimpleFormatter.format="%5$s%n"

Related

Java: Logging: Custom Formatter

I wrote a simple Custom Formatter for my logging that prints a DateTime in the specified format. Everything works fine, but the datetime doesn't get printed in my log file. Below is my CustomFormatter.java:
CustomFormatter.java:
public class CustomFormatter extends Formatter {
SimpleDateFormat sdf;
public CustomFormatter() {
sdf = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");
}
public String format(LogRecord rec) {
StringBuffer buf = new StringBuffer(1000);
buf.append(formatMessage(rec));
return buf.toString();
}
public String getHead(Handler h) {
return (sdf.format(new java.util.Date()) + ": \t");
}
public String getTail(Handler h) {
return "\n";
}
}
Im my main class, I initialize my logger as:
Main.java:
private static Logger logger = Logger.getLogger("org.somecompany.someproject");
public static void main(String[] args){
try{
String _pattern = "myLogger.log.%g";
int numLogFiles = 10;
int fileSize = 1000000;
boolean appendToFile = true;
FileHandler fh = new FileHandler(pattern, fileSize, numLogFiles, appendToFile);
fh.setFormatter(new CustomFormatter());
logger.addHandler(fh);
logger.setLevel(Level.ALL);
} catch(IOException i) { System.out.println("Unable to init logger");}
logger.info("Begin");
logger.info("Line 1");
logger.info("Line 2");
logger.info("End");
fh.close();
fh = null;
}
The log file should have a datetime printed at the beginning of each line and it isn't. Any help is appreciated.
I think you misunderstand the purpose of getHead() and getTail().
Those methods are not called per log entry - getHead() is called once before the Handler (FileHandler) logs the first entry and getTail() is called once before the Handler (FileHandler) closes the log stream.
This is used for example in the XMLFormatter to create a valid XML file (where there must be an XML start tag before the first log entry and an XML end tag after the last log entry to be valid XML).
If you look closely at the created log file this is exactly what happens: the log file creates a timestamp at the start and ends with a newline.
Note that adding time stamps to your log entries doesn't require writing a CustomFormatter. Properly configuring a SimpleFormatter is enough for your purpose.

Java Logger usage

I made a custom logger for my project by using java.util.logging:
public class SpotifyLogger {
private static final Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
public SpotifyLogger(String loggerFilePath) throws IOException {
Logger myLogger = Logger.getLogger("");
// suppress console messaging
Handler[] handlers = myLogger.getHandlers();
if (handlers[0] instanceof ConsoleHandler) { //exception occurs here
myLogger.removeHandler(handlers[0]);
}
// set level
LOGGER.setLevel(Level.SEVERE);
// create a txt handler
FileHandler textFileHandler = new FileHandler(loggerFilePath);
SimpleFormatter simpleFormatter = new SimpleFormatter();
textFileHandler.setFormatter(simpleFormatter);
LOGGER.addHandler(textFileHandler);
}
public void log(String user, Exception e) {
LOGGER.log(Level.SEVERE, user, e);
}
}
For the client and the server parts of my program, I create two separate Logger objects:
// class member initialized as null, because of exception handling
private SpotifyLogger logger = null;
//...
//in constructor:
this.logger = new SpotifyLogger(LOGGER_FILE_NAME); // the LOGGER_FILE_NAME is different for the client and the server
When I test my program manually, the loggers seem to work (the two log files contain exceptions that I have caused). Then, I wrote automatic tests. For each class that I am testing (a total of 5), I create a separate logger object with a different destination path. The tests (for whichever class comes first) work correctly. All other tests fail because I get an ArrayIndexOutOfBoundsException, when I initialize the logger for that particular class. The reason is that I am trying to access handlers[0], when handlers has 0 length. From what I understood after searching the web, this is because the logger is using parent handlers. I tried this:
public SpotifyLogger(String loggerFilePath) throws IOException {
Logger myLogger = Logger.getLogger("");
// suppress console messaging
myLogger.setUseParentHandlers(false);
Handler[] handlers = myLogger.getHandlers();
if (handlers.length > 0) {
if (handlers[0] instanceof ConsoleHandler) {
myLogger.removeHandler(handlers[0]);
}
}
//etc
}
I don't get an exception anymore but the logging doesn't work. What am I doing wrong?
If you want different Loggers, you need to supply different names for each. Hence this line of your code (in SpotifyLogger constructor) always returns the same Logger.
Logger myLogger = Logger.getLogger("");
This actually returns the java.util.logging.LogManager.RootLogger which has a single Handler which is an instance of ConsoleLogger. You subsequently remove that Handler in the first invocation of SpotifyLogger constructor, hence in every subsequent invocation, method getHandlers returns an empty array.
Since you only ever add Handlers to the global Logger, another FileHandler is added to the global logger every time SpotifyLogger constructor is called. I have not verified but I believe that a Logger will use the first, appropriate Handler in the array returned by method getHandlers, hence the behavior you are seeing whereby only the first log file is being written to, i.e. the file that you passed to the first invocation of SpotifyLogger constructor.
Note that you have not provided a reproducible example so I cannot verify any of the above with regard to your context. I only tested the code in your question in order to arrive at the above.
Consider the following rewrite of class SpotifyLogger – including a main method for testing purposes only.
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
public class SpotifyLogger {
private static final String[] NAMES = {"First", "Second"};
private static int count;
private int index;
public SpotifyLogger(String loggerFilePath) throws IOException {
index = count;
Logger myLogger = Logger.getLogger(NAMES[count++]);
myLogger.setUseParentHandlers(false);
// set level
myLogger.setLevel(Level.SEVERE);
// create a txt handler
FileHandler textFileHandler = new FileHandler(loggerFilePath);
SimpleFormatter simpleFormatter = new SimpleFormatter();
textFileHandler.setFormatter(simpleFormatter);
myLogger.addHandler(textFileHandler);
}
public void log(String user, Exception e) {
Logger myLogger = Logger.getLogger(NAMES[index]);
myLogger.log(Level.SEVERE, user, e);
}
public static void main(String[] args) {
try {
SpotifyLogger x = new SpotifyLogger("spotifyx.log");
SpotifyLogger y = new SpotifyLogger("spotifyy.log");
x.log("George", new Exception());
y.log("Martha", new RuntimeException());
}
catch (IOException x) {
x.printStackTrace();
}
}
}
Note that you are correct regarding parent Handlers, hence the following line in the above code:
myLogger.setUseParentHandlers(false);
After running the above code, the contents of file spotifyx.log is:
Feb 12, 2022 2:12:28 PM javalogp.SpotifyLogger log
SEVERE: George
java.lang.Exception
at javalogp/javalogp.SpotifyLogger.main(SpotifyLogger.java:38)
And the contents of file spotifyy.log is:
Feb 12, 2022 2:12:28 PM javalogp.SpotifyLogger log
SEVERE: Martha
java.lang.RuntimeException
at javalogp/javalogp.SpotifyLogger.main(SpotifyLogger.java:39)
And no log messages are written to the console.
This doesn't make much sense to me
Logger myLogger = Logger.getLogger("")
Reference: Oracle java docs

Why does my java logging go to console as well as file

I configure my application to use a logging class rather than the logging.properties file in the jre conf folder using -Djava.util.logging.config.class=com.jthink.songkong.logging.StandardLogging
And it works, except that I notice that information that is just mean to be going to my log file is also going to console window, this is being noticed when using --win-console with jpackage on Windows but I think it was already happening before I was using JPackage
This is my logging class:
public final class StandardLogging
{
public static int LOG_SIZE_IN_BYTES = 10000000;
//Default parent logger
public static Logger defaultLogger = Logger.getLogger("");
//jaudiotagger logger
public static Logger ioLogger = Logger.getLogger("org.jaudiotagger");
//SongKong logger
public static Logger debugLogger = Logger.getLogger("com.jthink");
//SongKong usaer Message Logger
public static Logger userInfoLogger = Logger.getLogger("com.jthink.songkong.ui.MainWindow");
//General c3p0
public static Logger c3p0Logger = Logger.getLogger("com.mchange.v2.c3p0");
//For capturing Preapred stament Cache hits
//public static Logger c3p0ConnectionLogger = Logger.getLogger("com.mchange.v2.c3p0.stmt");
//For capturing stack traces when connection lasted too long
public static Logger c3p0PooledConnectionLogger = Logger.getLogger("com.mchange.v2.resourcepool.BasicResourcePool");
//HIbernate SQL
public static Logger hibernateLogger = Logger.getLogger("org.hibernate.SQL");
//TODO not sure this even used, I think CmdLogger just does System.out
private static Logger cmdlineLogger = Logger.getLogger("cmdline");
protected void configureLoggerLevels()
{
//Default Log Level, used by any 3rd party libs we are using if not configured further
defaultLogger.setLevel(Level.WARNING);
//For Debug (songKong and jaudiotagger)
ioLogger.setLevel(Level.WARNING);
ioLogger.setUseParentHandlers(false);
try
{
//If GeneralPreferences exist and we can access set from user value
ioLogger.setLevel(Level.parse(String.valueOf(GeneralPreferences.getInstance().getIoDebugLevel())));
}
catch(Exception ex)
{
}
debugLogger.setLevel(Level.WARNING);
debugLogger.setUseParentHandlers(false);
try
{
//If GeneralPreferences exist and we cBuildBuiklan access set from user value
debugLogger.setLevel(Level.parse(String.valueOf(GeneralPreferences.getInstance().getDebugLevel())));
}
catch(Exception ex)
{
}
//C3p0 Logger
c3p0Logger.setLevel(Level.INFO);
c3p0Logger.setUseParentHandlers(false);
//Set to FINEST to see SQL
hibernateLogger.setLevel(Level.WARNING);
hibernateLogger.setUseParentHandlers(false);
//For Capturing CheckIn/Outs nad Prepared Statement Cache Hits
//c3p0ConnectionLogger.setLevel(Level.FINEST);
//c3p0ConnectionLogger.setUseParentHandlers(false);
//For capturing stacktrace from timed out connections
c3p0PooledConnectionLogger.setLevel(Level.INFO);
c3p0PooledConnectionLogger.setUseParentHandlers(false);
//For user message log
userInfoLogger.setUseParentHandlers(false);
userInfoLogger.setLevel(Level.FINEST);
userInfoLogger.setUseParentHandlers(false);
userInfoLogger.setLevel(Level.FINEST);
}
protected void configureHandlers() throws Exception
{
//Set Filehandler used for writing to debug log
String logFileName = Platform.getPlatformLogFolderInLogfileFormat() + "songkong_debug%u-%g.log";
FileHandler fe = new FileHandler(logFileName, LOG_SIZE_IN_BYTES, 10, true);
fe.setEncoding(StandardCharsets.UTF_8.name());
fe.setFormatter(new LogFormatter());
fe.setLevel(Level.FINEST);
//Set Filehandler used for writing to user log
String userLogFileName = Platform.getPlatformLogFolderInLogfileFormat() + "songkong_user%u-%g.log";
FileHandler userFe = new FileHandler(userLogFileName, LOG_SIZE_IN_BYTES, 10, true);
userFe.setFormatter(new com.jthink.songkong.logging.UserLogFormatter());
userFe.setLevel(Level.FINEST);
//Write this output to debug log file
//defaultLogger.addHandler(fe);
c3p0Logger.addHandler(fe);
c3p0PooledConnectionLogger.addHandler(fe);
//c3p0ConnectionLogger.addHandler(fe);
ioLogger.addHandler(fe);
debugLogger.addHandler(fe);
hibernateLogger.addHandler(fe);
//Write this output to user log file
userInfoLogger.addHandler(userFe);
//For cmd line output, is this still used
cmdlineLogger.setUseParentHandlers(false);
ConsoleHandler cmdLineHandler = new java.util.logging.ConsoleHandler();
cmdLineHandler.setLevel(Level.FINEST);
cmdLineHandler.setFormatter(new CmdLineFormatter());
cmdlineLogger.addHandler(cmdLineHandler);
System.out.println("debuglogfile is:" + logFileName);
System.out.println("userlogfile is:" + userLogFileName);
}
public StandardLogging()
{
try
{
configureLoggerLevels();
configureHandlers();
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
And this is an example of code that I would just expect to go to my songkong-debug0-0.log file, but it is also being output to the console window:
MainWindow.logger.warning("User Dir:"+ System.getProperty("user.dir"));
MainWindow.logger.warning("Java Dir:"+ System.getProperty("java.home"));
Why would that be?
Add code to com.jthink.songkong.logging.StandardLogging to print the logger tree at the end of your configuration. This will help you troubleshoot what is happening. Since you are using a configuration class you can even make your own system property to toggle printing the logger tree if you need to in the future.
If your console output looks like the format of the CmdLineFormatter you created then code is either using that logger or a child logger that is printing to the parent handlers. Assuming you control the format of the CmdLineFormatter you can include the logger name in the output to locate the logger in question.
If the output looks like the SimpleFormatter then more likely it is the console handler that is attached to the root logger. Simply remove that handler from the root logger in your configuration class.
A more complete solution is to invoke LogManager.reset at the start of your StandardLogging class constructor. This would clear out the configuration that the JRE set prior to invoking your changes. An alternative is to set the java.util.logging.config.file to point to a null device from the command line in addition to setting your java.util.logging.config.class.

Java util logger: custom formatter only works for some levels

I have made a custom formatter for the java.util logger, implemented as below
logger = Logger.getAnonymousLogger();
logger.setUseParentHandlers(false);
sb = new StringBuffer();
try {
Handler handler = new FileHandler("xx.log");
handler.setFormatter(new CustomFormatter());
logger.addHandler(handler);
} catch (IOException e) {
e.printStackTrace();
}
private class CustomFormatter extends Formatter {
#Override
public String format(LogRecord record) {
String msg = record.getMessage();
sb.append(msg);
System.out.print(sb.toString());
return sb.toString();
}
}
Running the following code:
logger.finest(msg);
logger.finer(msg);
logger.fine(msg);
logger.config(msg);
logger.info(msg);
logger.warning(msg);
logger.severe(msg);
logger.log(new LogRecord(Level.FINE, msg));
Only info, warning, and severe work. the other methods run but return nothing to console, and write nothing to the log file. I need to implement at least 4 logging levels, but only the 3 work.
What is happening to log,config,fine,finer, finest? How do I get them to use my formatter? Or how do I pass the level to the formatter?
You may set the Level of logging with setlevel(Level)
Set the log level specifying which message levels will be logged by
this logger. Message levels lower than this value will be discarded.
In your case as you are interested in all levels, it would be :
logger.setLevel(Level.ALL);

Logback usage and printing lists

I am starting using logback and I want to know if there are better ways of doing something.
I have this code:
public class ClassA {
private List<String> l;
private Logger logger;
public ClassA(){
this.logger = LoggerFactory.getLogger(this.getClass().getName());
}
....
public List<String> method() {
this.logger.debug("method()");
List<String> names;
try {
names = otherClass.getNames();
} catch (Exception e) {
String msg = "Error getting names";
this.logger.error(msg);
throw new ClassAexception(msg, e);
}
this.logger.debug("names: {}", xxxxx);
return names;
}
I have some doubts so far:
Every class will have a this.logger = LoggerFactory.getLogger(this.getClass().getName()); to create a logger.
Every method will have a this.logger.debug("method()"); to know when a method is called.
That doesn't look good. Is there a way to solve it?
Also I want to print a list in the .log in this line: this.logger.debug("names: {}", xxxxx);
the xxxxx should be replaced with something to print the list. An anonymous class?
Thanks for reading!
Using AspectJ and log4j you can use this. Compile your code with ajc compiler instead of javac and then run as normal with java executable.
You need to have the aspectjrt.jar and log4j.jar on the classpath.
import org.aspectj.lang.*;
import org.apache.log4j.*;
public aspect TraceMethodCalls {
Logger logger = Logger.getLogger("trace");
TraceMethodCalls() {
logger.setLevel(Level.ALL);
}
pointcut traceMethods()
//give me all method calls of every class with every visibility
: (execution(* *.*(..))
//give me also constructor calls
|| execution(*.new(..)))
//stop recursion don't get method calls in this aspect class itself
&& !within(TraceMethodCalls);
//advice before: do something before method is really executed
before() : traceMethods() {
if (logger.isEnabledFor(Level.INFO)) {
//get info about captured method and log it
Signature sig = thisJoinPointStaticPart.getSignature();
logger.log(Level.INFO,
"Entering ["
+ sig.getDeclaringType().getName() + "."
+ sig.getName() + "]");
}
}
}
Check out the AspectJ documentation on how to change the TraceMethodCalls calls.
// e.g. just caputre public method calls
// change this
: (execution(* *.*(..))
// to this
: (execution(public * *.*(..))
Regarding the
Also I want to print a list in the
.log in this line:
this.logger.debug("names: {}", xxxxx);
That's supported by slf4j/logback by default. Just do
logger.debug("names: {}", names);
for example
List<String> list = new ArrayList<String>();
list.add("Test1"); list.add("Test2"); list.add("Test3");
logger.debug("names: {}", list);
//produces
//xx::xx.xxx [main] DEBUG [classname] - names: [Test1, Test2, Test3]
Or do you want something specifically different?

Categories

Resources