Separate log files for different webapp in the same servlet container - java

Suppose I have 2 web applications (A & B) are hosted in the same weblogic server. I define a logging.properties file under /WEB-INF in each web application. The properties file defines a set of log levels for different classes and a file Handler which webapp A writes log to A.log, and webapp B writes log B.log. Standard java.util.logging package is used for logging.
A servlet context listener is defined such that the logging.properties file is read when the context initalized. The code fragment is as following:
InputStream is = sCtx.getResourceAsStream("/WEB-INF/logging.properties");
LogManager logManager = LogManager.getLogManager();
logManager.readConfiguration(is);
Logger rootLogger = Logger.getLogger("");
SysLogFileHandler fileHandler;
try
{
String location = SysLogFileHandler.getSysLogFilePath();
String fileName = SysLogFileHandler.LOG_FILE_PREFIX + "%d.log";
fileHandler = new SysLogFileHandler(location + File.separator + fileName, true);
fileHandler.setEncoding(Constant.DEFAULT_CHARSET);
fileHandler.setLevel(Level.ALL);
rootLogger.addHandler(fileHandler);
}
catch (Exception e)
{
e.printStackTrace();
}
I expect logs from webapp A write to A.log only, and webapp B write to B.log only. However, the outcome is not as expected. All logs from A & B are written to either 1 log file only, depends on which webapp starts at last.
May I ask what's the proper way of having separate log files in webapp environment?

If you are using the default LogManager class then calling readConfiguration will reset the logging configuration.
One solution is to use or build a new LogManager that is sensitive to the web classloader like org.apache.juli.ClassLoaderLogManager and start the server with that log manager.

Related

Retrieving slf4j log file in java

I'm making a project backup java application, that needs to create a mail report and send it every day. My idea was to generate the body message, and to attach to the email the application log.
I'm using slf4j logger, and I don't know ho to automatically retrieve the log file path using the logger.
Using plain log4j seems to be easy, but I can't get out trying to retrieve it from the slf4j logger. Anyone faced this problem?
At the end this is what I did, and it seems to work fine.
Any review or comment will be appreciated!
private File getLogFile(){
File file = null;
Appender rightAppender = null;
Enumeration<Appender> e = org.apache.log4j.Logger.getRootLogger().getAllAppenders();
while(e.hasMoreElements()){
Appender appender = (Appender)e.nextElement();
if(appender instanceof FileAppender){
rightAppender = appender;
}
}
if(null != rightAppender){
file = new File(((FileAppender)rightAppender).getFile());
}
return file;
}
There is no such thing as an SLF4J logger. SLF4J is a facade for another logging framework (log4j, commons-logging, java.util.Log, etc.). So an SLF4J logger is always just a wrapper around a logger in one of the supported frameworks. Look at how you configured the logger underneath.
So in your case, if you are using slf4j with log4j, consult your log4j.properties or log4j.xml as usual to find where your logs are. And if you haven't set up either of those files (or otherwise initialized log4j), you don't have logs.

File Handler doesn't start working until i have a file created

I have a loggers.config file that specifies a custom file handler to format log messages to a file. The handler wont work unless a create a default file with the specified file pattern. It then creates a new file with a 0 appended to it as specified by the pattern and starts logging to that. How can I get it to log to a file without specifying a default file first.
Here is the logging file:
handlers=com.daniel.logging.MyFileHandler
# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO
com.daniel.logging.MyFileHandler.level=INFO
# Naming style for the output file:
com.daniel.logging.MyFileHandler.pattern=daniel%g.log
# Limiting size of output file in bytes -defaulted to 1MB:
com.daniel.logging.MyFileHandler.limit=1MB
# Number of output files to cycle through, by appending an
# integer to the base file name:
com.daniel.logging.MyFileHandler.count=10
# Style of output (Simple or XML):
com.daniel.logging.MyFileHandler.formatter=com.daniel.logging.MyLogFormatter
# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
In the installer (this is an application logging properties file there was a file created there is a line specifying the logger.config file as logger.properties. A couple of lines later a file was created and the command CMD [#] >> default_file 2>&1. I am not sure what that line does (I'm quite new to bash)
Check that the user account the program is executing under has write permissions to the file path.
com.daniel.logging.MyFileHandler.limit=1MB
That should be changed to:
com.daniel.logging.MyFileHandler.limit=1048576
Your file pattern is using a relative path. You should use a absolute path or use the patterns for the home or temp directory. The FileHandler will fail to create a new log file if the directory path doesn't exist. The directories have to exist before the FileHandler will create the log file.
If you want to SimpleDateFormat patterns in the file pattern you can do something like:
public final class MyFileHandler extends FileHandler {
public MyFileHandler() throws IOException {
super(toPattern());
}
private static String toPattern() throws IOException {
String defaultPattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
String p = MyFileHandler.class.getName();
String v = LogManager.getLogManager().getProperty(p + ".pattern");
if (v == null) {
v = defaultPattern;
}
try {
return new SimpleDateFormat(v).format(new Date());
} catch (RuntimeException re) {
throw new IOException(v, re);
}
}
}
If you need rotation support you can reference PackageNameFileHandler as a starting point.

One Log file for a Java project

I wanted to keep track of a Java project main activity logs for debugging in one log file. I used the following approach to open the existed file, and append the new content, but it seems to add another file adding 1,2,3 in front of the name every time I run my program. Although the logfile.log keeps updated, and stored the desired content. But, I couldn't figure out why the other logfile.log.2, logfile.log.3 ... files created
LoggerFile lf = new LoggerFile();
final FileHandler fh = new FileHandler(System.getProperty("user.dir")+"/logfile.log", 1024 * 1024, 1, true);
Logger logger = lf.initialize(fh);
logger.info("log file message ... ");
fh.close();
Here is how the LoggerFile looks like:
public class LoggerFile {
public LoggerFile() {
}
public Logger initialize(FileHandler fh) throws SecurityException, IOException{
Logger logger = Logger.getLogger("MyLog");//Logger.getLogger(UpdateExposureScores.class.getName());
logger.addHandler(fh);
SimpleFormatter formatter = new SimpleFormatter();
fh.setFormatter(formatter);
return logger;
}
}
What seems to be wrong in the above code that it writes the log in several files? Thanks in advance.
I assume you are using java.util.logging. From the j.u.l.FileHandler documentation:
Normally the "%u" unique field is set to 0. However, if the FileHandler tries to open the filename and finds the file is currently in use by another process it will increment the unique number field and try again. This will be repeated until FileHandler finds a file name that is not currently in use. If there is a conflict and no "%u" field has been specified, it will be added at the end of the filename after a dot. (This will be after any automatically added generation number.)
Opening multiple file handlers within the same process also has the same effect. Add lines to log the result of java.lang.management.ManagementFactory.getRuntimeMXBean().getName() and java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime() so you can identify the JVMs that are creating the log files. You are either creating too many filehandlers pointing to the same location or you are starting multiple instances of your app.
You might want to look at the LogManager documentation which will explain alternate ways to configure your log settings.

Close log files

This project has been handed down to me, so I do not know much about it. There is a method where log (java.util.logging.Logger) is used and it creates two log files:
First file: fileName.log
Second file: fileName.log.lck
In Linux when I do lsof, I see these two files as open. How do I close these two files?
The reason why I want to close these files is this method is run multiple times a day and after couple of weeks the number of open files reaches a limit (around 1000), at which point our system stops working. When we restart our process ("Job Controller" which does the logging) the number of log files open goes to 0 and it works again.
This is what's been done to do logging
private static Logger log = Logger.getLogger(MyClass.class.getPackage().getName());
try{
log.logp(Level.SEVERE, "com.MyClass", "run", "It failed");
}
This is what I tried to do to close the files in the finally block but it didn't work
finally{
Handler[] handler = log.getHandlers();
for(Handler h: handler){
h.close();
}
}
I simply use:
LogManager.getLogManager().reset();
this will cancel all your log settings (log file path, file name pattern, formatter...) but it will stop using the logger close the lock file and release logger to
First solution
If you do not want to modify your code use: How to send java.util.logging to log4j?
java.util.logging.Logger to Logback using SLF4J?
I use log4j or logback. Both have Rolling File Appender (old files are removed) or Date/Time File appender.
Second solution
For logging the best usage is rolling file.
String filePattern = " fileName%.log";
int limit = 1000 * 1000; // 1 Mb
int numLogFiles = 3;
FileHandler fh = new FileHandler(filePattern, limit, numLogFiles);
// Add to logger
Logger logger = Logger.getLogger(MyClass.class.getPackage().getName());
logger.addHandler(fh);
I do not know if you can add globally file handler.

Renaming a Log4J log file during the program run

We're recently switched over to Log4J from JUL (java.util.Logging) because I wanted to add additional log files for different logging levels.
We have the option in the program to optionally append a value and a date/time stamp to the log file name at the (for all intents and purposes) end of the program's execution.
Because JUL seemed to open and close the file as needed to write to the file, it wasn't locked and we could simply use .renameTo() to change the filename.
Now, using Log4J, that file is left open and is locked, preventing us from renaming the file(s).
I can't decide the name of the file before I configure the logging because the property file containing the options for renaming is some time after the logging is needed (this is why we renamed it at the end of the program).
Do you have any suggestions as to how this can be achieved?
Would Logback and/or SLF4J help or hinder this?
I have sort of worked around the issue by using a system parameter in the log4j properties file, setting the property and then reloading the property file.
This allows me to change the name of the log file to something else at the end of the run, and then rename the old files.
It's inelegant, and very much of a kludge, so I would like to avoid this as it also leaves these temporary files around after the run.
One surefire approach would be to implement your own log4j Appender, perhaps based on the FileAppender ( http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/FileAppender.html ). Add your own specialized API to request the file be renamed.
I haven't tried this yet, but the tact I would take would be to use the underlying API setFile(...): http://www.jdocs.com/log4j/1.2.13/org/apache/log4j/FileAppender.html#M-setFile%28String,boolean,boolean,int%29
For example:
public class RenamingFileAppender extends FileAppender {
...
/** fix concurrency issue in stock implementation **/
public synchronized void setFile(String file) {
super.setFile(file);
}
public synchronized void renameFile(String newName) {
// whole method is synchronized to avoid losing log messages
// implementation can be smarter in having a short term queue
// for any messages that arrive while file is being renamed
File currentFile = new File(this.fileName);
File newFile = new File(newName);
// do checks to ensure current file exists, can be renamed etc.
...
// create a temp file to use while current log gets renamed
File tempFile = File.createTempFile("renaming-appender", ".log");
tempFile.deleteOnExit();
// tell underlying impl to use temporary file, so current file is flushed and closed
super.setFile(tempFile.getAbsolutePath(), false, this.bufferedIO, this.bufferSize);
// rename the recently closed file
currentFile.renameTo(newFile);
// now go back to the original log contents under the new name. Note append=true
super.setFile(newFile.getAbsolutePath(), true, this.bufferedIO, this.bufferSize);
}
Consider using a shutdown hooks, and renaming the file there...
http://onjava.com/pub/a/onjava/2003/03/26/shutdownhook.html
http://www.developerfeed.com/threads/tutorial/understanding-java-shutdown-hook
http://download.oracle.com/javase/1.4.2/docs/guide/lang/hook-design.html

Categories