Optional printing of stack trace in Java - java

I am creating a java application in which there is a possibility that exceptions might be thrown. I am handling these exceptions in a try-catch block and printing appropriate messages so that the business users don't have to see the ugly stack trace in case of a failure.
But now when I debug, I would like to invoke the JAR file in such a way that it will print the stack traces for me, so that I can know where exactly the problem lies.
Can someone enlighten me on how this can be achieved?

You could use slf4j as a facade in order to be able to change quickly implementations and logback as the implementation for your logging. The combination of slf4j and logback are currently a very efficient logging system. In case you would want to use another implementation such as log4j, JDK 1.4, or Jacarta Commons logging you could just change the jar of the implementation(binding) with the one you want. It follows a quick user manual of how to use slf4j with logback.
Setting Your Environment
First of all you should import the necessary jars that are required to use slf4j.
Go to slf4j download page and download slf4j-1.7.0.zip
Extract slf4j-1.7.0.zip
Go to logback download page and download logback-1.0.7.zip
Extract logback-1.0.7.zip
Eclipse Guide
Go to your project in eclipse and right click on it. Select Properties.
From the left panel select Java Build Path
Select Libraries tab
Click Add External Jars
Locate your extracted folders and add logback-core-1.0.7.jar, logback-classic-1.0.7.jar from the logback-1.0.7 folder and slf4j-api-1.7.0.jar from the slf4j-1.7.0 folder. Click ok to return to the project.
Netbeans Guide
Go to your project in Netbeans and right click on it. Select Properties.
From the left panel select Libraries
Select Compile tab
Click Add Jar/Folder
Locate your extracted folders and add logback-core-1.0.7.jar, logback-classic-1.0.7.jar from the logback-1.0.7 folder and slf4j-api-1.7.0.jar from the slf4j-1.7.0 folder. Click ok to return to the project.
*In case you are not using any of the above environments you should add the above 3 jars in your classpath manually. Dependening your operating system there is a different procedure to follow.
Using the logging framework
First of all you should import the required dependencies:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Each class that you would like to log should have a private logger:
private final Logger logger = (Logger) LoggerFactory.getLogger(this.getClass())
By having a seperate logger for each class you provide more flexibility and easier change of your logging levels(WARN,ERROR,INFO,DEBUG).
According to the log level that would like to use you should have
logger.info("Your info message");
logger.error("Your error message");
logger.debug("Your debug message");
logger.warn("Your warn message");
There is also the possibility to use more complex method calls dependening on your needs. For example if you want to debug and show also the stacktrace you should use
logger.debug("Your debug message",e);
(e stands for a catched exception).
For example:
try{
//some code
}catch (IOException e){
logger.debug("An IOException was thrown at this method ",e);
logger.error("An IOException was thrown at this method ",e);
}
*At this point without adding any other configuration you have a simple logging system. In case you want more advanced configuration
read the advanced settings of logback that i have posted below.
Advanced Settings of logback
In order to have a more advanced logging system you should create an additional file which will contain all the logging configuration. For this example i am going to use the logback.xml. For more info regarding the different files of logback please refer to logback configuration.
At this point you should create a folder named resources and add it in the buildpath of your project.
Eclipse Guide
Create a folder named resources(You must see the folder in your project)
Go to your project in eclipse and right click on it. Select Properties.
From the left panel select Java Build Path
Select Source tab
Click Add Folder
Tick the resources folder that you have created and click ok. Click ok to return to the project.
At this point you should see the resources folder as a package.
Netbeans Guide
Create a folder named resources(it will not be visible yet on the projects view..Only in the files view)
Go to your project in Netbeans and right click on it. Select Properties.
From the left panel select Sources
In the Source package folders click Add Folder.
Select the resources folder and click ok.
You should see the folder created as a package.
*The name of the folder could be any name that you like. For convenience i named it resources.
The following applies in both Eclipse and Netbeans.
*In case you will not have the following file in your classpath which specify specific settings in the logging framework, the root logger level is automatically assigned in DEBUG. That means that all the logging levels are going to be logged. Debug is the higher level.
Create a folder named logback.xml and place it inside the resources folder.
Inside logback.xml place the following tag.
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>[%-5level] - %msg%n
</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<append>false</append>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %class - %msg%n
</pattern>
</encoder>
<File>log/log.txt</File>
</appender>
<logger name="org.test" level="INFO" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.test" level="DEBUG" additivity="false">
<appender-ref ref="FILE" />
</logger>
<root level="OFF">
</root>
The above configuration contains 2 different appenders,one in the console and one in a file named log.txt. The file is going to be placed in a folder named log. In case the folder log does not exist is going to be created automatically.
The pattern that you are going to use depends on you and how informative your messages will be.
In the current configuration the console appender is less descriptive and shows only the logging level and the message, comparing to the file appender which contains logging level, thread name, class name, time and message. The value append is set to false in order to create a new log file after every run of your application.
The above configuration uses 2 different loggers for the same package. The first logger prints only the info messages in the console and the second logger prints the debug messages to a file. This is achieved by using the filter that is placed inside the appenders.
*In the logger name you can have the fully qualified name of your class or the package name. In this case i assumed package structure
org.test and placed the class inside this package. If you have the
fully qualified name of the class the logging configuration will be
applied only for this class. In case you use the package name, all the
classes inside this package are going to follow the above
configuration.
There are many different ways to use the appenders and loggers and it depends in your needs and the complexity of your program. According to the situation that you described i believe that above solution could meet your needs.
P.S. For more descriptive definitions and more advanced configurations you could refer to the Logback manual

Why not use Logging levels.?
DEBUG for your technical errors
and INFO or ERROR with the business codes that your users understand.

such things are usually handled with loggers and different log levels.
You typicaly have 3 log levels, general logs, warnings and errors.
When you output you decide which level a message is
when you start your application you say to your loggger which level it should display.
For your users this will be ERROR, only the worst things should be visible, for yourself you would output everything

create two classes with the same package/name at two different locations
public class PrintStackTraceUtil
{
public static void printStackTrace(Throwable err)
{
//ignore
}
}
and
public class PrintStackTraceUtil
{
public static void printStackTrace(Throwable err)
{
err.printStackTrace();
}
}
when compiling your java program, put only one of those paths in your sourcepath

Related

Spring boot logging

I am using Spring boot to build a simple REST service and I am wondering about the most appropriate way to handle the logging.
In my application.properties file I have the following:
logging.level.org.springframework.web: DEBUG
While developing the application I simply run it as such:
java -jar myapp.war
thus, I got all the nice log messages in stdout. However, I intend to deploy it, and I'm wondering what is the most appropriate way to deploy the application and still have my logs.
Sure, one can simply redirect the output
java -jar myapp.war >> somefile
but this is not very elegant, and I want to deploy my application so that it can easily be used as a service:
ln -s /my/app/xyz.war /etc/init.d/xyz
Then, doing
service xyz start|stop|restrart
to manage it. Seems like doing this prevents me from redirecting stdout ..
Any ideas or advice about this?
What you are really after is Spring Boot file logging output functionality.
Quoting the above documentation:
By default, Spring Boot will only log to the console and will not
write log files. If you want to write log files in addition to the
console output you need to set a logging.file or logging.path property
(for example in your application.properties).
Essentially adding in your application.properties:
logging.file=name.of.my.log.file.log
logging.path=/path/where/above/log/file/gets/stored
In you application.properties file you can set two attributes for your logging.file.
Like in the documentation description: (26.3 File output)
By default, Spring Boot will only log to the console and will not
write log files. If you want to write log files in addition to the
console output you need to set a...
logging.file
Writes to the specified log file. Names can be an exact location or relative to the current directory.
logging.path
Writes spring.log to the specified directory. Names can be an exact location or relative to the current directory.
After setting one of those logging properties will be written in a file.
Here you can find the complete doc
I want to give a short update in 2020:
logging.file and logging.path are now deprecated:
Instead, I suggest to use logging.file.name in your application.properties (logging.file.path is also available).
Example: logging.file.name=./logdir/spring.log
Spring Boot support almost every logging frameworks you can use as per your convenience, but I will suggest you to use slf4j logging framework and customize using logback.xml it's very easy look at
1) Create LOGGER object adding this single line of code in your class
private static final Logger LOGGER = LoggerFactory.getLogger(YourClassName.class);
2) Create logback.xml file in /resource folder and copy below code
<?xml version = "1.0" encoding = "UTF-8"?>
<configuration>
<appender name = "STDOUT" class = "ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{yyyy-MMM-dd'T'HH:mm:ss.sss'Z'}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
</encoder>
</appender>
<appender name = "FILE" class = "ch.qos.logback.core.FileAppender">
<File>/var/tmp/app.log</File> <!-- LOCATION of FILE WHERE YOU WANT TO SAVE -->
<encoder>
<pattern>[%d{yyyy-MM-dd'T'HH:mm:ss.sss'Z'}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
</encoder>
</appender>
<root level = "INFO">
<appender-ref ref = "FILE"/>
<appender-ref ref = "STDOUT"/>
</root>
</configuration>
Using logback.xml configuration you can customize your application logging in spring boot

How to configure log4j and logback to log into one single file properly?

I have a java web app that uses logback with slf4j for logging.
And this project has a dependency jar (which is a sub project). And this dependency jar uses org.apache.log4j.Logger for logging.
All logs must go into one log file.
My problem is, whatever I am logging with the code in the jar file is not being written to the log file.
Initially I had logback.xml. To resolve the above problem I added log4j.properties file to my web app.
It resolved my problem and now I can see all logs in one file.
Again my new probelm is: Earlier the log file was rolling every day according to the rolling policy in logback.xml.
Now it is not rolling. Even after I configured the rolling policy in log4j.properties that matches with the rolling policy that is in logback.xml, rolling is not happening.
I don't want to replace log4j in my dependency with logback. And also I want to write alll my logs into one file.
Is it possible to this, how can I resolve my issue?
FYI: I have "log4j-over-slf4j" dependency included in my pom.xml
Log4j.properties:
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File=SOME_PATH/logs/studentApp.log
log4j.appender.file.rollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy
log4j.appender.file.rollingPolicy.FileNamePattern = SOME_PATH/logs/studentApp.%d{yyyy-MM-dd}.log
log4j.appender.file.maxBackupIndex=14
log4j.appender.file.threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%-5p] %d %c - %m%n
log4j.rootLogger=DEBUG,file
Assuming your rolling policy is correct, it's possible that log4j picks up the wrong configuration. log4j looks for the first configuration file in the classpath (first .xml then .properties) and if it happens to be the configuration from one of your dependencies your rolling policy will not work.
To test this you can add a system property -Dlog4j.configuration=... with the path to your configuration to the tomcat startup script. Or simply set your log level to TRACE/DEBUG and see if it affects the output.
Edit: your log4j config looks good, but the date pattern is missing.
Try adding
log4j.appender.file.DatePattern='.'yyyy-MM-dd-HH-mm (note that I've set the pattern to roll-over every minute, for easier testing)
Edit: you can remove every line containing rollingPolicy and maxBackupIndex - those will not be picked up in this case
The following configuration will create a .log file for the current date and keep adding the log in that file. When the date chanages, it will create a new file with the current date. This configuration will also zip the files as or when they exceed a certain size limit.
<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>log.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- or whenever the file size reaches 500MB -->
<maxFileSize>300MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%date %level [%thread] %logger{0} - %msg%n</pattern>
</encoder>
</appender>
Hope it helps.

Weblogic Log4j Loggging Server Logging Bridge throws ClassCastException when starting up

With Weblogic 11g I have done the following:
1 Created log4j.xml file where I created a new appender:
<appender name="WEBLOGIC" class="weblogic.logging.log4j.ServerLoggingAppender">
<param name="Threshold" value="ERROR"/>
</appender>
<root>
<priority value="WARN"/>
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
<appender-ref ref="WEBLOGIC"/>
</root>
2 Updated the ${DOMAIN_HOME}/bin/setDomainEnv.sh script with these changes:
LOG4J_CONFIG_FILE="${DOMAIN_HOME}/config/log4j.xml"
if [ "${LOG4J_CONFIG_FILE}" != "" ] ; then
JAVA_PROPERTIES="${JAVA_PROPERTIES} Dlog4j.configuration=file:${LOG4J_CONFIG_FILE}"
export JAVA_PROPERTIES
fi
JAVA_OPTIONS="${JAVA_OPTIONS} ${JAVA_PROPERTIES} -Dweblogic.log.Log4jLoggingEnabled=true -Dwlw.iterativeDev=${iterativeDevFlag} -Dwlw.testConsole=${testConsoleFlag} -Dwlw.logErrorsToConsole=${logErrorsToConsoleFlag}"
3 Copied the log4j jars to the domain/lib
cp ./wlserver_10.3/server/lib/wllog4j.jar user_projects/domains/my_domain/lib/
cp ./wlserver_10.3/server/lib/consoleapp/APP-INF/lib/log4j-1.2.8.jar user_projects/domains/my_domain/lib/
4 Starts the AdminServer, but I get this error:
java.lang.ClassCastException:
weblogic.logging.log4j.ServerLoggingAppender cannot be cast to
org.apache.log4j.Appender
Keep step 1 and remove changes done in other steps.
Now copy the log4j.xml to $DOMAIN_HOME/lib folder. This will keep your log4j.xml in the server's classpath and the server will use this log4j.xml as its log4j configuration. No additional changes are required.
In setDomainEnv.xml file, please add the set log4j.xml location as below:
set LOG4J_CONFIG_FILE=C:\bea\user_projects\domains\dev\lib\log4j.xml
if NOT "%LOG4J_CONFIG_FILE%"=="" (
set JAVA_PROPERTIES=%JAVA_PROPERTIES% -Dlog4j.configuration=file:%LOG4J_CONFIG_FILE%
)
What you have to see is if despite the warning that you get, if the ServerLogging is effectively not working on your appliction on your domain.
Probably not even there since you are only copying the log4j to the domain/lib folder but not the wllog4j.jar.
So your setup looks like is doomed not to work in any case.
To me, It looks to me that you are undergoing Class Loader Vodoo Messup.
(1) You set a global log4j properties file for the entire app server.
(2) it looks like weblogic console app is itself a ear and it bundles its own LOG4J implementation library, which you are copying into your domain.
When the console app runs, it must for sure make use of Log4j and the class loader loading the Log4j Appender definition is most likely a level lower than the class loader that knows about the ServerLoggin bridge adapter.
I am beting its for reason like this that weblogic is geting rid of LOG4j in future gnerations of the product.
They have too many class loader issues - JUL logging you have the APP class loader behind the core classes - such as Handlers/Adapters.
Anyway.
(3) When weblogic runs, namley when it bootstraps the console it probably runs some sort class loader that gives a level of isolation to the libs bundled in the applicaiton and it sees:
Oh! How nice, LOG4j is here, leets initialize it.
Second, LOG4j bootstraps and hits head on your log4j.properties where you put the server logging appender in there - cross cutingly for everybody (including the weblogic application console).
He goes hunting for this library and where does it find it?
One of them, the wllog4j.jar he finds somewhere in the weblogic generic container libraries. While the base core classes of Log4j he finds a level lower in the domain configuration or in the EAR configuraiton of the console applicaiton.
This is not good.
Try the following:
(a) go into the weblogic console app, go to the Meta Inf folder and re-write the weblogi-application.xml
It will have a prefer applicaiton packages that looks like this:
org.apache.log4j.*</package-name--> <---- here I've commented this line.
If you comment the above line, the application class loader will search for log4j libraries in the highest parent that knows about these classes.
So if you put at the same level where your ServerLogger is found you should be fine.
(b) Pug the log4j implementation library at the same level where the wllog4j.jar will be found.
Check if you still get the casting exceptions.
Be very careful when you make server wide cross cuting configurations.
Good luck.

How to control the log console output in log4j

I am new to JavaEE SSH environment, and currently I use log4j as my application's log system. But the problem is that if I set the log output level at DEBUG there are too many console output in MyEclipse, switch the output level to WARN will reduce the amount of the messages but also lost some information I interested in. So my question is how to let the log4j ONLY output ALL the log message generated by the Java file I am editing and DO NOT output ANY messages generated by others.
Assuming you are configuring log4j with a log4j.properties file, you can set a specific class or package to a different level like this:
log4j.logger.com.foo.MyClass=DEBUG
See http://logging.apache.org/log4j/1.2/manual.html for more introductory log4j stuff.
http://logging.apache.org/log4j/1.2/manual.html
You can configure the log-level of every Logger you created via Logger.getLogger("org.springframework") . You must search the configuration file for log4j.
Example for XML (from http://en.wikipedia.org/wiki/Log4j):
...
<logger name="org.springframework">
<level value="info"/>
</logger>
<!--
everything of spring was set to "info" but for class
PropertyEditorRegistrySupport we want "debug" logging
-->
<logger name="org.springframework.beans.PropertyEditorRegistrySupport">
<level value="debug"/>
</logger>
Hope that helps.
if you are running in the unix/linux environment, you can tail and grep the log file what exactly you are looking for. this is more flexible than modifying log4j configuration and much more powerful
I had the same issue whereby my log4j debug logging was being overwhelmed by Spring debugging in my JUnit unit tests (in Eclipse). I got around this by adding the following to my log4j properties file.
log4j.logger.org.springframework=FATAL

Tomcat and logback.xml

I am starting up a Seam + Hibernate + Logback project deployed to Tomcat 6. I use Eclipse as an IDE. Hibernate prints many debug statements, so I have put the level to ERROR, so I don't see them.
<logger name="org.hibernate">
<level value="ERROR" />
</logger>
I don't paste my whole logback.xml, because the problem I have, is not connected with its contents.
I have the following classpath problem - If I put the logback.xml in WEB-INF\classesof my project, it is not found by the classloader and I see all debug statements. On the other hand, if I move the logback.xml to the lib directory of Tomcat, everything works perfect and the debug statements are not printed.
But I want to have my logback.xml inside my project, not in the root lib directory of Tomcat.
And here is my question - isn't the WEB-INF\classes a correct place for logback.xml? Why isn't it found?
I have always known that the files under the WEB-INF\classes directory of the project, are put to the classpath. Thank you for any ideas you have.
All the best, Petar
It looks like your logback.jar is also under ${catalina.home}\lib.
If you want to put your logback.xml to \WEB-INF\classes, the logback.jar should be only under \WEB-INF\lib. The ${catalina.home}\lib\logback.jar should be removed.

Categories