while migrating a project from log4j to log4j2 I encountered a situation when a logger that logs events to a separate file (let's call it uu.log) has to be added at runtime - all other loggers are configured in properties file. The below code is almost doing the job - namely, uu.log contains events from all existing loggers, not just from new one. This is what I try so far: How can I fix the code below to achieve the desired state in simplest way?
public class MultipleLoggersExample {
public static void main(String[] args) throws InterruptedException {
// this logger is configured in properties file and is logging to own file
Logger aud = LogManager.getLogger("aud");
// class B is logging to separate file (logger also defined in properties)
B b = new B();
// below classes should log to common file NormalLog.log defined in properties
C c = new C();
D d = new D();
E e = new E();
addLoggerAtRuntime();
// this logger needs to log only its OWN messages to uu.log file
Logger runtimeLogger = LogManager.getLogger("my runtime logger");
int counter = 2;
while(true) {
if(counter % 2 == 0){
aud.info("message from \"aud\" logger no. "+ (counter-1));
} else{
b.logger.info("message from class B no. " + (counter-1));
}
c.logger.info("message from class C");
e.logger.info("message from class E");
if(counter % 4 == 0) {
runtimeLogger.info("message from logger added at runtime");
}
counter++;
Thread.sleep(5_000);
}
}
private static void addLoggerAtRuntime() {
final String fileName = "C:\\Users\\Damian\\Desktop\\uu.log";
LoggerContext lc = (LoggerContext) LogManager.getContext(false);
RollingFileAppender rfa = RollingFileAppender.newBuilder()
.withName("my runtime logger").withAppend(true)
.withFileName(fileName)
.withLayout(PatternLayout.newBuilder().withPattern("%-5p %d [%t] %C{2} - %m%n").build())
.withPolicy(TimeBasedTriggeringPolicy.newBuilder().withModulate(true).withInterval(2).build())
.withFilePattern(fileName + "." + "%d{yyyy-MM-dd-HH-mm}")
.setConfiguration(lc.getConfiguration()).build();
rfa.start();
lc.getConfiguration().addAppender(rfa);
lc.getRootLogger().addAppender(lc.getConfiguration().getAppender(rfa.getName()));
lc.updateLoggers();
}
}
Appender names have no relationship to logger names, so this:
.withName("my runtime logger")
is pointless. You might as well use "My appender" or something.
The problem is this:
lc.getRootLogger().addAppender(
You’re adding your Appender to the root logger, from which all other loggers inherit, so of course all loggers are using it. Try adding the Appender to just your logger:
lc.getLogger("my runtime logger").addAppender(
Related
In the application I'm currently working on, we have an in-house built logging setup where log messages from the business methods are buffered in a ThreadLocal variable. The actual logging is deferred until the end of the servlet invocation where there's a Filter that picks all messages up, concatenates them together, tags them with the information from the mapped diagnostic context and then saves them to a SQL database as a single log entry.
We want to get rid of this in-house logger because the code quality is not that good and it's getting a bit hard to maintain. Is the above use case possible to achieve with any publicly available Java logging framework? I looked around in Logback and Log4j documentation a bit but wasn't able to find anything similar.
You can use Logstash
Logstash is an open source, server-side data processing pipeline that
ingests data from a multitude of sources simultaneously, transforms
it, and then sends it to your favorite “stash.”
We are doing something similar with log4j: we're having an engine that processes requests in the background and records log4j messages into a temporary file; at the end of the processing, if something was logged, the content of the temporary file is sent by e-mail.
To start recording to the temp file:
String root = props
.getProperty("gina.nas.log-capture.root", "gina.nas");
String thresholdLogLevel = props.getProperty(
"gina.nas.log-capture.threshold", "WARN");
String fullLogLevel = props.getProperty("gina.nas.log-capture.level",
"INFO");
String pattern = props.getProperty("gina.nas.log-capture.pattern",
"%d * %-5p * %m [%c]%n");
includeFullLog = Boolean.parseBoolean(props.getProperty(
"gina.nas.log-capture.full-log", "true"));
this.layout = new PatternLayout(pattern);
this.logger = root == null ? Logger.getRootLogger() : Logger
.getLogger(root);
// Threshold log
this.thresholdLogFile = File.createTempFile("thcap", ".log");
thresholdLogAppender = initWriterAppender(thresholdLogFile, layout,
Level.toLevel(thresholdLogLevel));
logger.addAppender(thresholdLogAppender);
To stop recording:
logger.removeAppender(thresholdLogAppender);
thresholdLogAppender.close();
if (thresholdLogFile.isFile()) {
if (sendMail && thresholdLogFile.length() > 0) {
LOG.info("Error(s) detected, sending log by email...");
MailService mail = new MailService(props);
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("username", username);
vars.put("taskid", taskID);
vars.put("instance", instance);
vars.put("exception", exception);
vars.put("thresholdLogFile", getFileContent(thresholdLogFile));
mail.sendMessage(LOGCAPTURE_TEMPLATE, null, null, vars);
}
thresholdLogFile.delete();
}
Method initWriteAppender:
private WriterAppender initWriterAppender(File file, PatternLayout layout,
Level level) throws IOException {
OutputStream stream = new FileOutputStream(file);
boolean ok = false;
try {
Writer writer = new OutputStreamWriter(stream, "UTF-8");
try {
WriterAppender result = new WriterAppender(layout, writer);
ok = true;
result.setThreshold(level);
return result;
} finally {
if (!ok) {
writer.close();
}
}
} finally {
if (!ok) {
stream.close();
}
}
}
This question already has answers here:
How can I create 2 separate log files with one log4j config file?
(2 answers)
Closed 6 years ago.
I have 2 loggers in the same class, set up like this:
Logger logger = Logger.getLogger("MyLog");
FileHandler fh;
try {
// This block configure the logger with handler and formatter
fh = new FileHandler("C:/temp/test/MyLogFile.log");
logger.addHandler(fh);
SimpleFormatter formatter = new SimpleFormatter();
fh.setFormatter(formatter);
// the following statement is used to log any messages
logger.info("My first log");
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
The second logger is set up the same way, just as logger2 and fh2 and MyLogFile2.log.
My problem is that whenever any logger logs, both files are written into, even though I only call one logger e.g. logger2.log(Level.INFO, "msg2").
Why is that? Is it because both loggers are open at the same time? But I do not want to .close()/create them each time I use them. Any better solution?
You have to set another name for the instance here :
Logger logger = Logger.getLogger("MyLog");
Logger logger2 = Logger.getLogger("MyLog2");
Cause Logger.getLogger("MyLog") return an instance of the logger with name "MyLog" or create it if not exist
We have an application. In which we have a condition.Based on the condition, if it is true then we will write some log messages to one file else we will log the messages to another file.
And logging should be happened based on the condition and not based on log level.
How it is possible in dropwizard using yaml file?
This is supported out of the box. Here is my example:
server:
rootPath: /api/*
requestLog:
appenders: []
applicationConnectors:
- type: http
port: 9085
logging:
level: INFO
loggers:
"my-log-1":
level: DEBUG
additive: false
appenders:
- type: file
currentLogFilename: /home/artur/var/log/test1.log
archivedLogFilenamePattern: /home/artur/var/log/test1.log%d.log.gz
archivedFileCount: 5
logFormat: '[%level] %msg%n'
"my-log-2":
level: DEBUG
additive: false
appenders:
- type: file
currentLogFilename: /home/artur/var/log/test2.log
archivedLogFilenamePattern: /home/artur/var/log/test2.log%d.log.gz
archivedFileCount: 5
NOTE: You can not use tabs in the configuration.
This configuration creates 2 loggers. First is called "my-log-1", second is called "my-log-2".
You can now create these loggers in your java class, for example in my application:
public class Application extends io.dropwizard.Application<Configuration>{
private static final Logger log = Logger.getLogger("my-log-1");
private static final Logger log2 = Logger.getLogger("my-log-2");
#Override
public void run(Configuration configuration, Environment environment) throws Exception {
log.info("Test1"); // writes to first file
log2.info("Test2"); // logs to seconds file
}
public static void main(String[] args) throws Exception {
new Application().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/test.yaml");
}
}
Note the two loggers and their creation at the top of the file.
You can now use them like any logger. Add your condition and log away:
int random = new Random().nextInt();
if(random % 2 == 0) {
log.info("Test1"); // writes to first file
} else {
log2.info("Test2"); // logs to seconds file
}
I hope that answers your question,
thanks,
Artur
I am using JDK logging and I am trying to set log-rotation of log based on size.The configuration file logging.properties is set as shown below :
# Specify the handlers to create in the root logger
# (all loggers are children of the root logger)
# The following creates two handlers
handlers = java.util.logging.ConsoleHandler, java.util.logging.FileHandler
#handlers = java.util.logging.ConsoleHandler
#handlers = java.util.logging.FileHandler
# Set the default logging level for the root logger
.level = INFO
# Set the default logging level for new ConsoleHandler instances
java.util.logging.ConsoleHandler.level = INFO
# Set the default logging level for new FileHandler instances, INFO, FINEST...
java.util.logging.FileHandler.level = INFO
# Set the default formatter for new ConsoleHandler instances
#java.util.logging.ConsoleHandler.formatter = com.hazelcast.impl.LogFormatter
#java.util.logging.FileHandler.formatter = com.hazelcast.impl.LogFormatter
#java.util.logging.FileHandler.pattern = ./hazelcast%u.log
# Set the default logging level for the logger named com.hazelcast
com.hazelcast.level = INFO
# Limiting size of output file in bytes:
java.util.logging.FileHandler.limit=1024
# Number of output files to cycle through, by appending an
# integer to the base file name:
java.util.logging.FileHandler.count=10
I see the log been written into file but I dont see any effect on log-rotation based on size.I am using hazelcast API which implements the logger and I am trying to configure the properties through file.Help would be appreciated.
Thanks & Regards,
Rajeev
According to the Hazelcast Logging Configuration the default logging framework is JDK logging. So all of the configuration is setup in the logging.properties.
The size of the log file has to exceed the limit (1024) before the rotation. You have not set a pattern in your logging properties so the FileHandler will default to your home folder starting with 'java0.log.0'.
The following code is self-contained conversion of your logging properties that does not require any system properties to be set on launch.
public class JdkLogRotation {
private static final String LOGGER_NAME = "com.hazelcast";
private static final int LIMIT = 1024;
private static final int COUNT = 10;
private static final Logger logger = Logger.getLogger(LOGGER_NAME);
public static void main(String[] args) throws Exception {
Properties props = create();
read(LogManager.getLogManager(), props);
String msg = message();
for (int i = 0; i <= COUNT; ++i) {
logger.log(Level.INFO, msg);
}
/*
try (FileOutputStream out = new FileOutputStream(
new File(System.getProperty("user.home"),
"jdklogrotation.properties"))) {
props.store(out, "JDK log rotation");
out.flush();
}*/
}
private static String message() {
char[] c = new char[LIMIT + 1];
Arrays.fill(c, 'A');
return String.valueOf(c);
}
private static Properties create() {
Properties props = new Properties();
props.setProperty("handlers", "java.util.logging.ConsoleHandler, java.util.logging.FileHandler");
props.setProperty(".level", "INFO");
props.setProperty("java.util.logging.ConsoleHandler.level", "INFO");
props.setProperty("java.util.logging.FileHandler.level", "INFO");
props.setProperty("java.util.logging.FileHandler.limit", String.valueOf(LIMIT));
props.setProperty("java.util.logging.FileHandler.count", "10");
props.setProperty(LOGGER_NAME + ".level", "INFO");
return props;
}
private static void read(LogManager manager, Properties props) throws IOException {
final ByteArrayOutputStream out = new ByteArrayOutputStream(512);
props.store(out, "No comment");
manager.readConfiguration(new ByteArrayInputStream(out.toByteArray()));
}
}
The following log file names are created in your user home folder:
java0.log.0
java0.log.1
java0.log.2
java0.log.3
java0.log.4
java0.log.5
java0.log.6
java0.log.7
java0.log.8
java0.log.9
Assuming your logging.properties is loaded and nothing has reset your LogManager you should expect to see the same output.
Hazelcast logging (its more of a general logging problem) sometimes can be a big PITA.
First make sure you have logging configured through a system property, don't rely on the properties inside of the hazelcast.xml file because it could be that a logger is triggered before this file is loaded and then things get funky.
So something like -Dhazelcast.logging.type=jdk.
And remove the hazelcast.logging.type if you have that in your hazelcast.xml file.
Second step: Do you see hazelcast.log being created? Just to be sure that your log configuration is picked up, give it a completly different name just to be totally sure that it got picked up, e.g. monty-python.log. If you don't see this file appearing, then you know your log file isn't picked up.
If this file is picked up; then I would try to figure out why your config doesn't work. I have wasted too much time on configuration issues with logging that were not actually related to the actual configuration file.
We have a requirement in the application that the log file created shoud have a different name every time it executes (based on functionality + the current thread). I have seen the questions already posted in SO and tried to use the same method, but it is not working.
All the log statements are getting logged in "xyz.log" (specified in the log4j.properties). Only "org.apache.log4j.PatternLayout" gets logged in abc.log (specified in the program). Any pointers to solve this will be very helpful.
log4j.properties
log4j.rootLogger=DEBUG, MyLogger
log4j.appender.MyLogger=org.apache.log4j.FileAppender
log4j.appender.MyLogger.layout=org.apache.log4j.PatternLayout
log4j.appender.MyLogger = org.apache.log4j.DailyRollingFileAppender
log4j.additivity = false
log4j.appender.MyLogger.File=xyz.log
log4j.appender.MyLogger.MaxFileSize = 5MB
log4j.appender.MyLogger.MaxBackupIndex = 20
# Pattern to output the caller's file name and line number.
log4j.appender.MyLogger.layout.ConversionPattern=%d %5p [%t](%c:%L) - %m%n
Java code
logger = Logger.getLogger(MyLoggerUtil.class);
Layout layout = new PatternLayout("org.apache.log4j.PatternLayout");
logger.removeAllAppenders();
FileAppender appender = null;
try {
appender = new FileAppender(layout, "abc.log", false);
} catch (IOException e) {
e.printStackTrace();
}
logger.addAppender(appender);
logger.setLevel((Level) Level.DEBUG);
logger.debug("This is a debug test");
The constructor of PatternLayout expects a conversion pattern, like the one used in the lo4j.properties. So change the creation of the Layout to:
Layout layout = new PatternLayout("%d %5p [%t](%c:%L) - %m%n");
Pattern taken from your log4j.properties