Adding configurable FileHandler to java.util.logging.Logger - java

We have a static java.util.logging.Logger variable as myLogger.
Currently, it prints only to console so we have to add FileHandler to it.
The project is Profile based so depending on the profile specified at the compile time, appropriate application-{profile}.properties file will be loaded. This file has different log file location for each profile.
How can a FileHandler be instantiated with value from application.properties?
Code snippet:
private static String logFileName;
#Value("${loggingSftp.logFileName}")
public void setLogFileName(String logFileName) {
SftpConfiguration.logFileName = logFileName;
}
private static final Logger LOGGER = Logger.getLogger(SftpConfiguration.class.getPackage().getName());
private static FileHandler fileHandler;
static {
try {
LOGGER.info("log file name: " + logFileName);
fileHandler = new FileHandler(LoggingFileHandler.getLogFileName()
+ LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd")));
LOGGER.addHandler(fileHandler);
} catch (IOException e) {
e.printStackTrace();
}
}
Now, logFileName is 'null' when static block is initializing and adding fileHandler.

What is happening is the static block executes before your properties are loaded.
You need to move the static block into a #PostConstruct method so all of your injected values are present. Then lookup your property values from within that #PostConstruct method.

Related

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.

System.setProperty for logger not modified after static method call in another class

I have a program in which I need to set the logger formatting, but since there are several entry points (I don't control all of them) I want to place the System.setProperty() call in the constructor of the class where all of the entry points converge.
Main class
public static void main(String[] args) throws Exception {
String foo = "bar";
String baz = "boo";
static final String FORMATTER_CONFIG = "%1$tb %1$td, %1$tY %1$tr %4$s: %5$s%n";
SomeClass sc = new SomeClass();
sc.method1(foo);
sc.method2(baz);
try {
SomeOtherClass soc = SomeOtherClass.newInstance();
} catch (Exception e) {;
}
// Next line will update the property of the logging formatter for this instance
System.setProperty("java.util.logging.SimpleFormatter.format", FORMATTER_CONFIG);
MyThirdClass mtc = MyThirdClass.getStaticMethod(foo, baz);
Logger logger = Logger.getLogger("myProject.main");
Handler h = new FileHandler("LogHere.txt");
h.setFormatter(new SimpleFormatter());
logger.addHandler(h);
logger.setLevel(Level.INFO);
logger.info("Info level log");
logger.fine("Fine level log");
logger.finer("Finer level log");
logger.finest("Finest level log");
}
Constructor of MyThirdClass:
public MyThirdClass() throws SecurityException {
this.classStaticVar = EntirelyDifferentClass.getVar();
logger = Logger.getLogger(this.getClass().getName());
Handler h = null;
try {
h = new FileHandler("MTC_LogHere.txt");
} catch (IOException ex) {
logger.warning("Failed to initialize custom FileHandler for MTC_LogHere.txt");
}
h.setFormatter(new SimpleFormatter());
logger.addHandler(h);
logger.setLevel(Level.INFO);
}
I want to put the call to System.setProperty as the first line of the constructor in MyThirdClass (before the call to EntirelyDifferentClass) but when I do the logging is not formatted according to the custom rules. If I leave it right where it is, the line before the call to MyThirdClass in the main method, it works as intended. Note that I've also tried putting the code block for System.setProperty in the getStaticMethod() method.
I understand that the system.properties are all static variables, so I have to believe that whatever is happening in the JVM during the method call to MyThirdClass.getStaticMethod is locking in the static properties before I can change it.
Am I understanding what is happening in the JVM correctly?
Since I don't control all entry points in the actual production
version of this program, and I also don't control the compile
settings nor can I issue a -Djava line command to set the properties
that way (which I've also verified will work) I need to be able to
do this programmatically in MyThirdClass. What am I
missing/overlooking, or am I just out of luck?
This answer was originally edited into the question above by the OP Bryan, I have posted this here and requested him to do so himself:
For anyone wanting to know the answer, I added a static block at the
class level of MyThirdClass:
protected static Logger logger = null;
static {
System.setProperty("java.util.logging.SimpleFormatter.format", FORMATTER_CONFIG);
logger = Logger.getLogger("myProject.MyThirdClass");
Handler h = null;
try {
h = new FileHandler("LogHere.txt");
}
catch (Exception ex) {
System.out.println("Exception");
}
h.setFormatter(new SimpleFormatter());
logger.addHandler(h);
logger.setLevel(Constants.LOGGING_LEVEL);
}
When you call a static method, such as getStaticMethod() in your case, an instance of the class is not created, and therefore the constructor is not called.
Try putting System.setProperty() in the static method instead of in the constructor.
For example:
import java.util.logging.*;
public class Main {
public static void main(String[] args) {
SomeClass.staticMethod();
Logger logger = Logger.getLogger(Main.class.getName());
logger.setLevel(Level.INFO);
logger.info("Info level log");
}
}
public class SomeClass {
public static void staticMethod() {
final String FORMATTER_CONFIG = "%1$tb %1$td, %1$tY %1$tr %4$s: %5$s%n";
System.setProperty("java.util.logging.SimpleFormatter.format", FORMATTER_CONFIG);
}
}

Java Log File creates multiple copies

I am trying to create a logs file in a Java swings application that will contain all the messages generated by the code. But unfortunately it does not create 1 single log file but creates a structure as shown. I need 1 single log file.
My code:
//get the logger object
logger = Logger.getLogger("MyLog");
try {
// This block configure the logger with handler and formatter
loggerFH = new FileHandler(System.getProperty("user.dir") + "\\resources\\logs\\logs.txt",true);
logger.addHandler(loggerFH);
SimpleFormatter formatter = new SimpleFormatter();
loggerFH.setFormatter(formatter);
} catch (IOException | SecurityException ex) {
logger.severe(ex.getMessage());
outputArea.append(ex.getMessage());
}
Your files are being rotated.
Did you try solution from this:
Java FileHandler disable log rotation
FileHandler fh = new FileHandler( "path" , 0, 1, false);
I realized my Logger file was used by several instances at a time. So when 1 instance of filehandler locked the access to the file a new file was getting created. So I created a Synchronized class to handle all logging. And it worked great.
public class SynchronizedLogger {
//Logger to put error logs and messages in the log file
public static Logger logger = Logger.getLogger("MyLog");
//Logger Filehandler
private static FileHandler loggerFH;
public SynchronizedLogger() {
}
public static synchronized void writeLog(String message) {
logger.info(message);
}
public SynchronizedLogger(int i) {
try {
synchronized (this) {
// This block configures the logger with handler and formatter
loggerFH = new FileHandler(System.getProperty("user.dir") + "\\resources\\logs\\logs.txt", 0, 1, true);
logger.setUseParentHandlers(false);
logger.addHandler(loggerFH);
SimpleFormatter formatter = new SimpleFormatter();
loggerFH.setFormatter(formatter);
}
} catch (IOException | SecurityException ex) {
writeLog(ex.getMessage());
outputArea.append("\n\n" + ex.getMessage());
}
}
}

java logger calling from another class

I'm learning this logger from java and trying to call the class MyLogger from the main class but it seems not to be working
MyLogger.java
public class MyLogger {
public MyLogger() {
Logger logger = Logger.getLogger("MyLog");
FileHandler fh;
try {
fh = new FileHandler("c:\\opt\\MyLogFile.log", true);
logger.addHandler(fh);
logger.setLevel(Level.ALL);
SimpleFormatter formatter = new SimpleFormatter();
fh.setFormatter(formatter);
logger.log(Level.WARNING,"My first log");
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client.java
public class Client {
private final static Logger LOGGER = Logger.getLogger(Client.class.getName());
public static void main(String[] args) {
LOGGER.setLevel(Level.SEVERE);
LOGGER.getName();
LOGGER.getClass();
LOGGER.info("ABC");
}
}
Why is it not calling the MyLogger.java to create the log file?
Is there a beter way to log the eclipse console rather than putting LOGGER on each methods for each class?
Why some uses flush() before closing the log file?
Why is it not calling the MyLogger.java to create the log file?
If it refers to running Client, then that's because there is no relation between that class and your MyLogger class.
Is there a beter way to log the eclipse console rather than putting
LOGGER on each methods for each class?
Better is very subjective. The majority of source code I've seen use the paradigm you're using in your Client class. That is, declaring a static final Logger with the name of enclosing class and using it where you need to log something within that class.
Some applications make use of AOP concepts for logging.
Why some uses flush() before closing the log file?
Some implementations may buffer log messages so as not to make many small IO system calls, but instead make few big ones. Closing the log file may not flush the buffer, so you'll want to flush it explicitly.

Problem with java.util.logging and Lock file

I have an application that scans a set of files. And this scanning take place parallely. I mean if I upload 5 files, five process will be created and start executing the job parallely. Now For logging the exceptions for the jobs running, there is only one log file. The problem that is coming is, the logger creates a lot of logs files as -
mylogfile.log(actual log file)
mylogfile.log.1
mylogfile.log.2
mylogfile.log.3
mylogfile.log.4
mylogfile.log.5
...
with their respective lock files.
Below is the code snippet that I used:
public static Logger getLogger(final String className) {
final Logger logger = Logger.getLogger(className);
FileHandler fh;
try {
// This block configure the logger with handler and formatter
fh = new FileHandler("mylogfile.log", true);
logger.addHandler(fh);
logger.setLevel(Level.ALL);
logger.setUseParentHandlers(false);
fh.setFormatter(new SimpleFormatter());
} catch (final SecurityException e) {
// }
} catch (final IOException e) {
//
}
return logger;
}
How do I make sure that only one log file should be used to write the exception of all the jobs running parallely..??
Thanks,
Anish
I face the same problem and what I was missing is an standard programming practice that I should close the filehandler after I finish the logging.
So you can create multiple loggers but make sure you close them at the end.
Following 2 lines in below example:
successLogFileHandler.close();
failureLogFileHandler.close();
class LoggerExample {
private Logger loggerFailure;
private Logger loggerSuccess;
public void myLogger() {
// some code block
FileHandler successLogFileHandler = new FileHandler(successLogFile.getAbsolutePath());
FileHandler failureLogFileHandler = new FileHandler(failureLogFile.getAbsolutePath());
// Create loggers
loggerFailure = createLogger(STR_FAILURE_LOG_FILE, failureLogFileHandler, Level.INFO, customFormatter);
loggerSuccess = createLogger(STR_SUCCESS_LOG_FILE, successLogFileHandler, Level.INFO, customFormatter);
// Write messages into loggers
// and at the end close the file handlers
successLogFileHandler.close();
failureLogFileHandler.close();
}
public static Logger createLogger(String strLoggerName, FileHandler handler, Level level, Formatter formatter) throws Exception
{
Logger logger = Logger.getLogger(strLoggerName);
logger.setLevel(level);
handler.setFormatter(formatter);
logger.addHandler(handler);
return logger;
}
}
You can use a singlton locator class that you can retrieve the logger from, that would force your application to use one logger (which means you'll have one log file)
class LoggerLocator {
private static LoggerLocator locator = new LoggerLocator();
private Logger logger;
/**
*
*/
private LoggerLocator() {
initLogger();
}
/**
* #return
*/
public static LoggerLocator getLocator(){
return locator;
}
/**
* #return
*/
public Logger getLogger() {
return logger;
}
/**
*
*/
private void initLogger() {
logger = Logger.getLogger("My General Logger");
FileHandler fh;
try {
// This block configure the logger with handler and formatter
fh = new FileHandler("mylogfile.log", true);
logger.addHandler(fh);
logger.setLevel(Level.ALL);
logger.setUseParentHandlers(false);
fh.setFormatter(new SimpleFormatter());
} catch (final SecurityException e) {
// }
} catch (final IOException e) {
//
}
}
}
In your jobs you will get the logger by the following call:
Logger logger = LoggerLocator.getLocator().getLogger();
This does not have much to do with parallel calls. As well from single thread with each call to your getLogger (even with same string as argument) you create a new FileHandler and add it to existing logger.Maybe you do not want to do all of the setup each time when you call your getLogger-method.
//now we will create new Filehandler and set it to logger
getLogger("identifier").log(Level.SEVERE, "Log to one file");
//now we have already one filehandler, but lets add one more
getLogger("identifier").log(Level.SEVERE, "Log to two files");
//now we have already two filehandlers, but lets add one more
getLogger("identifier").log(Level.SEVERE, "Log to three files");
Configure logging once and after that you can get reference to logger via java.util.loggingLogger.getLogger. Overview from Sun gives more ideas how to configure logging via properties files.

Categories

Resources