How to use docker passed environment variable in log4j2? - java

I have a spring-boot based application that uses log4j2.xml in a dockerized environment.
When I launch the docker I pass -e "LOG4J_PATH=/tmp/app.log" to specify where the logs should go to.
Next, in the log4j2.xml I use fileName="${env:LOG4J_PATH}" but this doesn't work. I searched the web for hours and thus tried double $ and tried sys instead of env... nothing.
This System.getenv("LOG4J_PATH") and (new EnvironmentLookup()).lookup("LOG4J_PATH") work fine, so I know that the variable is being passed to the running image ok, but from some reason the log4j doesn't seem to pick it up.
If I run this not via a docker and set the LOG4J_PATH environment variable in my .bash_profile it works fine so this is something between docker and log4j.
What am I doing wrong?

I believe you need to change the key value structure a bit for your passed environment variable. Can you try
docker run -e LOG4J_PATH='/tmp/app.log'

Related

How to read Ubuntu environment variables into SpringBoot Program?

Here's what I do in SpringBoot on Windows to read an environment variable (location of log folder).
In Windows Server, I set a System environment variable for "LOG_HOME" with the value with the directory that SpringBoot should use to write logs.
In SpringBoot's application.properties, I have:
logging.file.name= ${LOG_HOME}/ws.log
Works great!
But in Ubuntu Linux 20.04, the same approach doesn't work for me at all.
When the WAR file tries to deploy on Ubuntu 20.04 using this similar technique:
(in .bashrc): export LOG_HOME = /home/ubuntu/logs
reboot (to reload the environment for sure)
I get this error in the Tomcat log when trying to deploy the WAR file:
java.lang.IllegalArgumentException: Could not resolve placeholder 'LOG_HOME' in value "${LOG_HOME}/ws.log"
So, it seems that Spring doesn't see the environment variable set in Ubuntu.
I wrote a simple Java program just to check the value of the environment variables and they were all created as expected including the LOG_HOME as shown in Linux "printenv".
If possible, I need a technique that will work on Ubuntu without changing the working SpringBoot implementation on Windows Server.
Thanks in advance for suggestions.
Instead of exporting in shell session like
export LOG_HOME = /home/ubuntu/logs
try this as -D VM argument in your starup command
eg:
java -cp=xxx mainclass -DLOG_HOME=/home/ubuntu/log
if you are using tomcat then :
VM args can be added catalina.sh file under CATALINA_OPTS.
For tomcat, add your environment variables to $TOMCAT_HOME/bin/setenv.sh where $TOMCAT_HOME is the directory of your tomcat installation.
The solution for me posted by the extremely helpful satyesht above, was to edit the Catalina.sh file and add the "-D" name-value pair option under CATALINA_OPTS. Thanks to all who posted. :)

externalize spring datasource properties for docker

I want to be able to override
spring.datasource.url
spring.datasource.user
spring.datasource.password
on each application start using environment variables;
That's because I'm trying to dockerize application and database.
I want to specify url, user, password in .env file (for docker-compose)
and share them with application container (connect to db) and db-container (to be able to connect).
I tried
export SPRING_DATASOURCE_URL=${url}
export SPRING_DATASOURCE_USER=${user}
export SPRING_DATASOURCE_PASSWORD=${password}
./mvnw spring-boot:run
But it seems to pick up only the last param (password in this case)
Also i tried
./mvnw spring-boot:run \
-Dspring.datasource.url=${url} \
-Dspring.datasource.user=${user} \
-Dspring.datasource.password=${passwrod} \
but this one seem to care only about the first one (url in this case).
I really want to know if there is any viable method to achieve that
via terminal or do I actually have to override properties file each time?
UPDATE
The problem was indeed in me.
It's spring.datasource. username, not user. Really sorry for waisting time on that.
You are using maven to execute your spring boot app which is the cause of at least some of your problems.
When you specify a -D argument while using maven it is passing system arguments to maven not to your spring app. If you read https://docs.spring.io/spring-boot/docs/current/maven-plugin/examples/run-system-properties.html it shows you need to use the syntax
mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Dproperty1=overridden"
The easy answer is dont use maven to execute your spring boot app. Produce a jar and execute it with java -jar
Do any of your values contain perhaps a space, or other shell busting character? Quotes are your friend here
Does a more basic java -jar target/project.jar -Dspring.datasource.url=... do anything different?
On the export SPRING_DATASOURCE_URL ... variant, can you properly echo $SPRING_DATASOURCE_URL and get the expected value back? For all the env variables?
If you use simple values like XXX and YYY instead of real values, does that do anything different?
Trying to distinguish between the mechanism and the data...
Are the ${url}, ${user} and ${password} (or ${passwrod} even) literal (eg: env variables themselves) or just elided here for brevity?
There just was a typo. It's spring.datasource. username, not user (or SPRING_DATASOURCE_USERNAME). Really sorry for waisting time on that.

Access environment variable in .conf file for spring boot app

I have set environment variable by executing the command
export test=abcd
I can see test=abcd when I run printenvcommand
I have deployed a springboot.jar application and I am passing the JAVA_OPTS from the springboot.conf file.
JAVA_OPTS='-Dspring.profiles.active=aaa -Denv=$test'
I started the app by service springboot start . When I check the process, env variable doesn't have the value of $test environment variable.
/usr/bin/java -Dsun.misc.URLClassPath.disableJarChecking=true -Dspring.profiles.active=aaa -Denv=.
How can I access the environment variable in the conf file? I read somewhere the environment variables will be stripped off when run as service. Basically I want to run as service springboot start which internally executes the below command
java -Dspring.profiles.active=aws -Denv=${whatever is set for env environment variable} -jar springboot.jar
I have tried the below configurations but nothing worked
JAVA_OPTS='-Dspring.profiles.active=aaa -Denv='$test
JAVA_OPTS='-Dspring.profiles.active=aaa -Denv='${test}
JAVA_OPTS='-Dspring.profiles.active=aaa -Denv=${test}'
JAVA_OPTS="-Dspring.profiles.active=aaa -Denv=$test"
Be careful about your quotes. Assuming that you use a "normal" shell, variables won't be substituted in single quotes.
java -Dspring.profiles.active=aws -Denv="$myvariable" -jar springboot.jar should lead to env being available in the JVM, no matter if you run it as a service or not.
If you can't get it to work, try to specify a hard coded value like this java -Dspring.profiles.active=aws -Denv=foo -jar springboot.jar. If env is now available in the JVM, your problem is with your shell or run mechanism. Verify that the user who runs the command (i.e. do you use sudo?) has the variable set.
I had the same problem where my .conf was referencing an environment variable which was in the .bashrc.
What I found out is:
The problem is service strips all environment variables but TERM, PATH and LANG which is a good thing. If you are executing the script directly nothing removes the environment variables so everything works.
https://unix.stackexchange.com/questions/44370/how-to-make-unix-service-see-environment-variables
One solution would be to install your app as a systemd service:
https://docs.spring.io/spring-boot/docs/1.3.x-SNAPSHOT/reference/html/deployment-install.html
Or another way is to use docker and you can specify extra configuration in the docker file, like loading a file which contains your environment variables.
As those solutions where not available in my case I ended up with having the value in the .conf file, like: -Denv=prod

How to parameterize Dockerfile?

I'm creating a Dockerfile for our Spring Boot application. The application takes a couple of command line parameters. At the end of Dockfile:
CMD java -jar Application.jar --bucket=bucket.list --key=lost
But is it a best practice to hardcode the values for bucket and key in the Dockfile?
If it is ok, then I can live with that. Otherwise, I would like to know how to parameterize the Dockfile.
The application will be deployed on AWS, if that opens the door for more suggestions.
Docker design focused in being independent as far as possible of Host environment, including when building a Docker image. There were a request to let Docker build accessing to host environmental variables but it was rejected looking for independence of host machine. There it is also commented some workaround that could fit your problem.
Anyway, what is supposed to do Application.jar? If it's an application supposed to be running inside the container (and not in building time) the correct way to launch it is using a custom script run when you start the container. There you can set your credential or any other information you wish to be accessed from a environment variable, that can be set when launching the container: docker run -e "MYKEY=secret" -e "MYBUCKET=bucket" myuser/myapp /my/custom/script
You can use environment variables or build arguments.
Build arguments allow you to specify parameters that are applied at buildtime when you execute docker build using the --build-arg ARG_NAME=ARG_VALUE command line parameter.
Environment variables allow you to specify parameters that are applied at runtime when you execute docker run using the -e "ENV_NAME=ENV_VALUE" command line parameter.

Java System.getEnv()

In mac OSX and in Linux CentOS, I insert a new system environment variable (i.e. "MYAPP") using .bashrc & .bash_profile. I even restarted my laptop (mac) and my server (linux).
When I use the command line "env", that environment variable showed with the correct value. But somehow every time I try to get it in a Java app (desktop app or web app or EJB or servlet any other java app) in either mac or linux, that environment variable ("MYAPP") is not retrieved.
I tried to iterate through the entire environment variables that Java can retrieve and it turns out that it retrieves every environment variables other than "MYAPP". This is very odd.
Anyone know how to solve this?
Did you export MYAPP=...? Exporting the variable makes it available to child processes, like java being run by your shell.
In Linux, if you only set the variable (or export it) in a bash session, it will be available to a kind of "sub" session, which is only available to the command you just executed, and nothing else.
You could probably use the dot operator in bash (also called "source" command). From the page:
When a script is run using `source' it runs within the existing shell, any variables created or modified by the script will remain available after the script completes.
So you could try doing . export VARIABLE=value, and then running your java program. This is similar to setting a variable in a Windows terminal, and then opening a new terminal and expecting the env var to be there. It won't.
This way, you are telling bash "this command should be available in this specific session (the session's process)". OTherwise you are telling it "set this env var for the bash session that will end after I run this export command" thus, it won't exist when you run your Java program.
After having defined and exported the environment variable. Launch your IDE from the same Terminal.
Try to write
"$System.env.STOREPWD"

Categories

Resources