Alternatives to macro substitution in java - java

I have a log statement in which I always use this.getClass().getSimpleName()as the 1st parameter.
I would like to put this in some sort of macro constant and use that in all my log statements.
But I learned that Java has no such simple mechanism unlike say C++.
What is the best way to achieve this sort of functionality in Java?
My example log statements (from Android) is as follows..
Log.v(this.getClass().getSimpleName(),"Starting LocIden service...");

Java doesn't have macros but you can make your code much shorter:
Log.v(this, "Starting LocIden service...");
And in the Log class:
public void v(Object object, String s)
{
_v(object.getClass().getSimpleName(), s);
}
Another approach could be to inspect the call stack.

Karthik, most logging tools allow you to specify the format of the output and one of the parameters is the class name, which uses the method Mark mentioned (stack inspection)
For example, in log4j the parameter is %C to reference a class name.

Another approach is to follow what android suggests for its logging functionality.
Log.v(TAG, "Message");
where TAG is a private static final string in your class.

Use a proper logging framework (e.g. slf4j). Each class that logs has its own logger, so there's no need to pass the class name to the log method call.
Logger logger = LoggerFactory.getLogger(this.getClass());
logger.debug("Starting service");
//...
logger.debug("Service started");

Related

Getting the name of class from which a method is being called for logging in Java

I am using log4j for logging purpose in Java. When I use log.info(some message),it logs the api from which the function is being called along with the message,which is what it does.
But my case scenario here is different which I am explaining through code snippets.
ClassA{
void log(String message){
log.info(message);
}
}
ClassB{
classA obj = new classA();
obj.log("hello");
}
In this case while logging log4j will log classA in the log file. But I want it to show classB instead of A. Is it possible achieve this??
You can use one more parameter which contains the name of the class from where you called.
e.g.
ClassA{
void log(String message, String className){
// Edit the log4j details
log.info(message);
}
}
ClassB{
classA obj = new classA();
obj.log("hello", this.getClass().getSimpleName()); //here
}
what you can do is while creating the constructor of log4j pass the name of the class that you want to be in log and rest you can do usually.
Well, wrapping a Logger object inside a custom class like that seems pretty unnecessary, and ... as you are seeing ... it introduces this problem that you wouldn't normally have.
However, it may be possible to make it work. Internally, the log4j formatting code is creating an Exception object to capture the current stack trace, and then trawling that to identify the calling class, method and source file / line. What you could do is to modify the behavior (ideally by overriding things in a custom class) so that it uses the next stack frame up the stack from the one it would normally use.
Unfortunately, relevant log4j code is all heavily abstracted, and rather hard to follow. It would take a few hours for me to figure out the best way to make the necessary changes, and I don't have the inclination to do that. (As I stated at the outset, what you have done is a bad idea ...)
what I did is defined
private static MyLogger logger = MyLogger.getLogger(GetPlanOptionsForPackage.class)
and then
logger.error("input cannot be null");
this solved my use case

Java - Hide system output when testing

I am not sure if it is possible but if so how do I do it? I have a project which contains another 2 projects as dependencies. The parent project is only used for testing purposes (client server app). When I am testing I have a difficulty reading through my testing output because of the large amount of output the client and the server projects have. I am trying to find a way hide from the console all the output(printf()) of my sub-projects so that I can only see my testing output. Is there a way to do so?
For testing I am using JUnit.
Thanks
You should use Java Logger API (or Log4J) instead of using System.out.print directly, sou you could disable specific loggers during executions of your tests.
If you can't change legacy code to use Logger API, there are two options:
Option 1:
Create a PrintStream decorator class like this pseudo-code:
public class FilteredPrintStream extends PrintStream {
private final PrintStream stream;
...
#Override
public void print(String str) {
if (notCalledBySubproject()) {
stream.print(str);
}
}
// override other methods too
}
Then you set default output: System.setOut(new FilteredPrintStream(System.out));
To find method caller (to create notCalledBySubproject method), see How do I find the caller of a method using stacktrace or reflection?
Option 2:
Use Logger API in your test code, so you can redirect your output to a file, for example, then you ignore console output and see the file generated by your tests.
It is not a perfect solution nor good coding practice but you could add a class like this
public class SystemOutput
{
public static final boolean DO_PRINT = true;
public void printf(String format, Object... args)
{
if(DO_PRINT)
System.printf(format, args);
}
}
and use Find and Replace once to replace "System.out.printf" with "SystemOutput.printf" in every class needed. Because both methods have the same declaraction only this has to be changed. When you want to block the output you can just set DO_PRINT to false.
Eclipse for example provides a search tool which can find and replace a certain string in every .java file in a project. (Strg + H under the File Search tab)
of course it is also possible to call System.setOut() with your own subclass of PrintStream, that overrides the printf() method to only print when a certain boolean value is true.
why do you need console to run unit tests? ignore it. if your tests passes you got 0 status code or green bar (IDE or jenkins). any error stack trace you can find in e.g. maven log tests results. just ignore the std output
another thing: using console in your application is usually bad idea - avoid it. use logging framework instead (it will let you control the destination and level of logging). use your IDE and refactor - replace all calls to printf with log.debug or with you own wrapper. if your IDE doesnt support it then use some regex and try replace-all
if you want to get rid of all the output you can redirect stdout to /dev/null or change output stream in java. but it's not a proper solution

How to override java.util.logging.Logger to my private logger?

I have code fetched from jar that uses java.util.logging.Logger.
Jar contains about 1000 logger usages and each class start from:
private static final Logger LOG = Logger.getLogger(SomeClass.class.getName());
I want to handle all logs there, means, to point them to my Logger usage and not to java.util.logging.Logger.
Therefore I wrote my own logger.
So instead:
LOG.log(Level.SEVERE, "Error sleeping", e);
I can write:
MyLogger.toLog(TLogLevel.WFS_ERROR, "Monkey", "Error sleeping", e );
The problem is I need run over all java files and replace with mine.
Messy way, hmm
Does anyone know how can by easy way to convert java.util.logging.Logger to com.boo.MyLogger?
Thanks,
The SLF4J project has a jul-to-slf4j bridge that can be used to redirect java.util.logging.Logger calls to SLF4J. You could use that (by making your MyLogger implement the interface defined by SLF4J).
Note that, however, unlike all other logging libraries, j.u.l. is hard-wired into the Java class libraries and cannot be bridged without a performance penalty.
Also, I don't know what you are doing with MyLogger, but usually there is no need to write your own. There are plenty of logging implementations to choose from, and they can be configured in many different ways. And even if you do have to write your own Logger implementation, you should use an existing interface (such as SLF4J which seems to most popular these days).
Take a look at SLF4J:
The Simple Logging Facade for Java or (SLF4J) serves as a simple
facade or abstraction for various logging frameworks, e.g.
java.util.logging, log4j and logback, allowing the end user to plug in
the desired logging framework at deployment time.
Using that you could then also use logback (same author) to log to a common logging framework using the various bridges already available. Or, write your own, but either way you would not have to worry about replacing all that code...
Oracle's Java 7 Logger is configurable, its implementation is simply:
public static Logger getLogger(String name) {
// This method is intentionally not a wrapper around a call
// to getLogger(name, resourceBundleName). If it were then
// this sequence:
//
// getLogger("Foo", "resourceBundleForFoo");
// getLogger("Foo");
//
// would throw an IllegalArgumentException in the second call
// because the wrapper would result in an attempt to replace
// the existing "resourceBundleForFoo" with null.
LogManager manager = LogManager.getLogManager();
return manager.demandLogger(name);
}
So you can also via code set a logging level; besides declarative.
LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.INFO);
Lars Vogel has a nice page, also with its own Logger class.
All put together is quite workable, but maybe sometimes somewhat hard to understand.

Custom Log Level

In my existing application "org.apache.log4j" API's have been used in java code.
Requirement :
I have to log some statement(say xyz) in log file in any case and should not dependent of log levels.For example : if my log level is error then also xyz should print, if my log level is debug then also xyz should print.
I cannot make log statement of xyz is debug because if i do this, other log statements apart from xyz will also start printing.
For this, I believe, I have to add some custom log level.Please help how to do it and how to set its level ordering so that in any case it should print.
Thanks in advance.
Best Regards
What you could do is create a different Logger for those statements (you are not restricted to use classes names when defining a logger)
// Standard logger
private static Logger log = Logger.getLogger(MyClass.class)
// XYZ logger
private static Logger logXYZ = Logger.getLogger("logs.xyz");
You can access the same logger from several class, you just have to pass the same label.
Then, in the configuration file, you can define a different log level for that category, and even output these logs in a different appender (different file, processing, etc.)
You could "hijack" the protected method Logger#forcedLog() to always print to the log.
You must place the hijacker class in the same package as Logger.
package org.apache.log4j;
/**
* #author maba, 2012-08-23
*/
public class LogOverride {
public static void print(Logger logger, String message) {
logger.forcedLog(logger.getName(), Priority.INFO, message, null);
}
}
And from your calling code
log.setLevel(Level.OFF); // Make sure logging is turned off
log.info("Normal logging"); // Will not be seen anywhere
LogOverride.print(log, "Overriding logger"); // Will still make it to your appender
This is what the log4j FAQ says about custom levels:
How do I add a custom level?
It is possible, but rarely appropriate. The request is commonly for a level named something like "audit" that doesn't obviously fit in the progression "trace", "debug", "info", "warn", "error" and "fatal". In that case, the request for a level is really a request for a mechanism to specify a different audience. The appropriate mechanism is to use a distinct logger name (or tree) for "audit" related messages.
So if you want to go with that suggestion then you should look at the answer from SJuan76.
If you do decide to go with the idea of creating a custom, you would need to create a subclass of Level to do this, because the Level constructor is protected.
/**
* Instantiate a Level object.
*/
protected Level(int level, String levelStr, int syslogEquivalent) {
super(level, levelStr, syslogEquivalent);
}
It looks like you should then chain to the Level constructor, passing it a suitable level value. Note that the larger the level number the higher the priority is. So for a Level that won't be blocked at any of the existing named levels, you want a value that is greater than Priority.FATAL_INT which is 50000.
(However, I'm not convinced that this is the right approach. For a start, you probably won't be able to refer to your custom level by name in a logging config file.)

Writing a simple print method for debugging in Java

Before I reinvent the wheel - I want to be able to insert debugging traces in my code, such as say("We are here.");, without defining static void say() in every class. It needs to do System.out.println(s), and to be globally switched on or off (doSay(false)), and I'd also like it to be able to identify the class from which it's being invoked (as described here). For example:
MyClass: We are here.
Does Java already have such a tool?
Use SLF4j, not log4j (at least, not directly). They are both created by the same author, Ceki Gülcü, but SLF4J incorporates knowledge gained by seeing log4j in use, and looking at advances in other logging packages.
SLF4J is a common API for a number of different underlying logging systems, like log4j, the java.util.logging package, etc. It also has its own "native" implementation, logback.
One reason I like it better than log4j is its support for message templates. These keep your code simpler.
Also, it allows me to include logging in a library, but let the user of my library choose the logging implementation. Without something like this, a user might have to configure logging just for my library, and it wouldn't be unified with the rest of his application.
The most popular Java logging framework is Log4J which does this (and much more).
Look here for a list of other.
Yes. It's called a logging framework. Java has java.util.logging. But many prefer using Log4J.
You could use a logger like Apache's Log4J and do something like logger.trace("We are here");. When you want that off, set the log level higher (debug, warn, error) in your configuration and the trace logs will disappear.
Java has more advanced logging tools, like log4j or logback. There you should create a public static final Logger logger = Logger.getLogger(..) and use the logger to write debug/info/warn/error messages to wherever you like. They are highly configurable - what and where to log.
For the simpler case (if this is a toy project), you can simply define a class with the public static void log(..) method and use it from every class.
Why not static-declare a function in your Main.java, and use it allround?
public class Main {
private static boolean debug;
public static void setDebug(boolean d) { Main.debug = d; }
public static void say(String s) { if(Main.debug) System.out.println(s); }
}
Let me know if this fits your needs.
Edit: revised the code
I didn't know about static import! I combined ideas from #ninetwozero, #karl, and #erickson to create this:
package myPkg;
public class CLHUtilities {
private static boolean saying = false;
public static void tracing(boolean b) {
saying = b;
}
/*
* Technique taken from:
* http://stackoverflow.com/questions/282977/which-class-invoked-my-static-method
*/
public static void say(String s) {
if (saying) {
Throwable t = new Throwable();
StackTraceElement[] trace = t.getStackTrace();
String className = trace[1].getClassName();
String whoCalledMe = null;
try {
whoCalledMe = Class.forName(className).getSimpleName();
} catch (Exception e) {
}
System.out.println(whoCalledMe + ": " + s);
}
}
}
which can be used simply as:
import static myPkg.CLHUtilities.*;
:
tracing(true);
:
say("We are here.");
Which suits my needs perfectly.

Categories

Resources