JDK log rotation - java

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.

Related

Log4j2 add logger at runtime - what's wrong with this code?

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(

Group Java log entries per servlet invocation

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

log4j and YAML configuration with multiple appenders [duplicate]

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

changing log4j file name programatically in osgi maven bundle not working

Im developing a maven-osgi bundle and deploying in karaf.. In that, a piece of code, should get .cfg files from the karaf/etc and im programatically changing them at runtime.. writeTrace() is invoked within 'for loop' from another class. So that I can create different files and corresponding logging should go in to that file.
public void writeLog(int i,String HostName) {
StringBuilder sb = new StringBuilder();
sb.append("\n HEADER : \n");
....
String str = sb.toString();
String logfile = ("/home/Dev/" + HostName + i);
logger = LoggerFactory.getLogger("TracerLog");
updateLog4jConfiguration(logfile);
logger.error(str + i);}
public void updateLog4jConfiguration(String logFile) {
Properties props = new Properties();
try {
// InputStream configStream = getClass().getResourceAsStream(
// "/home/Temp-files/NumberGenerator/src/main/java/log4j.properties");
InputStream configStream = new FileInputStream("etc/org.ops4j.pax.logging.cfg");
props.load(configStream);
System.out.println(configStream);
configStream.close();
} catch (IOException e) {
System.out.println("Error: Cannot laod configuration file ");
}
props.setProperty("log4j.appender.Tracer.File", logFile);
LogManager.resetConfiguration();
PropertyConfigurator.configure(props);
}
and I am able to see new files created with hostname such as (hostname_1 , hostname_2, etc..) but logging happens only at actual appender configured at karaf/etc... thaat is log.txt..
log4j.logger.TracerLog=TRACE,Tracer
log4j.appender.Tracer=org.apache.log4j.RollingFileAppender
log4j.appender.Tracer.MaxBackupIndex=10
log4j.appender.Tracer.MaxFileSize=500KB
log4j.appender.Tracer.File=/home/Dev/log.txt
I got struck in this error.. Dont know whether it has to do something with the karaf or problem with code..???
Why aren't you just using the ConfigurationAdminService for this, instead of altering the file?
Just reference the configuration admin service from the registry and take the configuration with the PID org.ops4j.pax.logging.
With this approach you will have all configuration properties available for your proposal and it is in your code to alter this. It's also possible for you to add new configuration entries. In the end the combination of ConfigurationAdminService and the felix FileInstaller will even persist your changes back to the configuration file.
Btw. did you know that there is a shell command for configuring configurations, so actually also to alter the configuration for the org.ops4j.pax.logging service?
Just do a:
config:list
to retrieve all configurations available
and a
config:list "(service=org.ops4j.pax.logging)"
to retrieve just this information.

best ways to dynamically load config in java?

i am designing a web service in java where i need to do a sort of AB testing with requests in java.
Basically I'm looking for ways to easily configure parameters which will be dynamically loaded by a request handler to determine the code path based on config values.
for example, let's say i need to get some data either from an external web service or from local DB. i want to have a way to configure parameters(criteria in this context) so that it would determine whether to go fetch data from external web service or from local DB.
if i go with a key-value pair config system above example might produce something like this.
locale=us
percentage=30
browser=firefox
which would mean that i would be fetching data from local DB for 30% of requests from US users whose user-agent is firefox. and i would like this config system to be dynamic so that server does not need to be restarted.
sorry about very high level description but any insights/leads would be appreciated.
if this is a topic that is beaten to death in the past, please kindly let me know the links.
I've used this in the past. It is the most common way in java to achieve what you are asking using java.util.Properties:
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
/**
* Class that loads settings from config.properties
* #author Brant Unger
*
*/
public class Settings
{
public static boolean DEBUG = false;
/**
* Load settings from config.properties
*/
public static void load()
{
try
{
Properties appSettings = new Properties();
FileInputStream fis = new FileInputStream("config.properties"); //put config properties file to buffer
appSettings.load(fis); //load config.properties file
//This is where you add your config variables:
DEBUG = Boolean.parseBoolean((String)appSettings.get("DEBUG"));
fis.close();
if(DEBUG) System.out.println("Settings file successfuly loaded");
}
catch(IOException e)
{
System.out.println("Could not load settings file.");
System.out.println(e.getMessage());
}
}
}
Then in your main class you can do:
Settings.load(); //Load settings
Then you can check the values of those variables in every other class like:
if (Settings.DEBUG) System.out.println("The debug value is true");
I'm not sure if it helps you, but i usually put config data in some editable file:
file params.ini
vertices 3
n_poly 80
mutation_rate 0.0001f
photo_interval_sec 60
target_file monalisa.jpeg
randomize_start true
min_alpha 20
max_alpha 90
I use this class to load it:
import java.io.*;
import java.util.HashMap;
import java.util.Scanner;
public class Params
{
static int VERTICES = 0;
static int N_POLY = 0;
static float MUTATION_RATE = 0.0f;
static int PHOTO_INTERVAL_SEC = 0;
static String TARGET_FILE;
static boolean RANDOMIZE_START = false;
static int MIN_ALPHA = 0;
static int MAX_ALPHA = 0;
public Params()
{
Scanner scanner = new Scanner(this.getClass().getResourceAsStream("params.ini"));
HashMap<String, String> map = new HashMap<String, String>();
while (scanner.hasNext())
{
map.put(scanner.next(), scanner.next());
}
TARGET_FILE = map.get("target_file");
VERTICES = Integer.parseInt(map.get("vertices"));
N_POLY = Integer.parseInt(map.get("n_poly"));
MUTATION_RATE = Float.parseFloat(map.get("mutation_rate"));
PHOTO_INTERVAL_SEC = Integer.parseInt(map.get("photo_interval_sec"));
RANDOMIZE_START = Boolean.parseBoolean(map.get("randomize_start"));
MIN_ALPHA = Integer.parseInt(map.get("min_alpha"));
MAX_ALPHA = Integer.parseInt(map.get("max_alpha"));
}
}
Then just load and read:
// call this to load/reload the file
new Params();
// then just read
int vertices = Params.VERTICES;
Hope it helps!
I've just released an open source solution to this using .yml files, which can be loaded into POJOs, thus creating a nicer solution than a map of properties. In addition, the solution interpolates system properties and environment variables into placeholders:
url: ${database.url}
password: ${database.password}
concurrency: 12
This can be loaded into a Map or better still a Java POJO.
See https://github.com/webcompere/lightweight-config

Categories

Resources