Someone might yell at me to read the faqqing faq, but I'm in a hurry ...
Does anyone have a way to make javax or log4j logger refactor-sensitive?
Say, that currently utils.Something has the logger handle:
final static private Logger logger = Logger.getLogger(Something.class.getName());
And logging.properties has
.level = warning
utils.Something.level=info
Then using Eclipse to refactor Something to
nutilla.Somewhere
resulting in my logger handle and logger property becoming out of sync.
Perhaps, set logging levels programmatically?
Has anyone bothered to do it and was it worth the trouble?
Clarification:
After refactoring utils.Something to nutilla.Somewhere, the logger handle now would only log warning and not info, because of the entry in logging.properties file. So the question is, is there a way to replace the function of logging.properties file with programmatic means and if so, is it worth the trouble?
Reason and Motivation for question
I'm obstinate at not listening when advising me to avoid refactoring because ...
Refactoring is a constant habit of mine. I create classes by the hour, merge them, delete them, extract methods, etc ... I'm a restless class creator who finds no time wondering where to initially place a class. I dislike sitting down wasting time wondering where to place them initially - so I just place them in the most convenient package namespace.
After building a good amount of class/interface structure, it becomes apparent to me where certain classes, interfaces or methods shd have been then all the refactoring activities take place and ... tada ... that's when my logging.properties file is ruined a hundred lines.
If you configure logging using class (as opposed to package) names, checking "Update fully qualified class names in non-Java text files" in eclipse's rename refactoring dialog should do the trick.
I do not think there is a way out of the box that updated the package names and class names in your properties file as a result of refactoring actions.
You can:
update the properties file by hand when refactoring is done (refactoring should be an action that is not undertaken eveery week :=)
use fixed strings to create loggers (make logging more functional instead of physical)
load the properties file and adjust the property names on the basis of constants you declare in your class before initialising log4j with that properties collection
I would go for the first option myself, too much automagic behaviour can get you in a very non-transparent situation quickly.
I wouldn't use it (I think it makes more sense to be careful when refactoring) but here it goes:
private static Logger logger = Logger.getLogger(new Exception().getStackTrace()[0].getClassName());
Related
I'm trying to find the answer about the differences between:
class MyClass {
private static Logger logger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
}
and:
class MyClass {
private static Logger logger = LoggerFactory.getLogger(MyClass.class);
}
Is it only useful with you are planning to do a fine logging setup? Like separate the log of the class in a different file, print more/less informations for each one, etc.
I have this doubt because most of my classes I use LoggerFactory.getLogger(MyClass.class) but I think the LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) is enough in the most of the cases.
Thanks!
Is it only useful with you are planning to do a fine logging setup? Like separate the log of the class in a different file, print more/less informations for each one, etc.
This is correct. By controlling your logging down to the class level, by giving each class their own logger, you can more finely control the logging. For example, we typically log all log entries (regardless of level) for classes in our packages, e.g. my.employer.com.team.project. We then log ERROR for all other loggers. We then have the ability to view all the loggers that are being used on the application and can remotely enable/disable any logger we want in real-time.
I have this doubt because most of my classes I use LoggerFactory.getLogger(MyClass.class) but I think the LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) is enough in the most of the cases.
If you give all your classes the same logger, then they will all behave in the same way. I think you are right that for most cases you will treat all your classes' logging the same way, but that is not always the case. Also, if you are writing library code, then you must not use the root logger because now you remove the ability of the user's of your library to tune your libraries' logs.
I need to create a map of our domain classes simple names to their fully canonical names. I want to do this only for classes that are under our package structure, and that implement Serializable.
In serialization we use the canonical names of classes alot --it's a good default behaviour as its a very conservative approach, but our model objects are going to move around between packages, and I don't want that to represent a breaking change requiring migration scripts, so I'd like this map. I've already tooled our serializer to use this map, now I just need a good strategy for populating it. Its been frustrating.
First alternative: have each class announce itself statically
the most obvious and most annoying: edit each class in question to include the code
static{
Bootstrapper.classAliases.put(
ThisClass.class.getSimpleName(),
ThisClass.class.getCanonicalName()
);
}
I knew I could do this from the get-go, I started on it, and I really hate it. There's no way this is going to be maintained properly, new classes will be introduced, somebody will forget to add this line, and I'll get myself in trouble.
Second alternative: read through the jar
traverse the jar our application is in, load each class, and see if it should be added to this map. This solution smelled pretty bad -- I'm disturbing the normal loading order and I'm coupled tightly to a particular deployment scheme. Gave up on this fairly quickly.
Third alternative: use java.lang.Instrumentation
requires me to run java with a java agent. More specifics about deployment.
Fourth alternative: hijack class loaders
My first idea was to see if I could add a listener to the class loaders, and then listen for my desired classes being loaded, adding them to this map as they're loaded into the JVM. strictly speaking this isn't doing this statically, but its close enough.
After discovering the tree-like nature of class loaders, and the various different schemes used by the different threads and different libraries, I thought that implementing this solution would be both too complicated and lead to bugs.
Fifth alternative: leverage the build system & a properties file
This one seems like one of the better solutions but I don't have the ant skill to do it. My plan would be to search each file for the pattern
//using human readable regex
[whitespace]* package [whitespace]* com.mycompany [char]*;
[char not 'class']*
class [whitespace]+ (<capture:"className">[nameCharacter]+) [char not '{']* implements [char not '{'] Serializable [char not '{'] '{'
//using notepad++'s regex
\s*package\s+([A-Za-z\._]*);.*class\s+(\w+)\s+implements\s+[\w,_<>\s]*Serializable
and then write out each matching entry in the form [pathFound][className]=[className] to a properties file.
Then I add some fairly simple code to load this properties file into a map at runtime.
am I missing something obvious? Why is this so difficult to do? I know that the lazy nature of java classes means that the language is antithetical to code asking the question "what classes are there", and I guess my problem is a derivative of this question, but still, I'm surprised at how much I'm having to scratch my brain to do this.
So I suppose my question is 2 fold:
how would you go about making this map?
If it would be with your build system, what is the ant code needed to do it? Is this worth converting to gradle for?
Thanks for any help
I would start with your fifth alternative. So, there is a byte code manipulation project called - javassist which lets you load .class files and deal with them using java objects. For example, you can load a "Foo.class" and start asking it things like give me your package, public methods etc.
Checkout the ClassPool & CtClass objects.
List<CtClass> classes = new ArrayList<>();
// Using apache commons I/O you can use a glob pattern to populate ALL_CLASS_FILES_IN_PROJECT
for (File file : ALL_CLASS_FILES_IN_PROJECT) {
ClassPool default = ClassPool.getDefault();
classes.add(default.makeClass(new FileInputStream(file.getPath())));
}
The classes list will have all the classes ready for you to now deal with. You can add this to a static block in some entry point class that always gets loaded.
If this doesn't work for you, the next bet is to use the javaagent to do this. Its not that hard to do it, but it will have some implication on your deployment (the agent lib jar should be made available & the -javaagent added to the startup args).
Many forums and stackoverflow questions suggest that the recommended approach for creating loggers is to create them per class.
A quick look at the Log4j's Logger getLogger(String name) implementation suggests that, all the loggers are stored in a static map.
I wonder if we have thousands of classes in an application and a logger is defined in each class, wouldn't it cause memory/performance issues.
Alternatively, why cant we define some standard loggers (based on some functional criteria) in the application and have the developers use them in the classes. I understand that having a separate logger allows us to change its logging level, but I believe its not big issue if there are sufficient predefined loggers.
I looked at the questions Is a logger per class or is a set of loggers that are accessed by the entire application perferred? and Log4J: Strategies for creating Logger instances
but they dont seem to cover this topic.
You don't have to, it's just easier to manage. Loggers follow parent-child relationship. Children pretty much inherit everything from their parents. This way you can define very specific logging behavior or have it inherited generically.
Alternatively, why cant we define some standard loggers (based on some
functional criteria) in the application and have the developers use
them in the classes. I understand that having a separate logger allows
us to change its logging level, but I believe it's not big issue if
there are sufficient predefined loggers.
This would require some pretty intense dependency injection to make those loggers available to every type, also potentially adding an extra dependency.
I just ask about the logger name...
It it possible to give a custom name for a logger, or to use class name.
But what to use in which case?
-> Using a custom name for each application module: some classes are used by 2 modules -> to which logger name should it belong to?
-> Using classname: it seems to require a good package organisation -> best practices for logging known about that?
I prefer using class names for the purpose of debugging. You can read the log trace (and the class name will be displayed) from the log file and view line code, etc. It's useful in that sense. If someone else maintains your code, they don't have to do hectic searches on finding where the "custom name" is populated (in which class).
I still prefer classnames. It does not require a good package organisation but reflects your (good - hey what else?) package orgnization. And it's a common pattern to use the class names.
You can read the logging class file from the logs and set log levels on packages.
And following the common pattern prevents me from spending too much time on thinking about good (perfect) custom logger names ;-)
I've never heard of anyone use anything other than class name. I think anything else would be a mistake, leading to potential loss of logging detail down the line.
Don't overthink this one, just run with class name.
I donno about anything else than class name because in any case it's very easy to read through class for locating the issue or anything else..! And it is also better to log the code using all kinda log levels e.g. trace for all possible details, fatal for exception and similarly the debud,error,info etc.
is there a possibility to do some changes in object at runtime.
my problem is i have one class which returns me the instance of logger. and that class contains only one public method which returns the logger. below is the class..
public class LoggerManager {
public Logger getLogger(String FQCN) {
Logger logger = Logger.getLogger(FQCN);
logger.setLevel(Level.INFO);
return logger;
}
}
now if i want to change the returning object at runtime,
which means that the logger object which is set to level INFO, i want to change that one to DEBUG.. during program execution only when this code is called at a particular time... without changing the code anywhere.. some thing like that...
logger.setLevel(Level.DEBUG);
can i achieve this, by any means??
as this class is used everywhere within my code.. about a 1000 places, without changing the code by....some means can i achieve this...
I think that you are asking if you can change the behavior of the getLogger(String) method without changing the class. The simple answer is "no you cannot".
There are a couple of tricks you could try:
Putting a different version of the class ahead of the current one in the application's classpath ahead of the current version.
Using BCEL or something to modify the class bytecodes prior to loading.
However, both of these amount to changing the class.
I think your simplest approach is to modify the LogManager class so that you can generate loggers with different levels. With a little thought you should be able to come up with a solution that doesn't impact the rest of your codebase significantly.
However, it is also worth nothing that the normal way to set logging levels is to use a configuration file, rather than explicit calls to setLevel in the application.
is there a possibility to do some changes in object at runtime
Yes, you can make changes on Objects that are returned from method calls.
It is difficult to understand what you want to do. If you set the debug level on the returned logger, it should be set for all places in your running vm that request a Logger with the same FQCN argument.
You could set the desired value before your call, and back to normal afterwards.
Somme logging classes allow this.
I can't be specific here because your didn't say exactly what your Logger class is (Log4j ?).
This logger class seems to be called as a static method.
Therefore, if you have any threads in your system (if using Tomcat for example, or using a background thread), modifying the level is unsafe (it will modify for you but also for all threads).
However, the usual option to your problem is not to change the level.
When you want a log to appear, either :
Log at a level that appears
Choose a different logger (each one can be configured to a specific level).
It is possible to do it with AOP but it is rather compilcated. AOP allows you to handle both pre and post method invocation.
I am not sure why you have to getLogger more than 1000 places in your single project. Do you have more than 1000 classes? Normally, you only need to call it once per class. I would also change the code and redesign overcalling to this method.