I am starting using logback and I want to know if there are better ways of doing something.
I have this code:
public class ClassA {
private List<String> l;
private Logger logger;
public ClassA(){
this.logger = LoggerFactory.getLogger(this.getClass().getName());
}
....
public List<String> method() {
this.logger.debug("method()");
List<String> names;
try {
names = otherClass.getNames();
} catch (Exception e) {
String msg = "Error getting names";
this.logger.error(msg);
throw new ClassAexception(msg, e);
}
this.logger.debug("names: {}", xxxxx);
return names;
}
I have some doubts so far:
Every class will have a this.logger = LoggerFactory.getLogger(this.getClass().getName()); to create a logger.
Every method will have a this.logger.debug("method()"); to know when a method is called.
That doesn't look good. Is there a way to solve it?
Also I want to print a list in the .log in this line: this.logger.debug("names: {}", xxxxx);
the xxxxx should be replaced with something to print the list. An anonymous class?
Thanks for reading!
Using AspectJ and log4j you can use this. Compile your code with ajc compiler instead of javac and then run as normal with java executable.
You need to have the aspectjrt.jar and log4j.jar on the classpath.
import org.aspectj.lang.*;
import org.apache.log4j.*;
public aspect TraceMethodCalls {
Logger logger = Logger.getLogger("trace");
TraceMethodCalls() {
logger.setLevel(Level.ALL);
}
pointcut traceMethods()
//give me all method calls of every class with every visibility
: (execution(* *.*(..))
//give me also constructor calls
|| execution(*.new(..)))
//stop recursion don't get method calls in this aspect class itself
&& !within(TraceMethodCalls);
//advice before: do something before method is really executed
before() : traceMethods() {
if (logger.isEnabledFor(Level.INFO)) {
//get info about captured method and log it
Signature sig = thisJoinPointStaticPart.getSignature();
logger.log(Level.INFO,
"Entering ["
+ sig.getDeclaringType().getName() + "."
+ sig.getName() + "]");
}
}
}
Check out the AspectJ documentation on how to change the TraceMethodCalls calls.
// e.g. just caputre public method calls
// change this
: (execution(* *.*(..))
// to this
: (execution(public * *.*(..))
Regarding the
Also I want to print a list in the
.log in this line:
this.logger.debug("names: {}", xxxxx);
That's supported by slf4j/logback by default. Just do
logger.debug("names: {}", names);
for example
List<String> list = new ArrayList<String>();
list.add("Test1"); list.add("Test2"); list.add("Test3");
logger.debug("names: {}", list);
//produces
//xx::xx.xxx [main] DEBUG [classname] - names: [Test1, Test2, Test3]
Or do you want something specifically different?
Related
I am writing a test automation framework, and trying to simplify life for my users as much as possible. I would like my users to just assert as regular Junit 5 test, and the log writing (my instance of Log4J), report entry (Extent Report) will all be done within the assert.
So, I would like to delegate org.junit.jupiter.api.Assertions class so that:
assertTrue(myCondition, "My Message");
Will do the following (I copied the original assertTrue and added my functionality):
package org.junit.jupiter.api;
#API(status = STABLE, since = "5.0")
public class Assertions {
//...... Some original org.junit.jupiter.api.Assertions functions
public static void assertTrue(boolean condition, String message) {
try{
AssertTrue.assertTrue(condition, message);
}
catch(AssertionError error){
//Do my things - reporter and logger
throw error;
}
}
//...... Some original org.junit.jupiter.api.Assertions functions
}
However
org.junit.jupiter.api.Assertions is a long class to delegate.
it becomes complicated since AssertTrue is only visible in package level.
Would like to get some fresh thoughts on how to resolve it elegantly....
Thanks,
OK,
What I ended up doing was creating a new DelegatingAssert class, and for every Assert I was interested, I created the following:
public static void assertFalse(DelegatingExtentTest testCase, boolean condition, String message) {
try{
Assertions.assertFalse(condition);
testCase.pass(message);
}
catch(AssertionError e) {
testCase.fail("Did not: " + message);
getLogger().error("Fail message: " + e.getMessage());
getLogger().error("Fail stack trace: " + Helper.getStackTrace(e));
throw e;
}
}
I want to change the format of the log but after I overrided the formatter, it still outputs in the old way.
private void initializeLogger() {
logger = Logger.getLogger("gameLogger");
try {
Handler handler = new FileHandler("/home/bobby/IdeaProjects/GoFishBobby/src/logs/log1.txt");
MyFormatter myFormatter = new MyFormatter();
handler.setFormatter(myFormatter);
logger.addHandler(handler);
for (Handler parentHandler : logger.getParent().getHandlers())
logger.removeHandler(parentHandler);
}catch(IOException e){
System.out.println("IO exception");
}
logger.info("Game starting : ");
}
I overrided the default loggerFormatter
public class MyFormatter extends Formatter{
public String format(LogRecord record){
return record.getMessage();
}
}
and the output still contains the info line with the information I dont need
SEVERE: NativePlayer : 0 Scored A Book of rank 3
Mar 05, 2017 1:15:44 PM Game logAfterPlay
INFO: The transfer hand is:
4D 4H 4S
Edit
I just found out the log in the file is correct, but why its still like this on the console? how do I get rid of it on the console. I already removed all the parent logger.
You can locate all handlers by using the following method to walk all currently created loggers.
public static void printLoggerTree() {
final LogManager manager = LogManager.getLogManager();
synchronized (manager) {
final Enumeration<String> e = manager.getLoggerNames();
while (e.hasMoreElements()) {
final Logger l = manager.getLogger(e.nextElement());
if (l != null) {
for (Handler h : l.getHandlers()) {
System.out.println(l.getName() + "=" + h);
}
}
}
}
}
The logger tree changes over time when different parts of your code requests new loggers. So it matters when you call this method.
A better practice is to setup your own logging.properties file and configure your project to use that on startup. You also don't need to create your own formatter as you can change the format pattern of the SimpleFormater on the command line by using the following:
-Djava.util.logging.SimpleFormatter.format="%5$s%n"
I have created a common logger in the following manner in a root class that is referenced by many sub classes.
public static final Logger LOGGER = Logger.getLogger(RootClass.class.getName());
Then I am accessing that logger in child classes in the following manner.
private static final Logger LOGGER = RootClass.LOGGER;
I have a main classes which receives inputs from users and it calls a method in the RootClass to configure the logger.
logStatus = messageBus.configureLogPath(logPath,logLevel);
Below is the implementation of the above method.
public Boolean configureLogPath(String logPath,String level) {
Boolean result=false;
switch(level) {
case "info" :
LOGGER.setLevel(Level.INFO);
break;
case "severe":
LOGGER.setLevel(Level.SEVERE);
break;
case "debug":
LOGGER.setLevel(Level.CONFIG);
break;
case "off" :
LOGGER.setLevel(Level.OFF);
break;
default :
LOGGER.setLevel(Level.SEVERE);
}
try {
simpleFormatter = new SimpleFormatter();
logFileHandler = new FileHandler(logPath);
logFileHandler.setFormatter(simpleFormatter);
LOGGER.addHandler(logFileHandler);
result =true;
} catch (SecurityException e1) {
result= false;
LOGGER.log(Level.SEVERE, "Security exception when reading log file" + e1);
} catch (IOException e1) {
result = false;
LOGGER.log(Level.SEVERE, "IO Exception when reading log file" + e1);
}
return result;
}
Is it possible for me to disable logging from certain selected child classes? I have a requirement where the user should be able to set certain child classes for which the logs should be created. But since I am using a common logger in all the child classes I am not sure how to achieve this. Please advice.
To meet your requirements you have to create a custom log filter that checks the source class name and install it on the root logger.
public class SourceFilter implements Filter {
#Override
public boolean isLoggable(LogRecord record) {
//Source class can return null so handle it.
String cn = String.valueOf(record.getSourceClassName());
return !cn.startsWith("foo.bar.Child1")
&& !cn.startsWith("foo.bar.Child2")
&& !cn.startsWith("foo.bar.Child3");
}
}
Also, the level class has a parse method you can use to get rid of that switch statement.
We can mention the below code for logging off from particular Java Classes as:
Logger.getLogger("cs.fid.tm.reportingService.config.test1").setLevel(Level.OFF);
If all else fails, you could always just walk the call stack and see where your function was called from. Perhaps not the most efficient solution, but it should work in your case.
I have this program as shown below , right now its only printng the stacktrace .
my question is that , is it possible to get the stack trace and also a custom field , here in my case i need 1090099
Please tell me if its possible ??
package com;
import org.apache.log4j.Logger;
public class Test {
private static final Logger logger = Logger.getLogger(Test.class);
public static void main(String args[]) {
try {
String accountid = "1090099";
String desc = null;
System.out.println(desc.toUpperCase());
}
catch (Exception t)
{
logger.fatal("Exception inside the Test program ", t);
}
}
}
2013-06-26 21:44:29,723[main] FATAL(Test.java:<main>:16)- Exception inside the Test program
java.lang.NullPointerException
at com.Test.main(Test.java:12)
You have to include it manually in the message you're logging. But it looks to me like what you're really looking for is the MDC (mapped diagnostic context), a way to store values in a thread-local "context" that can then be used to distinguish between log messages relating to different application-level entities.
package com;
import org.apache.log4j.*;
public class Test {
private static final Logger logger = Logger.getLogger(Test.class);
public static void main(String args[]) {
MDC.put("accountid", "1090099");
try {
String desc = null;
System.out.println(desc.toUpperCase());
}
catch (Exception t)
{
logger.fatal("Exception inside the Test program ", t);
} finally {
MDC.remove("accountid");
}
}
}
You would then include %X{accountid} somewhere in your appender's layout pattern and it would include the appropriate MDC entry in every log message, including those logged by third-party code that you call.
I would create my own Exception class, with members to hold the additional information, and a suitable toString() method that displays them. Wrap the original Exception in your custom Exception and add the information you want preserved.
Yes, you can print the value as long as it's in scope.
String accountid = null;
try {
accountid = "1090099";
String desc = null;
System.out.println(desc.toUpperCase());
} catch (Exception t) {
logger.fatal("Exception inside the Test program " + accountid, t);
}
Also, I would suggest using logger.debug instead of system.out.println for your other logging calls...
You are close to achieving that.
In your case you will have to declare the accountid outside the try block and then you can append the accountid along with your Exception inside the Test program message`
String accountid = "";
try {
accountid = "1090099";
String desc = null;
System.out.println(desc.toUpperCase());
}
catch (Exception t)
{
logger.fatal("Exception inside the Test program.\nAccount ID: " + accountid, t);
}
In my aspect class's method I want to get the values of the parameters and the name of the parameters.
Names are still ok if I don't get but I need to get the values of the parameters passed is it possible?
(There is not issue with ptCut expression , the method is getting called i checked with sysouts)
My Aspect method is something like this :
public void excpetionHappened(Exception e) {
// Log the exception
// log the name of the method name/signature which caused the exception
// log the value of the input parameters to the method
// wrap and throw new exctn
}
Thanks in advance.
You can use a around advice.
This makes it possible to access the parameter while handling the exception.
public aspect ExceptionReporterAspect {
/** The name of the used logger. */
public final static String LOGGER_NAME = "AspectJExceptionLogger";
/** Logger used to log messages. */
private static final Log LOGGER = LogFactory.getLog(LOGGER_NAME);
pointcut stringRequestHandler() :
execution (#RequestMapping Object the.package..*(..));
Object around(): objectRequestHandler(){
try {
return proceed();
} catch (Exception ex){
Signature sig = thisJoinPointStaticPart.getSignature();
Object[] args = thisJoinPoint.getArgs();
String location = sig.getDeclaringTypeName() + '.' + sig.getName() + ", args=" + Arrays.toString(args);
LOGGER.warn("(AOP detected) exception within " + location, ex);
throw(ex)
}
}
}