Java Logging Across Multiple Environments - java

Our team is trying to get to a Single Build state; the build that's sent to Test is the same build that gets promoted to Staging->etc->Production. Right now setting up logging for different environments is a bit of a sticking point (currently we use Maven Profiles and filter the logger config at build time).
We have many web applications and I'd like the logging to be defined by the app and configurable by the environment it's deployed to.
We use Log4j though we're open to different implementations that could make this work. We'd like the app to be able to define the loggers as some apps may want specific logging on certain packages but, ultimately, I want the environment to be able to configure logging paths and levels. For example, the QA environment may typically have INFO level logging on the com.foo.bar package but, if they run into a sticky issue, they may wish to bump that up to DEBUG. I don't want to have the app redeployed since that technically changes the build.
With minimal poking around I found that log4j can directly use system properties, so a setup like this would be possible:
<appender name="someAppender" class="com.whatevs.SomeAppender">
<param name="File" value="${logger.path}/some_app.log"/>
<!-- ... -->
</appender>
<logger name="com.foo.bar">
<level value="${logger.level.com.foo.bar}"></level>
</logger>
<root>
<priority value="${logger.priority}"/>
<appender-ref ref="someAppender"/>
</root>
My concern is that as the number of apps deployed and number of loggers defined increases, the overhead of maintaining the system properties could become cumbersome:
-Dlogger.path=/var/log -Dlogger.priority=INFO -Dlogger.level.com.foo.bar=WARN ...
I'm sure this issue has been tackled before. What's the best way to go about it?

There have been several attempts to make this better but no one has come up with a "standard" solution so far:
You can configure the logging with an MBean.
Pro: You can easily change the config remotely at runtime
Con: You can't save/collect a config "profile" (i.e. a useful set of configs) this way.
You can make your logging framework to load the config from outside the web app.
Pro: Config can easily be collected in a file.
You can use build time scripts to generate them from templates, etc.
Con: You need to keep these files somewhere, backup/install them, etc.
You better figure out a way to load a default config when none can be found
You can allow the application to switch configs at runtime. Just make sure that the initial config is useful to debug startup problems
Some logging frameworks like logback allow to include config files. That makes it easy to load a shared config and then use a single system property to specify which include file to load with overrides.
At the moment, I prefer the last approach because it allows any number of special configs. The testing team can have their own, manager it themselves, put it wherever they like, update it at their leisure.
Note that replacing log4j with slf4j/logback means just replacing a couple of JAR files. If you use Maven, it takes 2 Minutes (just add logback-classic and log4j-over-slf4j as dependencies and remove log4j)

Use Log4j api to set up log levels through the code. So depending on the environment, on app startup, you can set up the log configuration. You can even make it configurable at runtime by providing appropriate UI to define the log levels for different packages, of course, if required.

Related

Isn't it possible to configure logging INSIDE the application when using jboss AS 7.1.1.Final?

I read a lot but I couldn't figure out how I could specify for example the log level for specific classes.
Only way I could figure out was in the standalone.xml but why should I configure some application specific setting very general in the server? This complicates the deployment process unnecessary.
Isn't it somehow possible to define the specific log level and the output files somewhere inside the war without touching the server?
Btw. it doesn't matter if log4j or commons-logging or slf4j or whatever is used.
Using a logging.properties file or a log4j configuration file in your deployment will work in JBoss EAP 6.x and WildFly (formerly JBoss AS). Note though that a log4j configuration would only work if you use log4j for your logging facade.
That said I agree with Marko that this should probably be done in the server configuration. I would also encourage you to use the CLI or web interface rather than editing the raw XML as well. You can get some more information on the documentation.
I am sorry for not providing a direct answer, but consider this: the application being in charge of logging levels is a bad idea most of the time as this is something an AS admin should be able to change at any time. For example, the point of the DEBUG or TRACE log levels is to be able to place a lot of such statements in the code without hurting the production server's performance. However, once a bug is detected, you want to be able to lower the logging level without rebuilding the application. This should be a purely administrative task.
On the other hand, I do recognize the need to at least have a decent starting point for the logging configuration and I don't know of any architecture which would allow the application to provide defaults which are overridable by the server configuration.

Pros & cons of configuring logging at server-level vs application-level

Configuring logging in java can sometimes be tricky (due to the multiple existing logging APIs) and can be done at different levels (server, application, both?). So what are the pros and cons to configure logging at those levels?
I came with this list but I would like others to share their experience:
Server-level
Pros
Centralized configuration
Application must not be modified prior to deployment
Can log to resources managed by the server (files relative to server path, DB...)
Cons
Must be sure each application uses the same logging api
Configuration can grow big as more applications are deployed
Server knows maybe too much about categories=>logging-level mapping for each application
Application-level
Pros
Application may use the logging api of its choice
Application can configure its own logging-levels
Cons
Configuration must be edited prior to deployment in order to specify path to logfiles (if relative to server) or JNDI name of the logdatabase
Is there a way to combine the two to keep only pros? Like configuring loggers at the server-level and then categories=>logging-level mapping at the application-level?
Is there a way to combine the two to keep only pros? Like configuring loggers at the server-level and then categories=>logging-level mapping at the application-level?
To answer this question I would say, Yes, we can do. But in this case your server and applications have to follow a proper design and in the negative side all the application might become tightly coupled.
If your apps are java based then, you can actually make a single logger file for all applications and there you have to set the application logger level, provided your applications follow different package structures.
Suppose app1 follows package structure com.app1.. and app2 follow com.app2.., then you can modify logger level like below,
*.*.com.app1 = logger_level1
*.*.com.app2 = logger_level2
But if your app2 is dependent on app1, or it has package like this com.app1.app2.* or some common package names, then you might have define more on the logger properties, so the package structure and design is very much important to follow single logging mechanism.

logging frameworks and logback.xml

While creating an example using Java and the com.alvazan.orm.api library, the use of "System.out.println" is prohibited.
One of the first and most simple Java exercises learned is "Hello World", also using the
"System.out.println" (...also known as logging, or returned requested data found in the console?)
When using Eclipse, logging is turned off by modifying the logback.xml file (ctrl-shift-R and typing in logback.xml)
From there;
<logger name="com.alvazan.orm" level="WARN"/>
is the line to add to the logback.xml file so that only startup logs appear.
In addition, two more logs such as....
2012-09-14 22:05:08,067 com.alvazan.test.FactorySingleton createFactory
INFO: CREATING FACTORY FOR TESTS
2012-09-14 22:05:08,809 com.impetus.annovention.ClasspathDiscoverer processFile
INFO: adding folder to scan=file:/C:/AAROOT/workareas/area1/playorm/eclipsegen/
are used.
Just clarifying that all information is typed into the logback.xml file?
Is there a diffrent file to use(other than logback.xml)?
Or is the end-user to use, for instance, "com.alvazan.test.FactorySingleton createFactory"; and "com.impetus.annovention.ClasspathDiscoverer processFile"?
Finalizing this question, is the file path for the preceeding necessary?
Thanks for your time,
Ryan
In response to Brett, and additional information/questions,
How is your root logger configured? You are only setting WARN for com.alvazan.orm, so if your root logger is INFO, then com.alvazan.test INFO's will be logged.
Hey Brett, thanks for the reply...
As for the root logger configuration, I believe the value is set at "INFO".
That being said, I would want to set "INFO" to "WARN", to prevent the use of
"System.out.println"
Also in the previous question, I mentioned:
2012-09-14 22:05:08,067 com.alvazan.test.FactorySingleton createFactory
INFO: CREATING FACTORY FOR TESTS
2012-09-14 22:05:08,809 com.impetus.annovention.ClasspathDiscoverer processFile
INFO: adding folder to scan=file:/C:/AAROOT/workareas/area1/playorm/eclipsegen/
com.alvazan.test.FactorySingleton
and
com.impetus.annovention.ClasspathDiscoverer (diffrent package within same library)
Different locations found within the same library...
Do i need to do the logback process for the other files, or *package, or doing it one time within the same library, should suffice for all? Or do I adjust additional values?
Your first statement....
"While creating an example using Java and the com.alvazan.orm.api
library, the use of "System.out.println" is prohibited."
In general, no one uses in System.out.println. Hibernate does not, JBoss does not, tomcat does not. They all use a logging framework so you can configure each and every log in production and each company using tomcat or jboss or hibernate can configure it differently. If these programs use System.out.println, customers would have no control and your server would ALWAYS run slow as you NEVER want "all" logging and there is no way to turn System.out.println's off!!!!! they are always on. log.info can be turned off and on.
The most complete answer on configuring logback can be found in their documentation
http://logback.qos.ch/manual/configuration.html
Just clarifying that all information is typed into the logback.xml
file?
I am not sure what you mean by this question. logback.xml is the configuration for a logging library called logback which you can find at the link above.
Is there a diffrent file to use(other than logback.xml)?
logback has other options like a groovy file to configure it, but playOrm is using only logback.xml though any client can decide what configuration file they use since playOrm discards the logback.xml file that is checked in when delivering to other projects.
Or is the end-user to use, for instance, "com.alvazan.test.FactorySingleton createFactory";
and "com.impetus.annovention.ClasspathDiscoverer processFile"?
I am very confused by this question. The end user should not be using FactorySingleton(neither directly nor indirectly AND that class is not even in the jar because it's in the test package). The end-user will be using ClasspathDiscoverer only indirectly...in fact, end-user won't even know about these classes.
Finalizing this question, is the file path for the preceeding necessary?
Are you trying to ask is the file path in logback.xml necessary? If you want to know more about how logback works, you need to read alot of the documentation. Basically, you can do stuff like com.alvazan level=WARN to turn any classes in com.alvazan.** to warn level(This is recursive and applies to children, grandchildren, etc. etc.). The ROOT logger is always defined as well in logback.xml and is the level for ALL classes in a all packages unless overridden.
yes, the root logger in playorm is set to warn.
At the bottom of the picture you show(your picture is cut off so not in that picture), there is a "source" tab and you may want to click that to view the xml better and it would match up with logback's documentation better as well. Here is a link to the file I bet you are looking at..
https://github.com/deanhiller/playorm/blob/486079cfefbd2b4b79e99652b24c146572663dda/input/javasrc/logback.xml
root logger is clearly set to info and could be set to warn if you want.
So, what do you want. Do you want those two log statements to go away and be turned off? Do you want ALL log statements to be turned off except WARN?
If you want all to be turned off in ALL software libraries, just change the root logger to "WARN". If you want to turn just FactorySingleton off you can add this line
<logger name="com.alvazan.test.FactorySingleton" level=""WARN"/>
If you want all of com.alvazan logging turned off instead of just everything in com.alvazan.orm as you also want com.alvazan.test off as well, BUT you want all other libraries to still be on(BUT always want WARN on which you generally should want), then you could change this
<logger name="com.alvazan.orm" level=""WARN"/>
to the following instead
<logger name="com.alvazan" level=""WARN"/>
Your best bet to understand logback though is to read logback documentation.
How is your root logger configured? You are only setting WARN for com.alvazan.orm, so if your root logger is INFO, then com.alvazan.test INFO's will be logged.

Can I configure Log4j using Tomcat Context Parameters?

I want to be able to log at different levels depending on what server I deploy my Java web application on. For example, on my test server I would like to log at the DEBUG level and on my production server I would like to log at the WARN level. Currently my log4.xml file is located inside of my WAR file. I want to be able to configure the logging level of my web app using Tomcat context parameters. Is this possible?
I have seen that environment variables and system properties can be used. For example, ${catalina.home} gets interpreted. Is this same functionality available for substituting in Tomcat context parameter values?
For example, this would be the log4j.xml snippet:
<level value="${log.level}"
Where log.level is defined in %TOMCAT_HOME%/conf/context.xml:
<Parameter name="log.level" value="DEBUG" />
I have tried the above and it does not seem to work. Google has not turned up anything for me related to using context parameters in log4j configuration files. I found this SO question doing something similar using system properties, but it does not use Tomcat context parameters so it is not quite what I want. Is a context parameter value substitution capability available in log4j configuration? Do I need to explore other options such using an external log4j.xml file?
I don't think that Log4J does this natively. I recall that the Spring Web Framework came with a listener configured via web.xml that might do something a little similar. If you really wanted to, I think you could could grab the code for that and use it to create something that does exactly want you want.
One method I've been using recently for extremely fine-grained control over logging is the log4 appender. It's a little more work than editing your config file, but gives you an incredible amount of flexibility. See my answer here for a quick example on how one of these would be configured.

LOG4J as a singleton, anyway to avoid log4j logging conflicts in web environment

We have a j2ee web environment. The server is configured to share session and possibly classloaders across multiple webapps. Basically, one classloader could server multiple web apps.
This seems to cause issues with log4j. Different webapps could have different log4j configurations but the logging will move to the same file.
Reading online, it looks like log4j uses singletons a lot, in terms of the appenders and other functionality.
Is there a way to completely separate the log4j configurations from one webapp from the other.
Server: websphere6+
Log4j: 1.4.2
Java: 1.5
Example log4j.properties (webapp1):
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=/usr/local/file1.log
log4j.additivity.com.app=false
Example log4j.properties (webapp2):
log4j.appender.Z=org.apache.log4j.RollingFileAppender
log4j.appender.Z.File=/usr/local/file2.log
log4j.additivity.com.app=false
Right now, logging from webapp2 may appear in the webapp1 logs and vice verse. We don't want that.
Possible Solution:
It might be possible to add a custom file appender? Would that fix the issue and what code would I add to the custom appender?
Is it possible to change the log4j initialization. E.g., could I use some startup servlet to load logj4 for each webapp.
You have two ways to solve your problem:
Configure your app. server so it doesn't share classloaders across multiple webapps. When it does do that, because of log4j's nature, only one log4j.properties file will be loaded.
If you leave the app. server so it shares classloaders, then use one "master" log4j.properties file. In it, define appenders for root of every one of your applications (example com.mycompany.webapp1, com.mycompany.webapp2)
No solution is perfect. Particularly, the second one will be problematic if your web apps share some classes that use log4j. In that case, logs from both apps will end up in the same file.
Your problem is a common one. To understand more about this topic, google for "log4j and j2ee".
Edit: since solutions 1 and 2 aren't feasible, you could try something else:
Use log4j.properties file per application. In every one of them, define an appender for their root (as explained in solution 2), and set additivity to false. This also won't be perfect if there is any class sharing between them.
Configure log4j programatically for every application. Since they're legacy applications, this could be tough. One way to do it is to use ServletContextListener for every application and configure log4j on application startup. I haven't personally tried this, so I'm not 100% sure if there will be clashes due to shared classloaders.
Log4j won't be really updated anymore. Ceki Gülcü, who created log4j, stated that he will focus his efforts on slf4j and logback, to correct some mistakes he did during development of log4j.

Categories

Resources