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.
Related
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.
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.
Just a quick and simple question. I have a program with several classes that read information off of a .properties file. Is it better practice to pass the file from class to class as an argument in the constructor, or open the file directly in each class?
If you're going to do this by hand, I would recommend you create a configuration class, that takes the file via the constructor, and reads the property values into member variables. Then every other class that needs configuration takes a Configuration class via it's constructor. However, almost no one does this, and instead uses a framework like spring, which handles property injection for you.
In spring, it would look something like this:
<!-- application context xml file -->
<context:property-placeholder location="file:///some/path/to/file" />
Then in your java classes:
public class SomeClass {
#Value("${some.property}")
private String someProp;
#Value("${some.other.prop}")
private Integer someOtherProp;
// ...
}
At application startup the properties get injected into your class.
My suggestion is to have a Util class which loads the properties file and get values from that Util to the required classes.
Note: I dont think you have any issues on loading the properties file.
I would suggest that you create an immutable class that takes in the file as a constructor argument and sets all the instance variables. I'd call it PropertyConfiguration. Then since the class is immutable, you won't have to worry about passing it to everyone. You could even have a class that holds it.
For example, the code below would set you up to have a nice set up to have several things available project wide. I would just ensure that anything that's shared be immutable to ensure thread safety.
public class ClientUtils {
private static ClientContext _clientContext = null;
public static void setClientContext(ClientContext cc) {
_clientContext = cc;
}
public static ClientContext getContext() {
return _clientContext;
}
}
public class ClientContext {
private final Configuration _configuration;
public ClientContext(Configuration config){
_configuration = config;
}
public Configuration getClientContext() {
return _configuration;
}
}
If your program contains data which need not be a part of compilation and can vary from deployment to deployment, you must add it to the properties file : ( Things like database connection string, email addresses ).
Just in case you need this, I'm adding the code for accessing the properties files.
Drop the file in build directory.
Properties properties = new Properties();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("credentials.properties"));
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.
May I know what is the best practice of using org.apache.commons.logging.LogFactory.getLog?
For me, I use it the following way :
public class A
{
private static final Log log = LogFactory.getLog(A.class);
}
public class B
{
private static final Log log = LogFactory.getLog(B.class);
}
So, if I have 100 of classes, 100 of static log object will be created?
Or is it best to do it this way?
public class A
{
private static final Log log = LogFactory.getLog(Main.class);
}
public class B
{
private static final Log log = LogFactory.getLog(Main.class);
}
All the 100 classes are referring to a same single same log?
Thank you.
Please remember the 'static problem', which most of java developer ignore and which affect log4j, java.util.Logger, and SLF4J as well. You can read about it in the Apache Commons Wiki. As reported, if you're developing a library, planning to release it in a container (such as a j2ee container), shared among many application, is better use something like
private transient final Log logger = LogFactory.getLog( this.getClass() );
The first option - have a logger per class (or functionality). As most logging systems - log4j, logback, java util logging, etc. have the notion of logger hierarchy, you can match that to your package hierarchy and have finer control of which functionality you want to be logged at which level. For example:
com.example=WARN # global setting
com.example.web=INFO # increase logging for the web controllers
com.example.dao=DEBUG # trying to track bugs in the database layer
In the vast majority of cases, your way is the way to go.
The main advantage is that there is a logger (or category, or whatever) for each class that can be configured individually, like
log4j.logger.org.springframework.transaction=DEBUG
(assuming log4j) which will set the log level just for that class or package. If you use only one logger, you cannot do that.