Java Log File creates multiple copies - java

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());
}
}
}

Related

Logging exception stacktrace with custom rollingFileAppender and custom JsonLayout

I have written a custom logger with its own rolling file appender. I am logging an exception using one of the log messages (e.g., info(), debug(), etc.), I am excepting that it would print the entire stacktrace of the exception, but it is printing only the message.
public class Logger {
private static ConcurrentHashMap<String, Logger> loggerMap = new ConcurrentHashMap<String, Logger>();
private static ConcurrentHashMap<String, FileAppender> appenderMap = new ConcurrentHashMap<String, FileAppender>();
private static LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
public static org.slf4j.Logger getLoggerWithRollingFileAppender(String appenderName,
String loggerName,
Level logLevel) {
RollingFileAppender rollingFileAppender;
// Check if the appender is already created.
if (! appenderMap.containsKey(appenderName)) {
LayoutWrappingEncoder jsonEncoder = new LayoutWrappingEncoder();
JsonFormatter jsonFormatter = new JacksonJsonFormatter();
((JacksonJsonFormatter) jsonFormatter).setPrettyPrint(false);
JsonLayout jsonLayout = new JsonLayout();
jsonLayout.setJsonFormatter(jsonFormatter);
jsonLayout.setTimestampFormat("yyyy-MM-dd HH:mm:ss.SSS");
jsonLayout.setContext(loggerContext);
jsonLayout.setAppendLineSeparator(true);
jsonLayout.setIncludeException(true);
jsonLayout.setIncludeMDC(true);
jsonEncoder.setLayout(jsonLayout);
jsonEncoder.setContext(loggerContext);
jsonEncoder.start();
rollingFileAppender = new RollingFileAppender();
rollingFileAppender.setContext(loggerContext);
rollingFileAppender.setName(appenderName);
rollingFileAppender.setEncoder(jsonEncoder);
rollingFileAppender.setAppend(true);
SizeAndTimeBasedRollingPolicy sizeAndTimeBasedRollingPolicy =
new SizeAndTimeBasedRollingPolicy();
sizeAndTimeBasedRollingPolicy.setContext(loggerContext);
sizeAndTimeBasedRollingPolicy.setParent(rollingFileAppender);
sizeAndTimeBasedRollingPolicy.setFileNamePattern(logBaseDir +
File.separator +
appenderName + "-" +
applnName + "-%d{yyyy-MM-dd}.%i.log");
sizeAndTimeBasedRollingPolicy.setMaxHistory(maxLogHistory);
sizeAndTimeBasedRollingPolicy.setMaxFileSize(FileSize.valueOf(maxLogFileSize));
sizeAndTimeBasedRollingPolicy.setTotalSizeCap(FileSize.valueOf(totalLogSizeCap));
sizeAndTimeBasedRollingPolicy.start();
rollingFileAppender.setRollingPolicy(sizeAndTimeBasedRollingPolicy);
rollingFileAppender.start();
appenderMap.put(appenderName, rollingFileAppender);
} else {
rollingFileAppender = (RollingFileAppender) appenderMap.get(appenderName);
}
// Return the logger
org.slf4j.Logger slf4jLogger = getLogger(loggerName);
Logger logger = (ch.qos.logback.classic.Logger) slf4jLogger;
logger.setAdditive(false);
logger.setLevel(logLevel);
logger.addAppender(rollingFileAppender);
return slf4jLogger;
}
}
#Test
void testExceptionWithLoggerWithRollingFileAppender() {
org.slf4j.Logger slf4jLogger = Logger.getLoggerWithRollingFileAppender
("testAppender",
"com.test7",
Level.DEBUG);
try {
FileInputStream fis=new FileInputStream("dummyfile.txt");
} catch (FileNotFoundException e) {
slf4jLogger.info("Exception is", e);
}
Logger logger = (ch.qos.logback.classic.Logger) slf4jLogger;
logger.debug("This is a log message");
try {
FileInputStream fis=new FileInputStream("dummyfile.txt");
} catch (FileNotFoundException e) {
logger.info("Exception is", e);
}
}
But the log file do not contain the complete stack trace of the exception. This is not useful from debugging point of view. What could be missing here?
{"timestamp":"2021-03-09 18:03:21.443","level":"INFO","thread":"main","logger":"com.test7","message":"Exception is","context":"default","exception":"java.io.FileNotFoundException: linessssss.txt (No such file or directory)\n"}
{"timestamp":"2021-03-09 18:03:21.476","level":"DEBUG","thread":"main","logger":"com.test7","message":"This is a log message","context":"default"}
{"timestamp":"2021-03-09 18:03:21.477","level":"INFO","thread":"main","logger":"com.test7","message":"Exception is","context":"default","exception":"java.io.FileNotFoundException: linessssss.txt (No such file or directory)\n"}
Looking at the source for JsonLayout it uses a ThrowableProxyConverter to control what ends up in the JSON - so even if you call setIncludeException (as recommended by responses to other similar questions) what it does will depend on which converter is selected (didn't spend long enough to see how). You probably want something like the RootCauseFirstThrowableProxyConverter or something like it being involved.
I found the fix for this. Added a CustomThrowableProxyConverter and handled the stack trace processing through it.
public class CustomThrowableProxyConverter extends ThrowableHandlingConverter {
public CustomThrowableProxyConverter() {
}
#Override
public String convert(ILoggingEvent event) {
StringBuilder sb = new StringBuilder();
IThrowableProxy itp = event.getThrowableProxy();
if (itp instanceof ThrowableProxy) {
ThrowableProxy tp = (ThrowableProxy)itp;
sb.append(tp.getClassName() + ": " + tp.getMessage());
for (StackTraceElementProxy element : tp.getStackTraceElementProxyArray()) {
sb.append("\t\n");
sb.append(element.getSTEAsString());
}
return sb.toString();
}
return "";
}
}
Then set this into jsonLayout instance.
JsonLayout jsonLayout = new JsonLayout();
jsonLayout.setThrowableProxyConverter(new CustomThrowableProxyConverter());
jsonEncoder.setLayout(jsonLayout);

Adding configurable FileHandler to java.util.logging.Logger

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.

java.util.logging: where does handler output go when running from Windows comand prompt?

Just getting to grips with this logging module.
When I run it in Eclipse the console output goes where I expect: console handler to console, file handler to file.
When I run from the command prompt I have no idea where the console output or the file output is going.
This is how I configure it:
Handler consoleHandler = null;
Handler fileHandler = null;
try {
logger.setUseParentHandlers(false);
// Creating consoleHandler and fileHandler
consoleHandler = new DualConsoleHandler();
// 1000 bytes max, 5 files max, append mode = true
fileHandler = new FileHandler("./output/logging/log%g.xml", 10000, 5, true);
// Assigning handlers to logger object
logger.addHandler(consoleHandler);
logger.addHandler(fileHandler);
// Setting levels to handlers and logger
consoleHandler.setLevel(Level.ALL);
fileHandler.setLevel(Level.ALL);
logger.setLevel(Level.ALL);
} catch (IOException exception) {
logger.log(Level.SEVERE, "Error occur in FileHandler.", exception);
}
The DualConsoleHandler looks like this (found in another SO question), the idea being to print INFO or lower to stdout, but WARNING or higher to stderr:
public class DualConsoleHandler extends StreamHandler {
private final ConsoleHandler stderrHandler = new ConsoleHandler();
public DualConsoleHandler() {
super(System.out, new SimpleFormatter());
}
#Override
public void publish(LogRecord record) {
if (record.getLevel().intValue() <= Level.INFO.intValue()) {
super.publish(record);
super.flush();
} else {
stderrHandler.publish(record);
stderrHandler.flush();
}
}
}
When I run from the command prompt I have no idea where the console output or the file output is going.
The console output goes to the console.
The file output goes to the path you gave it. i.e. ./output/logging/log%g.xml
Look in the directory under where you ran it for a output/logging directory.

Multiple logging files

I want to make multiple logging files (For each day), the code I have is this:
public class LoggingInkrement {
static final Logger logger = Logger.getLogger(LoggingInkrement.class.getName());
public static void main(String[] args) {
LogManager manager = LogManager.getLogManager();
try {
FileHandler fhandler = new FileHandler("C:/Users/Administrator/Desktop/Logging%g.txt", 1024*1024,10, true);
SimpleFormatter sformatter = new SimpleFormatter();
fhandler.setFormatter(sformatter);
logger.addHandler(fhandler);
} catch (IOException | SecurityException ex) {
logger.log(Level.SEVERE, ex.getMessage(), ex);
}
try {
((Object) null).toString();
}
catch ( Exception e ) {
logger.log( Level.SEVERE, "oh oh", e );
}
logger.info( "Hat funktioniert" );
}
}
my problem is that:
First run: logging0.txt is created
Second run: nothing happens
the changes are unnoticed (it stays on "Hat funktioniert") regardless of the changes i made in logger.info
but my desired outcome is:
You can make logging files with a set size
the files append and dont overwrite till they're full limit
The files are automatically deleted when outdated
logging0.txt
logging1.txt
logging2.txt
and so on
but I have no idea how I could do it
The FileHandler doesn't support daily file rotation. However, you can always trigger a rotation by creating a throw away FileHandler with the limit set to zero and append set to false.
if (isNewDay()) { //then rotate and close.
new FileHandler("C:/Users/Administrator/Desktop/Logging%g.txt",0,5, false).close();
}
//Open the log file.
FileHandler fhandler = new FileHandler("C:/Users/Administrator/Desktop/Logging%g.txt", 1024*1024,10, true);

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