Writing a simple print method for debugging in Java - 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.

Related

Is there any way with one of the Java logging frameworks to restrict log records from being logged unless some security feature is disabled?

Is there any way with one of the Java logging frameworks to restrict log records from being logged unless some security feature is disabled?
Use case:
Company X has Java software called SuperThing in package com.x.superthing
Java class com.x.superthing.SuperSecretThingy contains lots of important IP
Company X wants to be able to enable logging from SuperSecretThingy using one of the standard Java logging frameworks (java.util.logging, log4j, logback, etc.); if they have to do something special to make this work, that's ok.
When someone outside Company X wants to run SuperThing, they should not be able to enable logging from SuperSecretThingy, e.g. with -Dlog4j.configurationFile=... because some of the log messages contain sensitive information
Constraint: code in the class in question (SuperSecretThingy) must not have a compile-time dependency on anything special, so that the logging code is just the normal stuff.
class SuperSecretThingy
{
final static private Logger logger =
LoggerFactory.getLogger(SuperSecretThingy.class);
// this example uses SLF4J
...
public void foo()
{
logger.info("Entering foo");
do_stuff();
logger.info("Exiting foo");
}
}
There's a Filter feature in log4j:
In addition to the automatic log Level filtering that takes place as described in the previous section, Log4j provides Filters that can be applied before control is passed to any LoggerConfig, after control is passed to a LoggerConfig but before calling any Appenders, after control is passed to a LoggerConfig but before calling a specific Appender, and on each Appender.
Is there a way to do this programmatically? If so, then I can filter out log events from a specific class.
Perhaps you could make your own logger class that wraps the log methods so you test for whatever your condition is and then call the logger if the condition is what you want.
Something like:
class SuperSecretThingy {
final static private SecretLogger logger =
SecretLogger.getLogger(SuperSecretThingy.class);
public void foo(){
logger.debug("Entering foo");
do_stuff();
logger.debug("Exiting foo");
}
...
}
class SecretLogger {
private Log log;
public SecretLogger(Class c) {
log = LogFactory.getLog(c);
}
public void debug(String message) {
if (mySecretConditions()) {
log.debug(message);
}
}
}

How to log to a regular file with Log4J without using levels (warn, info, etc)?

I am interested in using log4j for audit logging, in other words, I will open my own file and start logging anything I want. Therefore I do NOT want to use warn, info or any log level. Something like that:
Log log = new Log("blah.txt");
log.log("Test");
log.close();
How can I do that with LOG4J?
A concrete example that I can run will be very appreciated.
Thanks!
You could create your own level AUDIT and use that to log everything. Logging without a level might not be too easily achieved.
If you're already using log4j, you don't need to re-invent the wheel. You could just configure a FileAppender and only specify the package or class that should log to it, so nothing else does. You're not forced to use any specific or all logging levels, just stick to one, like debug or info. You can take it a step further and configure your FileAppender to only capture info or debug, whichever you choose.
Unless there's some value in not using what log4j already has built, I'd stick with what they have. Could you elaborate on your use case perhaps so we understand why you don't want to use the built in functionality?
It is highly recommended to keep logging configuration outside of your code. Today you think that you have simple logging logic, however tomorrow it can become more and more complex.
Anyway, if you want a quick start and one simple log method - you can build a simple wrapper over log4j API:
class Log {
private final Logger logger = Logger.getLogger(Log.class);
public Log(String fileName) throws IOException {
// print just message
Layout layout = new PatternLayout("%m");
// log to file
FileAppender appender = new FileAppender(layout, fileName, true);
logger.addAppender(appender);
logger.setLevel(Level.INFO);
}
public void log(String string) {
logger.info(string);
}
public static void main(String[] args) {
try {
Log log = new Log("test.log.txt");
log.log("Test message");
} catch (IOException e) {
e.printStackTrace();
}
}
}

Logging controller/service using Log4j ( Centralized logging )

I don't want to use something like this :
Logger.getLogger(MyClass.class).info("Info message");
each time I want to log something.
I've tried to implement a logging service that has methods like :
public void info(final Object aMessage, final Object aSender);
This allows me get Logger instance based on sender's class, but it hides the method and line of the log source class.
I know about alternatives like AOP or Slf4j. The first one is not exactly I want, the second one introduces something similar to the first line of code :
LoggerFactory.getLogger(HelloWorld.class).info("Info message");
So, my concern is not about hiding Log4j dependency, it's about unifying logging calls through the whole application, like this :
//loggingController instance was injected previously
loggingControler.info("Info message",this);
Is there any way to implement this ?
Ok, seems that there is at least one way to resolve the issue :
For example there are LoggingService and LoggingController. The first one works directly with Log4j, the second one is just a layer between service and the whole application.
public class LoggingService implements ILoggingService{
//other methods here.
#Override
public void warn(final Object aMessage, final Object aSender, final Throwable aThrowable) {
log(aMessage, aSender, Level.WARN, aThrowable);
}
private void log(final Object aMessage, final Object aSender, final Level aLevel, final Throwable aThrowable) {
final String className = getClassNameBy(aSender);
Logger.getLogger(className).log(className, aLevel, aMessage, aThrowable);
}
}
public class LoggingController implement ILoggingController{
//logging service is injected previously
#Override
public void warn(final Object aMessage, final Throwable aThrowable) {
loggingService.warn(aMessage, this, aThrowable);
}
}
So, in this case you allow the user to log something using :
loggingController.warn("A warning", null);
Using this way:
User knows nothing about underlying logging functionality
You always have the possibility to provide a dummy logger if you don't need it, or the environment doesn't allow it.
The logging functionality is unified across the whole application
The class name and code line are shown correctly in the log.
You cannot use one of the most useful features of the Log4j - filtering by package/class name.

Accessing a logger in a Java application

I have a question regarding logging in a Java application. I want to log the main user actions and possible errors with user friendly messages. Therefore, I have two appenders for the logger: one shows errors (level = error) in dialogs and the other writes the logs of the current user session into an html-file so that the user could send this file back if something goes wrong.
To avoid having the logger creation in every class (private Logger logger = …) I have a static reference of the configured logger in a class App which has also the methods for accessing the logger:
public class App {
private static Logger logger = Logger.getLogger("logger name");
…
public static void logError(String message, Throwable cause) {
…
}
public static void logInfo(String message) {
…
}
}
The logging is mainly done in the UI classes:
class UIWidget extends UIFrameworkWidget {
void aMethod() {
try {
someBusinessLogic();
} catch (Exception e) {
App.logError(“log message”, e);
}
}
}
Is this a good practice? (Note that the widgets are created by the framework.)
Thanks in advance for answers, comments, or hints on this
Better would be to use a single static Logger instance but allow each class to create a private instance which is initialized with the name of the class (and maybe other info). This private instance then uses the static instance to actually perform its logging but can be omitted if necessary and reduces references to external classes.
This is how slf4j does it, which is a logging framework you should consider using - however you could roll your own in the same manner.
Note, however I don't know how to get your error messages to be displayed within a dialog box - that may need to be explicitly added.
It seems you're just one step away of subclassing the JDK Logger. Having only one static instance prevents you from targeting specific classes at runtime. If you subclass the Logger, then you can still have has many loggers as logged classes and yet keep your appender's peculiarities.
You can also craft your own layout (and, in log4j at least, even add placeholders - geronimo has an example of this).

Alternatives to macro substitution in 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");

Categories

Resources