I've an issue with a project I try to deliver using the one-jar packager to simplify the deployment process.
Without the packaging, everything works fine and the logging configuration is perfectly loaded, but within the packaging, only part of the configuration is appied.
So, here is the logging.properties I use:
handlers= java.util.logging.ConsoleHandler, java.util.logging.FileHandler
.level= INFO
java.util.logging.FileHandler.pattern = C:\\MyPath\\logging.csv
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = my.package.logging.Formatter
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = my.package.logging.Formatter
And In my main class, here is how I load it:
public class MainClass {
public static void main(final String[] args) {
try {
LogManager.getLogManager().readConfiguration(
new MainClass().getClass().getResourceAsStream("logging.properties"));
// main process goes here.
} catch(Exception e) {
// Exception handling
}
}
}
The log level as well as the FileHandler pattern are well understood because the logging ends up in the correct file, but as row XML output, which makes me think that the formatter is not loaded as it normally outputs a CSV format.
Could it be related to a classpath issue? Does anyone knows how to handle this?
It could be in your jars you have more than one logging.properties file, with similar but slightly different settings. When you combine them with one-jar the order changes and one of them gets hidden. Do a "jar -tf *.jar |grep logging.properties" and see what you see.
If that doesn't work can you try unjarring the onejar result to a directory structure, and then running with the directory on the classpath instead of the jar? That will let you see if it is something to do with the jar, and actually inspect the logging.properties you have in the onejar, and see if it matches what you expect.
Use LogManager.getLogManager().readConfiguration(LogManager.class.getResourceAsStream("/logging.debug.properties"));
(note the extra slash).
Related
I am using Google Reflections 0.9.10 to scan an external jar file (using a URLClassLoader) and my main class of my application, which is called Volts of Doom, (using ClassName.getClassLoader())
I am searching for a custom annotation, #Mod, so I do not want to try to search for it in files such as:
could not scan file META-INF/MANIFEST.MF in url file:/C:/Users/admin/.m2/repository/com/google/code/gson/gson/2.3.1/gson-2.3.1.jar because it is slowing down the loading cycle a lot
I assume that this is being scanned because it is on my Maven classpath, as it should be because I use it as a dependancy.
In this case, they are not java files, but resources, so they do not impact the speed, as they are not being scanned (as it says), but to me, this means that it is still searching in those files. If it does find java files there, those will impact the loading speed.
How do I prevent Reflections from scanning these external jar files?
Thus far, I have tried using:
new FilterBuilder().excludePackage("java")
.excludePackage("org.reflections")
.excludePackage("com.google")
.excludePackage("com.sun")
...however this does not solve the issue.
A sample of my logs:
11:58:09.005 [main] DEBUG org.reflections.Reflections - going to scan these urls:
file:/C:/Users/admin/AppData/Roaming/zapbyte/voltsofdoom/resources/mods/test3.jar
file:/C:/Users/admin/.m2/repository/org/lwjgl/lwjgl/3.2.3/lwjgl-3.2.3-natives-windows.jar
file:/C:/Program%20Files/Java/jdk1.8.0_241/jre/lib/ext/zipfs.jar
file:/C:/Users/admin/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
file:/C:/Program%20Files/Java/jdk1.8.0_241/jre/lib/ext/jfxrt.jar
file:/C:/Users/admin/OneDrive/Desktop/Java/VoltsOfDoom/voltsofdoomparent/voltsofdoom/target/classes/
file:/C:/Program%20Files/Java/jdk1.8.0_241/jre/lib/ext/nashorn.jar
file:/C:/Users/admin/.m2/repository/org/lwjgl/lwjgl-openal/3.2.3/lwjgl-openal-3.2.3.jar
file:/C:/Users/admin/.m2/repository/org/lwjgl/lwjgl-glfw/3.2.3/lwjgl-glfw-3.2.3-natives-windows.jar
... etc for more files
11:58:09.065 [main] DEBUG org.reflections.Reflections - could not scan file .classpath in url file:/C:/Users/admin/AppData/Roaming/zapbyte/voltsofdoom/resources/mods/test3.jar with scanner SubTypesScanner
11:58:09.065 [main] DEBUG org.reflections.Reflections - could not scan file .classpath in url file:/C:/Users/admin/AppData/Roaming/zapbyte/voltsofdoom/resources/mods/test3.jar with scanner MethodAnnotationsScanner
^^ This is good: they are in the jar I want to scan
11:58:09.080 [main] DEBUG org.reflections.Reflections - could not scan file META-INF/MANIFEST.MF in url file:/C:/Users/admin/.m2/repository/org/lwjgl/lwjgl/3.2.3/lwjgl-3.2.3-natives-windows.jar with scanner TypeAnnotationsScanner
11:58:09.080 [main] DEBUG org.reflections.Reflections - could not scan file META-INF/MANIFEST.MF in url file:/C:/Users/admin/.m2/repository/org/lwjgl/lwjgl/3.2.3/lwjgl-3.2.3-natives-windows.jar with scanner SubTypesScanner
^^ This is not, because they are extraneous.
I have patched the issue with this FilterBuilder:
public static FilterBuilder defaultFilterBuilder() {
return new FilterBuilder()//
// Exclude sources
.excludePackage("java")//
.excludePackage("lib/").exclude("lib/")//
.excludePackage("lib.").exclude("lib.")//
.excludePackage("resources/").exclude("resources/")//
.excludePackage("resources.").exclude("resources.")//
.excludePackage("META-INF/").exclude("META-INF/")//
.excludePackage("META-INF.").exclude("META-INF.")//
.excludePackage("org.reflections").excludePackage("org/reflections")//
.excludePackage("com.google").excludePackage("com/google")//
.excludePackage("org.lwjgl").excludePackage("org/lwjgl")//
.excludePackage("ch.qos").excludePackage("ch/qos")//
.excludePackage("edu.umd").excludePackage("edu/umd")//
.excludePackage("jdk.nashorn").excludePackage("jdk/nashorn")//
.excludePackage("jdk.internal").excludePackage("jdk/internal")//
.excludePackage("windows.x64").excludePackage("windows/x64")//
.excludePackage("net.jcip").excludePackage("net/jcip")//
.excludePackage("com.sun").excludePackage("com/sun")//
.excludePackage("sun.text").excludePackage("sun/text")//
// Exclude resources
.excludePackage("image/").exclude("image/")//
.excludePackage("image.").exclude("image.")//
.excludePackage("font/").exclude("font/")//
.excludePackage("font.").exclude("font.")//
;
}
Which excludes every package which was logged as slowing the process, and has cut the average log size during loading from 2.5MB to 91KB.
According to this comment on the org.reflections GitHub repository you need to exclude both “org.google” and “org/google” to catch everything.
Let's say there is a jar main.jar which depends on two other jars - dep1.jar and dep2.jar. Both dependencies are in a classpath in MANIFEST.MF of main.jar. Each of dependency jars has a directory foo inside with a file bar.txt within:
dep1.jar
|
\--foo/
|
\--bar.txt
dep2.jar
|
\--foo/
|
\--bar.txt
Here is a main class of main.jar:
public class App
{
public static void main( String[] args ) {
ApplicationContext ctx = new StaticApplicationContext();
Resource barResource = ctx.getResource("classpath:foo/bar.txt");
}
}
Which of two bar.txt files will be loaded? Is there a way to specify in a resource URL a jar the file should be loaded from?
Which one you get is undefined. However, you can use
Resource[] barResource = ctx.getResources("classpath*:foo/bar.txt");
to get them both (all). The URL in the Resource will tell you which jar they are in (though I don't recommend you start programming based on that information).
Flip a quarter, that's the one you'll get. Most likely, it will be the one highest alphabetically, so in your case the one inside dep1.jar. The files both have identical classpaths (foo.Bar), and while this should look to throw a compile time exception, it will not because it will just package both jars up and not try to compile/look at the (this specific file) file as it is a .txt file.
You wouldn't expect a compile time exception as resource loading is a run time process.
You can't specify which jar the resource will come from in code, and this is a common issue, particularly when someone bundles something like log4j.properties into a jar file.
What you can do is specify the order of jars in your classpath, and it will pick up the resource from the first one in the list. This is tricky in itself as when you are using something like ivy or maven for classpath dependencies, you are not in control of the ordering in the classpath (in the eclipse plugins at any rate).
The only reliable solution is to call the resources something different, or put them in separate packages.
The specification says that the first class/resource on the class path is taken (AFAIK).
However I would try:
Dep1Class.class.getResource("/foo/bar.txt");
Dep2Class.class.getResource("/foo/bar.txt");
As Class.getResource works cannot take resources from another jar, as opposed to the system class loader.
With a bit of luck, you will not need to play with ClassLoaders and hava a different class loader load dep2.jar.
As #Sotirios said, you can get all resources with the same name using ctx.getResources(...), code such as :
ApplicationContext ctx = new StaticApplicationContext();
Resource[] resources = ctx.getResources("classpath*:/foo/bar.txt");
for (Resource resource : resources) {
System.out.println("resource file: " + resource.getURL());
InputStream is = new FileInputStream(resource.getFile());
if (is == null) {
System.out.println("resource is null");
System.exit(-1);
}
Scanner scanner = new Scanner(is);
while(scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
}
I am trying to create multiple logs in Log4j, but I am facing a weird problem. Here's the log4j.properties and the code implementing it.
# Define the root logger with appender file
log4j.rootLogger = DEBUG, FILEALL
# Define the file appender
log4j.appender.FILEALL=org.apache.log4j.FileAppender
log4j.appender.FILEALL.File=${logfile.name}
# Define the layout for file appender
log4j.appender.FILEALL.layout=org.apache.log4j.HTMLLayout
#log4j.appender.FILEMAIN=org.apache.log4j.FileAppender
#log4j.appender.FILEMAIN.File=${logfilemain.name}
#log4j.appender.FILEMAIN.layout=org.apache.log4j.HTMLLayout
I have added the statement when running both and removed the original one
log4j.rootLogger = DEBUG, FILEALL , FILEMAIN
And this is the java code:
System.setProperty("logfile.name", savePath1);
// System.setProperty("logfilemain.name", savePath1);
logger = Logger.getLogger(HarishLog.class.getName());
PropertyConfigurator.configure("log4j.properties");
The code works perfectly fine till I make one log, but as soon as I enable the setting for 2nd log in either the properties or the javafile, nothing happens.
Besides I am unable to put a different name at
log4j.appender.FILEALL.File=${logfile.name}
it only works for logfile.name and logfilea.name, It doesn't work for any other name if I change it both in the javacode and the properties folder. Why is this???
Thank you
This works for me:
log4j.rootLogger = DEBUG, FILEALL, FILEMAIN
log4j.appender.FILEALL=org.apache.log4j.FileAppender
log4j.appender.FILEALL.File=${logfile.name}
log4j.appender.FILEALL.layout=org.apache.log4j.HTMLLayout
log4j.appender.FILEMAIN=org.apache.log4j.FileAppender
log4j.appender.FILEMAIN.File=${logfilemain.name}
log4j.appender.FILEMAIN.layout=org.apache.log4j.HTMLLayout
import org.apache.log4j.Logger;
public class LogTest {
public static void main(final String... args) {
System.setProperty("logfile.name", "logall.txt");
System.setProperty("logfilemain.name", "logmain.txt");
Logger logger = Logger.getLogger(LogTest.class.getName());
logger.info("hello");
}
}
If you're still having problems, try adding:
log4j.debug = true
to the beginning of your log4j.properties, and check the output messages.
I want to customize my log messages in Tomcat6, and have created a class "MyFormatter" which looks like this:
public class LogFormatter extends Formatter {
#Override
public String format(LogRecord record) {
StringBuilder sb = new StringBuilder();
sb.append("LOLCAT--")
.append(new Date(record.getMillis()))
.append(" \t")
.append(record.getThreadID())
.append(" \t")
.append(record.getSourceMethodName())
.append(" \t")
.append(record.getSourceClassName())
.append(" \t")
.append(record.getLevel().getLocalizedName())
.append(": ")
.append(formatMessage(record))
.append(System.getProperty("line.separator"));
return sb.toString();
}
}
I've packed this into a .jar and placed in ${catalina.home}/lib.
In my logging.properties file I've added the following:
1catalina.org.apache.juli.FileHandler.level = FINE
1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
1catalina.org.apache.juli.FileHandler.prefix = lolcat.
1catalina.org.apache.juli.FileHandler.formatter = my.package.LogFormatter
After sevral attempts trying different packaging, different configs, i decided to try the built in "org.apache.juli.OneLineFormatter" - and this works perfectly. So the config should be fine.
The question remains, why doesn't Tomcat6 load my class?
I found the solution.
After reading about Tomcat and Class Loading, i found that there is an order which Tomcat follows. It goes like this;
Bootstrap (/jre/lib/ext) -> System (/catalina-home/bin/) -> Common (/catalina-home/lib) -> Webapps.
tomcat-juli.jar which contains the logging stuff is loaded with the "System"-step, so when you place other logging stuff in common, it ignores it cause its already loaded.
The solution is then to place the .jar before tomcat-juli.jar is loaded aka in /jre/lib/ext.´
Edit:
It's not always a great idea to keep it in the jre folder, so I found that the best solution is to put it in an endorsed directory.
-Djava.endorsed.dirs=${catalina_home}/endorsed
This endorsed directory will run before System class loading.
In weblogic I can configure in the console for the Serverlog to use log4j instead of default JDK logging.
However the serverlog is not using a log4j.properties file, but seems to use the configuration in config.xml
Even if the log4j.properties file is in the classpath and I set these properties:
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dlog4j.configuration=file:<path>/log4j.properties
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.log.Log4jLoggingEnabled=true
Is it possible to use log4j.properties configuration for Weblogic Server Logging, or can I only change the log4j configuration with java code?
I don't know anything about WebLogic in particular, but adding -Dlog4j.debug will cause log4j to tell you where it's looking for its configuration. I've found that to be invaluable when tracking down logging issues in tomcat previously.
Check out the docs for PropertyConfigurator and DOMConfigurator for details on the log4j configuration process.
If you put the log4j.xml in your classpath, WebLogic will pick it up. I use Apache Commons logging with log4j in WebLogic, and it's an easy thing to do. No need for those Java options.
Where are you setting the above options? Try putting the -Dlog4j option in the Server Start options for each managed server that will use log4j
To specify logging to a Log4j Logger instead of the default Java Logging:
* When you start the Administration Server, include the following Java option in the weblogic.Server command:
-Dweblogic.log.Log4jLoggingEnabled=true
From: http://edocs.bea.com/wls/docs103/logging/config_logs.html#wp1014610
I never got this working as I intented.
What I eventually did was create some kind of work-around.
I register a Handler that listens to the Weblogic serverlog.
From this handler I do my own logging to log4j. That logging can be redirected to do anything I want.
create a custom logHandler:
public class CustomLogHandler extends Handler {
..
public CustomLogHandler () throws SecurityException, IOException,
NamingException {
String log4jConfig = LogFilterConfiguration.getLog4jDirectory();
classlogger.info("log4j configured for file"+ log4jConfig );
PropertyConfigurator.configure(log4jConfig);
logFilterConfiguration = new LogFilterConfiguration();
}
public void publish(LogRecord record) {
WLLogRecord rec = (WLLogRecord) record;
if (!isLoggable(rec))
return;
if (getLoggerName().. is something i want to log) {
// do my own log4j logging
}
then create an ApplicationLifecycleListener.
with a postStart method:
public void postStart(ApplicationLifecycleEvent evt) {
Logger logger = LoggingHelper.getServerLogger();
Handler oldHandler = null;
Handler[] currentHandlers = logger.getHandlers();
.. code to remove an old custom handler if exists...
with something like logger.removeHandler(oldHandler);
// add custom handler to serverlogger.
CustomLogHandler h = null;
try {
h = new CustomLogHandler ();
// If handler was removed we can add a new version.
if (!(unRemovedHandlerClasses.contains(h.getClass()))){
logger.addHandler(h);
registerMBean(h) ;
}
} catch (Exception nmex) {
classLogger.error("Error adding CustomLogHandler to serverlogger "
+ nmex.getMessage());
logger.removeHandler(h);
}
}