Changing log level for an executable JAR - java

I have built a maven Java project and deployed my JAR as an executable one on linux. I placed my log4j.xml under /src/main/resources directory during the build and hence it is now part of the final deployed JAR. I have configured log4j.xml with RollingFileAppender. So far, everything is working fine and I am able to see the logs generated.
However, wondering how to change the log level or any of the configuration within log4j.xml which is now part of the deployed JAR?
The other approaches I tried is having the log4j.xml outside in a separate directory and passed it as a configuration option to the JAR file using the below command
java -Xms512m -Xmx1024m -Dlog4j.configuration=/etc/myapp/log4j.xml -jar mylar.jar /etc/input.properties
java -Xms512m -Xmx1024m -Dlog4j.configurationFile=/etc/myapp/log4j.xml -jar mylar.jar /etc/input.properties
However, it is not working. No logs are being generated. So wondering whether I am doing this correctly or there is a better approach to implement.

from the quick look at your arguments, can you try passing the log4j path like below. (you are missing file: in the beginning of the path )
java -Dlog4j.configuration=file:/etc/cfg/log4j.properties -jar ./MyProject.jar ...
here is a similar answer present.

Related

How to run java project from command line (works fine in eclipse, gradle)

I'v got a simple java program. I'm developing in Eclipse, using gradle, and I'm logging with log4j2. This all works fine.
When I come to run from the command line, I do a gradlew build which works, but when I run the jar I get an error:
java -jar build\libs\testproj.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/logging/log4j/LogManager
at com.xxxxxx.practice.App.<clinit>(App.java:17)
Caused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.LogManager
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
... 1 more
I can see a bunch of log4j files in my userprofile.gradle folder, and I could run a gradle dist task and then got and unzip the resulting zip file and then point at the contents of that, but if there some way of avoiding this? Surely there's something I can add to the manifest or something so that I can run from the command line at will with no overhead?
To be clear, it's just the logging that has this problem. Other than that it runs, the tests work etc from the command line, and the logging works fine from eclipse.
Someplace in your eclipse project, you'll find a library (these end in .jar) named log4j.jar or org.apache.logging-log4j.jar or log4j-api.jar or something along those lines.
This jar contains the classes that are missing when you run this on the command line. Try it: jar tvf log4j.jar prints the contents of the jar and you'll find it contains, for example, org/apache/logging/log4j/LogManager.class.
During compilation and during running a java app, this class needs to be on the classpath. Eclipse is taking care of the compilation part of it.
If you run java as java com.foo.ClassName, the classpath is defined by the -cp parameter: java -cp testproj.jar:log4j.jar com.foo.yourapp.TestApp would work (use semicolon on windows instead). If you don't specify it, the system environment variable CLASSPATH is used, but you don't want that (you can run multiple java apps on one machine after all, so a global setting makes little sense).
If you run java using java -jar myjar.jar however, the classpath is solely taken from the jar file itself; inside the jar file is a file called META-INF/MANIFEST.MF and it contains (in text) key/value pairs. The relevant one is the Class-Path: foo.jar bar.jar entry: Space-separated listings of jar files, relative to the dir your jar is in.
You cannot use the -cp option when using the -jar option; the -cp option is ignored.
So, next steps:
I don't know if gradle made the Class-Path entry correctly. Best option is to use the jar or zip tool to unpack your jar to check that MANIFEST.MF file and see what's there. If there is, say, Class-Path: lib/log4j.jar in that file, then make sure that if testapp.jar is at /Users/Dev123456/project/testapp.jar, that that log4j jar is at /Users/Dev123456/project/lib/log4j.jar. If there is no class-path entry, then the error lies in your gradle config; you'd have to post your build.gradle file in that case.
There are 'striper plugins' which mix all the various jars together in one giant jar. This is usually a bad idea (it just makes the jar itself humongous, and makes builds take longer. If you're deploying java code to a server, you can manage the Class-Path entry by yourself, and if you are making a desktop app, you need an installer which... can manage the class-path stuff just as well. Really no reason to use stripers). I advise against using these.
Just run with java -cp yourapp.jar:dep1.jar:dep2.jar com.foo.fullyqualified.ClassName instead.
Try to run your jar by specifying Log4j jar as well in classpath and then mention your MainClass from testproj.jar :
java -cp build\libs\testproj.jar:build\libs\log4j.jar com.package.MainClass

slf4j over log4j configuration after deployment

I have built a java application and I've been using slf4j/log4j for logging. I would now like to provide the user with the possibility of changing logging levels without needing to re build the .jar file.
I've read around that to do this your properties file needs to be inside of the classpath of the application. I've tried using the Class-Path header in the MANIFEST.MF file to achieve this, however it is not working.
These are the two examples I've tried.
Class-Path:./config/
Class-Path:C:/users/user/directory/tools/config/
However none of these seem to be added to the classpath as I've tried printing its contents once the application starts running.
As suggested by this question, I ended up adding a parameter to the java execution command. Bear in mind that if you execute through bash commands, as I do, you need to add the full path to the directory.
java -Dlog4j.configuration=file:/path/to/log4j.properties -jar myApp.jar
I don't like this since it makes it extremely dependent on path configuration but it will suffice.
Please find below a list of possible solutions.
1) java -jar myApp.jar will search for the log4j.properties in following order
root of myApp.jar
the directories specified in the header Class-Path: of the myApp.jar
2) java -Dlog4j.configuration=file://path/to/file//log4j.properties -jar myApp.jar will use the properties file specified by -Dlog4j.configuration=...
3) java -Xbootclasspath/a:../config -jar myApp.jar will search for the log4j.properties in following order
directory ../config/
root of myApp.jar
the directories specified in the manifest header Class-Path: of the myApp.jar
I believe solution 1) should solve your problem like following
make sure there is no log4j.configuration in the root of myApp.jar
the manifest header contina for example Class-Path: config/
and the directory structure of your installation is
./myApp.jar
./config/
edit A possibe solution to avoid the hardcoded path, but use a defined location for the log4j.properties file could be as below.
assume following structure of your application
c:\somewhere\myApp.jar
c:\somewhere\config\log4j.properties
c:\somewhere\run_myapp.cmd
run_myapp.cmd
#echo off
... do your necessary preparation here
java -Dlog4j.configuration=file:%~dp0log4j_2.properties -jar myApp.jar
This will use always the config\log4j.properties relative to your myApp.jar.
%~pd - expands to drive letter and path of run_myapp.cmd
With this solution your users need to store the properties file at the given place but don't need to change the run_myapp.cmd script.

log4j likes its properties in a jar file?

I have a Java application which I'm executing on Linux direct from an executable jar file
java -cp .:./lib -Duser.timezone=GMT -Dlog4j.debug -jar programName.jar
The program uses a number of other jar files which are all in one directory and 4 properties files all of which are in another directory (the current directory). Both directories are included in the CLASSPATH.
Simple enough right.
It would be, except that Log4j fails to find log4j.properties. The only way I have managed to make it find log4j.properties is to include it in programName.jar
This is not what I want, I want to have it using log4j.properties residing in the same directory as all the other properties files, they are in the CLASSPATH and are found as you would expect.
The other jar files being used are:
jdom-2.0.5.jar
log4j-1.2.17.jar
ojdbc7.jar
quartz-2.2.1.jar
slf4j-api-1.7.7.jar
slf4j-log4j12-1.7.7.jar
I'm wondering if slf4j-log4j12-1.7.7.jar does some configuration which prevents log4j from scanning the CLASSPATH when looking for the properties file. My code does not include any instructions which aim to specify the location of the properties file.
I've not yet tried executing the program without the -jar option, I will try that next.
Does this ring any bells so far ?
Add an argument to jvm (log4j.configuration). e.g.:
java -cp .:./lib -Dlog4j.configuration=file:log4j.properties -Duser.timezone=GMT ...
You may want to see this answer for more options.

Running jar file from cmd with reference to external jars

I would like to know can I run a jar file from the command, with the jar file using log4j and ojdbc.jar as well.
The 'main' is located in: nmap_logic.jar.
Within the package containing the 'main' is called: "nn.gmap.logic".
I also use 2 external jar files: log4j.jar & ojdbc.jar.
I have tried running:
java -cp "nmap_logic.jar;log4j.jar;ojdbc.jar" nn.gmap.logic.NNmain
And I get an error that the log4j cannot be initialized.
From the Eclipse environment the application runs fine.
Please let me know how should I execute the command properly.
Thanks.
Try to give the full path to the jars. I believe that there is a difference between what you think is your root folder and what Java thinks about it.
Something like java -cp "c:\myjars\nmap_logic.jar;c:\myjars\log4j.jar;c:\myjars\ojdbc.jar" nn.gmap.logic.NNmain
Btw, you can also do the following: java -cp "c:\myjars\*" nn.gmap.logic.NNmain

How do you programatically 'relative import' a directory of jar files?

I am using Drools Planner which ships with 21 Jar files in a directory binaries. For example the
drools-core-5.3.0.Final.jar would provide org.drools.someClasses.
The included examples run it in command line by running an all-inclusive command:
mainClasspath=
for i in binaries/*.jar; do mainClasspath=${mainClasspath}:$i; done
mainClass=org.drools.planner.examples.app.DroolsPlannerExamplesApp
$JAVA_HOME/bin/java -Xms256m -Xmx512m -server -cp ${mainClasspath} ${mainClass} $*
I am developing the program in commandline. But the final application (which just adds a HTTP interface) runs in a JSP container, not from a commandline. Hence I need to programmatically import the jar files into my project.
Question:How do I programatically import the the jar files?
Do I rather have to specify the binaries path in the environment variable?
Update
I did this to tell Java that globally, extra classes may be found in the same directory, meant by the . and /home/jesvin/dev/drools/binaries. Note that : is the separator in Linux.
declare -x CLASSPATH='.:/home/jesvin/dev/drools/binaries'
You can do it per execution instance as per Miserable Variable's answer.
Finally, in a Tomcat deployment, I give it a HTTP interface, deploying it as per havexz's answer.
If the jars need to be in global scope:
Well if you want these jars to be available to all apps on the server try putting them in
`<tomcat_install_dir>/lib`
EDIT: #Geoffery comment: If you have access to
<tomcat_install_di>/conf/catalina.properties
then you can added your own common jars dir like:
common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar,${catalina.base}/your_common_jar_folder/*.jar
(See carefully at the end, I had added a new folder)
But either way, if the jars meant to be at global scope they should be in any of above folders. Else you have to put other common jars for logging and jdbc etc with every app.
If the jars need to be in app scope
And if you want these jars to be used only one specific app then put them in
`<your_web_app>/WEB-INF/lib`
Note: You can put the <your_web_app>/WEB-INF/lib of your development folder and if you are using the right tools then it will make these jars part of your .war file.
SIDE NOTE: Since you are running java program from command line too and having issues adding dependency jars. Sometime back I wrote a shell script for this purpose, hope this will help you too.
Small snippet of it:
Run Java easily from shell command
Usage: easyrunjava [-c <jar_name/jar_dir>,<jar_name/jar_dir>...] [-m <email_id1>,<email_id2>...] [-p <prop_file1>,<prop_file2>...] class_name [args_to_program]
Example:
`easyrunjava -p .,./dependency -m user.name#xyz.com com.example.HelloWorld`
Are you trying to put all jars in a folder in classpath? import is the wrong word then.
Newer versions of java allow '*' as classpath:
$JAVA_HOME/bin/java -Xms256m -Xmx512m -server -cp 'binaries/*' ${mainClass} $*
should work. Note binaries/* is in single quotes to prevent shell expansion.

Categories