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
Related
I am creating a log4j2 logger programmatically and adding appenders to it.
I want the logger to write the messages in two different locations/files according to some parameter/criteria.
For this I found that RoutingAppender can be good option to route the messages in different locations.
And for the criteria with the help of which I want to route messages, I am using Marker as used in How to create multiple log file programatically in log4j2?
But I am enable to manage it properly.Below is my code snippet for reference :
public class Log4j2TestApplication {
private static final Marker MARKER1 = MarkerManager.getMarker("MARKER1");
private static final Marker MARKER2 = MarkerManager.getMarker("MARKER2");
public static void main(String[] args) {
String loggerName = "demoLogger";
final ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
final LoggerComponentBuilder loggerComp = builder.newLogger(loggerName, Level.ALL).addAttribute("additivity",
false);
builder.add(loggerComp);
Configuration configuration = builder.build();
LoggerContext ctx = Configurator.initialize(builder.build());
ctx.start(configuration);
ctx.updateLoggers(configuration);
Logger logger = ctx.getLogger(loggerName);
Appender csvAppender = createCsvAppender(configuration);
Appender textAppender = createTextAppender(configuration);
csvAppender.start();
textAppender.start();
logger.addAppender(csvAppender);
logger.addAppender(textAppender);
Appender routingAppender = createRoutingAppender(configuration, textAppender, csvAppender);
routingAppender.start();
logger.addAppender(routingAppender);
logger.error(MARKER1, "the text message", "testing parameter");
logger.error(MARKER2, "the csv message", "testing parameter");
csvAppender.stop();
textAppender.stop();
routingAppender.stop();
}
private static Appender createCsvAppender(final Configuration config) {
return RollingFileAppender.newBuilder().setConfiguration(config).setName("csvAppender")
.withFileName("TestFile.csv").withFilePattern("TestFile.csv")
.withPolicy(SizeBasedTriggeringPolicy.createPolicy("100M"))
.withStrategy(DefaultRolloverStrategy.newBuilder().withConfig(config).build()).withImmediateFlush(true)
.setFilter(ThresholdFilter.createFilter(Level.ALL, Result.ACCEPT, Result.DENY)).setLayout(getCsvLayout(config))
.build();
}
private static Layout<String> getCsvLayout(final Configuration config) {
return new CsvParameterLayout(config, StandardCharsets.UTF_8, CSVFormat.DEFAULT.withDelimiter(','),
"column1;coloumn2\n", null);
}
private static Appender createTextAppender(final Configuration config) {
return RollingFileAppender.newBuilder().setConfiguration(config).setName("txtAppender")
.withFileName("TestFile.txt").withFilePattern("TestFile.txt")
.withPolicy(SizeBasedTriggeringPolicy.createPolicy("100M"))
.withStrategy(DefaultRolloverStrategy.newBuilder().withConfig(config).build()).withImmediateFlush(true)
.setFilter(ThresholdFilter.createFilter(Level.ALL, Result.ACCEPT, Result.DENY)).setLayout(getTextLayout(config, "header\n"))
.build();
}
private static Layout<String> getTextLayout(final Configuration config, final String header) {
return PatternLayout.newBuilder().withConfiguration(config).withCharset(StandardCharsets.UTF_8)
.withPattern("[%d][%-5.-5p]").withHeader(header).build();
}
private static Appender createRoutingAppender(final Configuration config, Appender appender1, Appender appender2) {
Route[] routeArray = new Route[2];
routeArray[0] = Route.createRoute(appender1.getName(), "MARKER1", null);
routeArray[1] = Route.createRoute(appender2.getName(), "MARKER2", null);
Routes routes = Routes.newBuilder().withRoutes(routeArray).withPattern("marker").build();
Appender routingAppender = RoutingAppender.newBuilder().setName("routingAppender").setConfiguration(config)
.withRoutes(routes).build();
return routingAppender;
}
}
I have referenced below links also but I could not find the accurate way of RoutingAppender programmatically.
Is there a way to Route logs based on Marker with the RoutingAppender in Log4j2
Wildcard pattern for RoutingAppender of Log4j2
How to create multiple log file programatically in log4j2?
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.
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
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)
Is there any way to specify Log4J 2.x log4j2.xml file location manually (like DOMConfigurator in Log4J 1.x), without messing with classpath and system properties?
You could use the static method #initialize(String contextName, ClassLoader loader, String configLocation) (see source here) in org.apache.logging.log4j.core.config.Configurator.
(You can pass null for the class loader.)
Be aware that this class is not part of the public API so your code may break with any minor release.
For completeness, you can also specify the location of the configuration file with this system property:
-Dlog4j.configurationFile=path/to/log4j2.xml
If you are using log4j2 and properties are in defined in log4j2.properties file then use this.
-Dlog4j2.configurationFile=file:/home/atul/log4j2.properties
For log4j version 2.12.1, you can find how to reconfigure log4j2 here.
Below is an example
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
File file = new File("C:\\Path for Windows OS\\yourConfig.xml");
LoggerContext context = (LoggerContext) LogManager.getContext(false);
context.setConfigLocation(file.toURI());
Logger log = LogManager.getLogger(YourClass.class);
It seems to me that way of configuring log4j2 is changing with new releases, so you should be aware of that.
In Windows, be aware that you need to use a URI with the log4j.configurationFile property
-Dlog4j.configurationFile=file://C:\path\to\log4j2.xml
Using the LoggerContext allows to setConfigLocation.
File f = new File(this.logConfigFile);
URI fc = f.toURI();
System.out.println("Loading logging config file: " + fc);
Logger l = (Logger) LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
l.getContext().setConfigLocation(fc);
or alternatively
LoggerContext.getContext().setConfigLocation(java.net.URI);
You can initialize like below as well
ConfigurationSource source = new ConfigurationSource(new FileInputStream(log4j file Path));
XmlConfiguration xmlConfig = new XmlConfiguration(source);
Logger logger = (Logger) LogManager.getLogger();
logger.getContext().start(xmlConfig);
In each class you can get logger instance as below
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
private final Logger logger = LogManager.getLogger(ABC.class);
Step: 1 - Get ready with your log4J.xml file with the appender details (Mostly under the resource folder)
Step: 2 - Following code should be added to the configuration class (In the previous log4J we had PropertyConfigurator and now we need to go with LoggerContext)
String log4JFilePath = "file path of your log4J.xml file";
LoggerContext loggerContext = (LoggerContext)LoggerManager.getContext(false);
File file = new File(log4JFilePath);
loggerContext.setConfigLocation(file.toURI());
Step: 3 - Add the following line to utilise the logger in any classes
private static final Logger logger = LogManager.getLogger(yourClassName.class);
logger.info("log here");
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;
public class Foo {
public static void main(String[] args) {
Configurator.initialize(null, "src/main/config/log4j2.xml"); //path specify
Logger logger = LogManager.getLogger(APITestToolMain.class);
logger.info("working");
}
}
resource: https://www.baeldung.com/spring-boot-change-log4j2-location
void initializeLogger()
{
try
{
String basepath=checkpath();
File f = new File(basepath+"\\config\\log4j2.properties");
URI fc = f.toURI();
LoggerContext context = (LoggerContext) LogManager.getContext(false);
context.setConfigLocation(f.toURI());
}
catch (Exception e)
{
errorlog="Unable to load logging property:";
System.out.println(errorlog+": "+e.getMessage());
}
}
This is the way I initialize my log4j2 properties file from a different location, so what I simply do is to call the initializeLogger() method in my main method.
And it works perfectly.
Perhaps you need to see what the checkpath() blocks looks like, I added the function below.
String checkpath()
{
String parentpath="";
try
{
URL url=getClass().getProtectionDomain().getCodeSource().getLocation();
File f=new File(url.toURI());
parentpath=f.getParent();
}
catch(Exception ex)
{
//logger.error("unable to retrieve application parent path");
}
return parentpath;
}