java custom annotation to use a class field - java

I'm in a situation where I need to log some events and there are too many of them. So I want to be able to do this:
class S{
Logger logger = Logger.getLogger(S.class)
// default logger annotation to print
// >> [info] 'method void foo(A) was invoked'
#Log
void foo(A x){
/*...*/
}
// a specified comment to log
// >> [info] 'message to log'
#Log("message to log")
void foo(A x){
/*...*/
}
}
Then I need a custom annotation for this. As you can see I need to be able to access a variable and get method name dynamically. Is that possible in Java?

I'm not quite sure about what exactly you are asking, but I can tell you this:
You can always look at the current stack trace if you want to see the name of the method that you're in: new Exception().getStackTrace(). Variable names are not in the Java bytecode unless you compile in debug mode.

Related

How to instantiate loggers inside static methods in Log4j (For a large project)?

I know the question title is a bit raw, the more I tried to make it clear in a single statement the less I was successful.
Anyways, in a web application project I need to log every action (and the errors which occur inside them) with Log4j, there are hundreds of classes in the project and I want to avoid having a Logger object for each class. The level needed for logging is ERROR.
So, I've thought of a custom class containing a static method which handles the Logger object's "error()" method. As shown below:
public class LoggerUtil {
public static void error(String message, Object classObject) {
String className = classObject.getClass().getName();
Logger logger = LogManager.getLogger(className);
logger.error(message);
}
}
So that whenever "LoggerUtil.error(message, this.getClass().getName())" is called anywhere in the project, we are able to log something by current class' related logger.
Problem is that this doesn't seem to work, I guess it is not possible to instantiate a logger related to a specific class in another class.
When our LoggerUtil.error() is called in one class the ERROR log that is given shows an unrelated class as it's source (java.lang.String). I wonder if there's a way to make this idea work?
[UPDATE]: Problem solved. There was a problem in design, which was fixed in this way:
public class LoggerUtil {
public static void error(String message, String className) {
Logger logger = LogManager.getLogger(className);
logger.error(message);
}
}
The second argument was changed into String so the class name is passed to the method directly. The logger works fine now.
Your method expects an object as argument, and gets the class of this object to get the appropriate logger.
But when you call your method, you call it with
LoggerUtil.error("Error", this.getClass().getName());
So, what you pass as argument is not the object for which to log (i.e. the CustomClass instance), but the name of its class ("com.foo.CustomClass"), which is a String. And in the method, you just ask a logger for the name of the class of the object "com.foo.CustomClass", which is thus java.lang.String.
In short you're getting the name of the class oof the object twice: once in the caller, and once in the called method.
So, change your call to
LoggerUtil.error("Error", this);
and you'll get the desired result.

List the names of methods being invoked

I'd like to have a reflection-like solution for displaying the methods which are called.
Example:
public class Foo {
public void bar() {
new Y().x();
}
}
public class Y {
public void x() {
}
}
public class Main {
public static void main(String[] args) {
// SETTING UP THE MAGIC
new Foo().bar();
new Y().x();
}
}
The output should be:
1. Foo.bar
1.2 Y.x
2. Y.x
Optimally there could be an event handler which would be fired every time a method is called(and some info could be passed as a parameter).
PS: I don't want to use this in a production environment, I only need this for displaying output in a skeleton app without dummy copy-paste.
I would use aspectj to define an aspect which writes log messages for every method call. You can find an example here: Tracing Java Method Execution with AspectJ. or here: Logging with AspectJ
The advantage of this solution is that you don't have to make any changes on your code. You will need some time to get into aspectj, but it meets your requirement very well.
You would have to build a profiler agent if you wanted to achieve this without having to pollute your code.
Take a look at this article, it shows you how to do it. Specifically, look at profiling aspects lower in that article.
Listing 2 is a profiling aspect that can be used to output the class name, method name, and a timestamp every time the JVM enters or leaves a method.

Class '...' must be declared as 'abstract'. Checkstyle

I have this warning on most of my classes and not sure why is that. This happens on both public normal classes and final classes which have private constructors, some no constructor at all. I tried changing my private class methods to protected, doesn't help. Any suggestions on how to turn this off?
Here's a class example
public final class PlanBenefitManagerAssembler {
private static final Logger LOGGER = Logger.getLogger(PlanBenefitManagerAssembler.class);
/**
* No Instance of the this class is allowed.
*/
private PlanBenefitManagerAssembler() {
}
public static List<BenefitDecisionDetailsBean> assembleBenefitDecisionDetailsBean(
List<BenefitDetails> benefitDecisionDetailsList, int relationalSequenceNumber) {
LOGGER.debug("Enter assembleBenefitDecisionDetailsBean");
List<BenefitDecisionDetailsBean> benefitDecisionDetailsBeanList = new ArrayList<BenefitDecisionDetailsBean>();
for (BenefitDetails benefitDecisionDetails : benefitDecisionDetailsList) {
BenefitDecisionDetailsBean benefitDecisionDetailsBean = new BenefitDecisionDetailsBean();
benefitDecisionDetailsBean.setBenefitTypeCode(benefitDecisionDetails.getBenefitTypeCode());
benefitDecisionDetailsBean.setRelationSequenceNumber(relationalSequenceNumber);
benefitDecisionDetailsBean.setBenefitStatusDescription(
benefitDecisionDetails.getBenefitStatusDescription());
benefitDecisionDetailsBean.setBenefitStatusCode(benefitDecisionDetails.getBenefitStatusCode());
benefitDecisionDetailsBean.setBenefitUnderwritingStatusCode(
benefitDecisionDetails.getBenefitUnderwritingStatusCode());
benefitDecisionDetailsBean.setBenefitUnderwritingStatusDescription(
benefitDecisionDetails.getBenefitUnderwritingStatusDescription());
benefitDecisionDetailsBean.setBenefitChangeReasonCode(
String.valueOf(benefitDecisionDetails.getBenefitChangeReasonCode()));
benefitDecisionDetailsBean.setBenefitChangeReasonDescription(
benefitDecisionDetails.getBenefitChangeReasonDescription());
benefitDecisionDetailsBean.setComponentNumber(benefitDecisionDetails.getBenefitNumber());
benefitDecisionDetailsBean.setBenefitVisible(benefitDecisionDetails.isExplicitBenefitDecisionRequired());
benefitDecisionDetailsBean.setModelChanged(false);
// * Set BenefitLoading and BenefitExclusion
List<ExclusionDetailsBean> exclusionDetailsBeanList =
PlanBenefitManagerAssembler.assembleExclusionDetailsList(benefitDecisionDetails
.getBenefitExclusionsDetailsList().getBenefitExclusionsDetailsList());
List<LoadingDetailsBean> loadingDetailsBeanList =
PlanBenefitManagerAssembler.assembleLoadingDetailsList(benefitDecisionDetails
.getBenefitLoadingsDetailsList().getBenefitLoadingsDetailsList());
benefitDecisionDetailsBean.setExclusionDetailsBeanList(exclusionDetailsBeanList);
benefitDecisionDetailsBean.setLoadingDetailsBeanList(loadingDetailsBeanList);
benefitDecisionDetailsBeanList.add(benefitDecisionDetailsBean);
}
LOGGER.debug("Exit assembleBenefitDecisionDetailsBean");
return benefitDecisionDetailsBeanList;
}
}
When Checkstyle produces a warning the warning text should include a short rule name which will allow you to look up the exact rule that is being triggered. "DesignForExtension", for example.
Given the rule name, you can look up more detail on what it means in the Checkstyle documentation: http://checkstyle.sourceforge.net/availablechecks.html
Post the full details of the rule being triggered and someone might be able to help.
You can always turn the warnings off, but they generally are here for a reason :)
Do you intend to make them abstract classes ? If so, declare them that way.
Will you need to instantiate them at some point ? If so, add a public constructor.
I'm pretty sure this will solve your problem.
On sourceforge it says that the AbstractClassName rule uses the following regex:
^Abstract.*$|^.*Factory$
This causes classes with a name starting with 'Abstract' or ending with 'Factory' to be flagged. I get the 'Abstract..' part of that, but why should all '..Factory' classes be abstract? Sometimes I create factories which use dependencies to do their work so I need an instance to inject into.
This however does not explain your case. I tried your example class and did not get any Checkstyle warning (I am using the Eclipse Checkstyle Plug-in version 5.3.0.201012121300).
Are you sure you are getting the AbstractClassName warning for this class? Which version of Checkstyle are you using?

How to use Mockito to verify that an error message is logged?

I have an error condition that I want to test. The behavior I want to verify is that an error message gets written to the log. Since Mockito can't stub static methods, this is rather difficult, because I want my class-under-test to either write directly to System.err.println() or my static Log.error() method. I don't want to have to inject a mocked "logger object" into every single object that might write error messages!
So I guess what I'm asking is, what do you think is the best way to structure my Log class and/or the class-under-test so that I can stub out the logging methods or replace them with a mocked logger?
The best answer might not even make use of Mockito, it could be anything. I'd rather not import yet another library like PowerMock, but if you have a good answer that requires something like that I wouldn't mind seeing it.
If you want to keep your logging logic in static methods, you can still initialize real logging implementation when the class is loaded based on some system property:
class Log {
private static MyLogger logger;
static {
String className = System.getProperty("my.static.logger.class.name");
// Instantiate your logger here...
// By default use some DefaultLogger implementation...
}
public static void error(String message, Throwable t) {
logger.error(message, t);
}
}
Then when you run your test you can specify a mock class name using -D property. But you will need to implement your mock logging class without mockito magic.

a way to use log4j pass set flags in my code

I need to pass some value to enable certain code in may app (in this case is to optionally enable writing some stats to a file in certain conditions, but it might be anything generally).
My java app is installed as a service. So every way I have thought of has some drawbacks:
Add another param to main(): cumbersome as customers already have the tool installed, and the command line would need to be changed every time.
Adding java -DmyEnvVar=A_VALUE to my command line: same as above.
Set an environment variable: service should at least be restarted, and even then you must take care of what user is the service running under etc.
Adding the property in the config file: I prefer not to have this visible on the config file so the user does not see it, it is something for debugging etc.
So I thought maybe there is some way (or hack) to use log4j loggers to pass that value to my code. I have thought of one way already, although is very limited:
Add a dummy class to my codebase com.dummy.DevOptions
public class DevOptions {
public static final Logger logger = Logger.getLogger(DevOptions.class);
In my code, use it like this:
if (DevOptions.logger.isInfoEnabled()){
//do my optional stuff
}
//...
if (DevOptions.logger.isDebugEnabled()){
//do other stuff
}
This allows me to use discriminate among various values, and I could increase the number by adding more loggers to DevOptions. But I wonder whether there is a cleaner way, possibly by configuring the loggers only in log4j.xml??
In log4j you dont need Java classes at all to create loggers (This may come as a surprise). All you need is a package qualified string and not a Java class to create logger category. If I were you I would do the following
PS: Code not guaranteed to compile/run
public class SomeAppCode {
public static final Logger specialLogger = Logger.getLogger("com.mypackage.mysubpackage.speciallogger");
public void someMethod() {
if(specialLogger.isDebugEnabled()) {
//do low level stuff
}
}
}
In your log4j.xml add a category for this string mentioned and if you want you could set "additivity" to be true/false (depending on whether you want to propogate this log message to multiple loggerS)
PS: Note specialLogger is public static and such can be used by 100s of classes like it were their own loggers.
ok I think I found what I needed, wasn't that difficult actually...
public class DevOptions{
public static boolean isEnabled(String myvalue){
Logger logger = Logger.getLogger(myvalue);
return logger.isDebugEnabled();
}
}
public class SomeAppCode {
public void someMethod() {
if(DevOptions.isEnabled("value.A")) {
//do low level stuff
}
}
}
And I can add as many values like value.A in log4j.xml:
<logger name="value.A" additivity="true"><level value="debug" /></logger>
This way I can add as many values as I want by only modifying log4j.xml, no need to add more loggers to DevOptions, only one is sufficient.

Categories