My logging.properties custom formatter is not working - java

I'm actually adding java logging (can't use other framework) to my project. I build my app on a .war, and deployed it over Weblogic, the logger is working with my logging.properties config, except for the formatter i don't know why the app is ignoring it.
This is my class where i prepare the logger;
public class CtgLogger {
private static final String LOAD_ERROR = "Properties could not be loaded.";
private static final Map<String, Level> LEVEL_MAP;
private static final Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
static {
final InputStream inputStream = CtgLogger.class.getResourceAsStream("/logging.properties");
try {
LogManager.getLogManager().readConfiguration(inputStream);
} catch (Exception e) {
Logger.getAnonymousLogger().severe(LOAD_ERROR);
Logger.getAnonymousLogger().severe(e.getMessage());
}
// and I add the LEVEL_MAP to the logger...
And this is my properties...
handlers = java.util.logging.FileHandler
java.util.logging.FileHandler.pattern=logsfolder/CTGLOG_%g.log
java.util.logging.FileHandler.level=ALL
java.util.logging.FileHandler.limit=3000
java.util.logging.FileHandler.count=6
#java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
#If I use the SimpleFormatter, apps goes well with it format.
java.util.logging.FileHandler.formatter = com.package.my.log.JsonCustomFormatter
#If I use my custom formatter, the weblogic works with a XMLFormatter (default)
I know the .properties is working, because logger is working with the pattern, limit and count I setted.
PD: If i run my app with JUnit, logs are working with my custom formatter, but do not at weblogic! Don't know why!

Weblogic is going to have multiple class loaders. The standard LogManager can only see classes loaded via the system class loader. Check the Server Log for errors related to not finding your custom class. If that is the case, you have to move your formatter to the system classloader. Otherwise you have have to use code to install your formatter from your web app which is running in a child classloader.
There are also bugs in the LogManager.readConfiguration and alternative methods to use in JDK9 and later.

Using Eclipse and java standard logger may be painful. I found something to produce similar output to Log4J:
"%d{HH:mm:ss,SSS} %-5p %m (%F:%L) in %t%n" in Log4J : you can click on reference and you are there log was issued
21:36:37,9 INFO process model event Digpro2021a/digpro.Digpro(Digpro.java:358) in processModelEvent
21:36:37,9 INFO start polling Digpro2021a/digpro.Digpro(Digpro.java:398) in processEventAutoreload
21:36:37,9 INFO reload now Digpro2021a/digpro.Digpro(Digpro.java:370) in processModelEvent
public class Digpro {
protected static final Logger L = Logger.getLogger("Digpro");
//logger conf
static {
L.setLevel(Level.FINE);
Handler handler = Logger.getLogger("").getHandlers()[0];
handler.setLevel(Level.FINE); // Default console handler
handler.setFormatter(new Formatter() {
#Override
public String format(LogRecord r) {
Date d = new Date(r.getMillis());
String srcClassLong = r.getSourceClassName();
String[] aClass = srcClassLong.split("\\$")[0].split("\\.");
String srcClass = aClass[aClass.length - 1];
StackTraceElement elem = (new Throwable()).getStackTrace()[7];
int line = elem.getLineNumber();
String modulName = elem.getModuleName();
return String.format("%tH:%tM:%tS,%tl %.7s %s %s/%s(%s.java:%d) in %s\n", d, d, d, d, //
r.getLevel(), r.getMessage(), // LEVEL and message
modulName, srcClassLong, srcClass, line, r.getSourceMethodName()); //ref to click on
}
});
}
...
public static class TestDigpro extends Digpro {
//TESTING:
#Test
public void testLogFormat() {
L.info("poll info");
L.fine("got fine");
}
}
}
produses:
21:51:20,9 INFO poll info Digpro2021a/digpro.Digpro$TestDigpro(Digpro.java:723) in testLogFormat
21:51:20,9 FINE got fine Digpro2021a/digpro.Digpro$TestDigpro(Digpro.java:724) in testLogFormat

Related

How to separate a Log4j configuration?

I've got a program that has a log4j configuration written in XML. I am trying to modify the original application, and attempting to improve upon the previous logger config.
Since I cannot modify the xml file itself, I want to be able to generate a new configuration through the ConfigurationBuilderFactory, and use it alongside the other config. The only issue is that, I am unable to accomplish this. It does not seem to want to work with both.
What can I do?
The following is my code, greatly simplified:
/**
* Internally uses {#link org.apache.logging.log4j.Logger}
*/
public final class MyLogger {
private static final LoggerContext context;
static {
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
{
builder.setStatusLevel(WARN);
AppenderComponentBuilder console = builder.newAppender("SysOut", "Console");
console.addAttribute(...);
console.add(builder.newLayout(...).addAttribute(...));
builder.add(console);
// ... more configuration below
}
context = Configurator.initialize(builder.build()); // only works if no previous config exists, but will not replace an old config
}
}
// later on...
context.getLogger("MyLogger"); // uses the xml config, not the one written above
For creating loggers using the ConfigurationBuilder API programmatically you can refer below code.
It creates a logger in log4j2 environment added with some layout and appenders defined :
public class Log4j2Logger {
int counter = 0;
LoggerContext ctx;
Configuration config;
Logger logger;
String loggerName = "testLogger";
String appenderName = "myAppender";
static String testMessage = "This is a Test Message";
public void log() {
final ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
final LoggerComponentBuilder loggerComp = builder.newLogger(loggerName, Level.ALL).addAttribute("additivity",
false);
builder.add(loggerComp);
config = builder.build();
ctx = Configurator.initialize(config);
config = ctx.getConfiguration();
ctx.start(config);
ctx.updateLoggers(config);
// To create/add the logger of the configuration specified above we can use the
// getLogger(..) method
logger = ctx.getLogger(loggerName);
// Now we need to attach an appender to the logger so that our messages could be
// logged
logger.addAppender(addConsoleAppender(ctx.getConfiguration(), appenderName));
while (counter < 10) {
logger.error(testMessage + counter);
counter++;
}
}
private Appender addConsoleAppender(Configuration config, String appenderName) {
Appender consoleAppender = ConsoleAppender.newBuilder().setConfiguration(config).setName(appenderName)
.withImmediateFlush(true).build();
consoleAppender.start();
return consoleAppender;
}
}
And for testing, you can have following in any test class:
Log4j2Logger testLogger = new Log4j2Logger();
testLogger.log();
This API helps you to handle logs in a powerful way.
You can :
Create multiple loggers with your configuration
Add multiple Appenders to it.
Configure them also.
Remove logger when the usage is over.
Create Asynchronous Loggers also.
PS : I have used log4j2 version 2.12.1.
I think you can create your own log4j.xml. You have to ensure that your XML will be loaded in your program. So just define the resource containing your XML in the Java Classpath before the resource containing the other XML.

sl4j and logback - Is it possible to programmatically set the logging level for package?

I'm able to programmatically set the logging level on the application with the following code, but is it also possible to do this on a package level, say com.somepackage.* where I want the level to be only ERROR rather than DEBUG or INFO on said package?
// Sets the logging level to INFO
LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory();
Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.setLevel(Level.INFO);
But I can't seem to find a way to set it on a package level...
You should set the package name as logger-name
// Sets the package level to INFO
LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory();
Logger rootLogger = loggerContext.getLogger("com.somepackage");
rootLogger.setLevel(Level.INFO);
You should be able to get the package name more elegant, but this is basically it.
This follows the tree like hierarchy for the Logger Context:
Logger Context
You can do it by using logback..
Logger LOG = (Logger) org.slf4j.LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
LOG.setLevel(Level.WARN);
This solved my problem.
You can get the SLF4J logger for the package and cast it to Logback logger. Less code that #dimitri-dewaele's.
((Logger) LoggerFactory.getLogger("com.somepackage")).setLevel(DEBUG)
#nandu-prajapati's approach is similar, except that it sets the root logger level, not the one desired.
In case you don't want to change the logback-classic to a compile-time dependency, here is some code that uses reflection to set this level assuming that logback is used as the slf4j runtime-binding. Here AbcClass is the class whose logger level you want to change to TRACE:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SomeClass {
private static final Logger logger = LoggerFactory.getLogger(SomeClass .class);
public static void beforeEachTest() throws Exception {
final Logger loggerInterface = LoggerFactory.getLogger(AbcClass.class);
String loggerLevelNew = "TRACE";
if (!loggerInterface.isTraceEnabled()) {
try {
Class<?> levelLogBackClass = Class.forName("ch.qos.logback.classic.Level");
Method toLevelMethod = levelLogBackClass.getDeclaredMethod("toLevel", String.class);
Object traceLvel = toLevelMethod.invoke(null, loggerLevelNew);
Method loggerSetLevelMethod= loggerInterface.getClass().getDeclaredMethod("setLevel", levelLogBackClass);
loggerSetLevelMethod.invoke(loggerInterface, traceLvel);
} catch (Exception e) {
logger.warn("Problem setting logger level to:{}, msg: {}", loggerLevelNew, e.getMessage());
throw e;
}
}
}
}
Something similar can be done for log4j and JDK logging to make this (kind-of) library agnostic

Java Logger with Log4j

Am processing files using java , there are 2 or 3 Java components to process files.My requirement is for file must have a log file and its processing details will be logged in relevant log file.
Problem is for single file it works well but with multiple file am getting problem.
When multiple files are getting processed logger is logging log detail of file1.txt logs to file2.log instead of file1.log...
public Class FileProcessComponent1
{
public void process(String fileName)
{
Logger Log = Logg.getLogger1(fileName,this.getClass());
log.info("file1 logging");
}
}
public Class FileProcessComponent2
{
public void process(String fileName)
{
Logger Log = Logg.getLogger1(fileName,this.getClass());
log.info("file1 logging");
}
}
public Class Logg
{
public static Logger getLogger1(String fileName,Class clazz) throws Exception
{
if(fileName == null || "".equals(fileName.trim()))
throw new Exception("File Name or Map for File Name is Null");
fileName = "/home/logs/"+fileName+".log";
Logger logger = Logger.getLogger(clazz.getCanonicalName()+":"+System.nanoTime());
logger.setAdditivity(false);
FileAppender appender = new DailyRollingFileAppender(new PatternLayout("%d{ISO8601}\t%p\t%c\t%m%n"), fileName, "'.'yyyy-MM-dd");
logger.addAppender(appender);
logger.setLevel(Level.DEBUG);
return logger;
}
}
I think you want to create a logger pointing to a unqiue file for each files comes for processing.
Try this,
a) At the point where you start processing a new file, Create a new Logger with file name as Loggername.
b) Create the Appender with filename.log and assign the appender to the logger.
Now, when you try to get the logger, always use the filename in getLogger().
Once you are done with processing a file, remove the logger/appender. (this is very important)

How to configure log4j fileappender for all classes?

In my application I read in an inputfile (e.g. myFile1.txt) and create an outputfile with the same name (e.g. myFile2log). Point is that the inputfile will be read within the java application and not given as command line parameter. Therefore it is required to set an appender in the application, e.g.
public class Example {
private static final Logger LOG = Logger.getLogger(Example.class);
public Example() throws IOException {
FileAppender appender = new DailyRollingFileAppender(new PatternLayout(
PatternLayout.DEFAULT_CONVERSION_PATTERN), "yourfilename.log",
"'.'yyyy-MM-dd");
LOG.addAppender(appender);
LOG.setLevel((Level) Level.DEBUG);
LOG.debug("blabla");
new RandomClass();
}
public static void main(String[] args) throws IOException {
new Example();
}
}
public class RandomClass {
private static final Logger LOG = Logger.getLogger(RandomClass.class);
public RandomClass() {
LOG.debug("hello Randomclass");
}
}
And here is the problem: if I do the above the custom file appender only works for the specific class where the fileappender was defined, but not on any other class. Therefore the output of the "RandomClass" will not be written into this logfile, but which is what is required. How can I achieve that?
If you would like to set the appender dynamically you should try setting the new appender to the root logger:
Logger logger = Logger.getRootLogger();
FileAppender appender = new DailyRollingFileAppender(new PatternLayout(
PatternLayout.DEFAULT_CONVERSION_PATTERN), "yourfilename.log", "'.'yyyy-MM-dd");
logger.addAppender(appender)
You can use log4j.properties file in your class path then simply you can initilize logger in all the classes.
private static final Logger LOG = Logger.getLogger(Example.class);
and Appender all not required in constructor. your property file should contain
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=yourfilename.log
log4j.appender.R.MaxFileSize=2048KB

generating logs of simple java class with log4j api

I have one query is that if I write a simple Java class and I want to see the logs that are executed at the behind, such that are executed at the backend by the JVM. I have log4j JAR with me. Could you please give me advice to how to achieve this?
I would to see the same logs that are generated in case of a WebApp, so I can reuse it for a Java App.
try looking at log4j api/demo here http://www.vaannila.com/log4j/log4j-tutorial/log4j-tutorial.html
you can generate log for any class and any application afaik using log4j , which needs not necessarily be a web app
Here is a very simple example that you should be able to adapt to what you are trying to achieve.
Step 1:Import log4j jar file in your project
Step 2:-Create a Log java file
public Log(Class className) {
logger = Logger.getLogger(className.getClass());
logger.getClass();
final String LOG_PROPERTIES_FILE_NAME = "log4j.properties";
props = loadLogFile(LOG_PROPERTIES_FILE_NAME, className);
//props =getPropsFile(LOG_PROPERTIES_FILE_NAME, className);
PropertyConfigurator.configure(props);
}
public static Properties loadLogFile(String propertyFile, Class className) {
//String filePath="";
try {
Properties ps = null;
ResourceBundle bundle = ResourceBundle
.getBundle("resource.DbProperties");
String logPath = bundle.getString("logs.path");
File filePath = new File(logPath);
if (!filePath.exists()) {
filePath.mkdirs();
}
props.clone();
props = getPropsFile(propertyFile, className);
props.setProperty("info_file_path", filePath.toString());
// PropertyConfigurator.configure(props);
} catch (Exception e) {
e.printStackTrace();
}
return props;
}
public void info(String message) {
logger.info(message);
}
public void warn(String message) {
logger.warn(message);
}
public void error(String message) {
logger.error(message);
}
public void fatal(String message) {
logger.fatal(message);
}
public void printStackTrace(Throwable ex) {
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw));
logger.error("\n" + sw.toString());
}
log4j.properties
log4j.rootCategory=LOGFILE
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=${info_file_path}\\info_Pension_application.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.Threshold=INFO
log4j.appender.LOGFILE.Threshold=DEBUG
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
If your "simple Java class" happens to be in a Java EE container (like a .jsp or servlet in Tomcat, JBoss or WebSphere), then all you need to do is:
1) import org.apache.log4j.Logger;
2) declare a static instance in your class:
EXAMPLE: `static Logger logger = Logger.getLogger ("MyApp");`
3) Use your logger
EXAMPLE: `logger.info ("something interesting happened");`
4) If you include log4j.java and your own log4j.properties in your CLASSPATH, then you don't need to do anything else.
Here's a bit more info on using log4j with Tomcat or app servers:
http://tomcat.apache.org/tomcat-5.5-doc/logging.html
http://logging.apache.org/log4j/1.2/manual.html
http://www.tutorialspoint.com/log4j/log4j_quick_guide.htm
Here's a very simple example program that "does logging", from the above tutorialspoint.com link:
import org.apache.log4j.Logger;
import java.io.*;
import java.sql.SQLException;
import java.util.*;
public class log4jExample{
/* Get actual class name to be printed on */
static Logger log = Logger.getLogger(
log4jExample.class.getName());
public static void main(String[] args)
throws IOException,SQLException{
log.debug("Hello this is an debug message");
log.info("Hello this is an info message");
}
}

Categories

Resources