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.
Related
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
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);
}
}
}
I had difficulties finding a relevant title since it is not a simple issue. I will try to explain. I have a class responsible of error reporting whose methods basically wrap multiple ways of reporting an error.
For example, I have a method failTest:
public static void failTest(Logger log, Exception e, String message, boolean reportToES, String esTestPath, String esTestSet, String esTestInstance)
{
log.error(e, message);
someExternalErrorReportingService(reportToES, esTestPath,esTestSet,esTestInstance);
Assert.fail(e,message);
}
And I call this error reporting method in many, many places and it doesn't seem a good practice (too many parameters, hard to follow their order etc.) to just call it with the es* parameters each and every time because they don't change very often so they could be set up once and then reused.
And I came up with this version
public static void failTest(Logger log, Exception e, String message)
{//same body
}
And then added method to set up es* parameters
setES(boolean reportToES, String esTestPath, String esTestSet, String esTestInstance)
{
this.reportToES = reportToES;
this.esTestPath = esTestPath;
this.esTestSet = esTestSet;
this.esTestInstance=esTestInstance;
}
and of course added these instance variables above.
And only now I can enunciate the issue:
now if I want to use this error reporting class I need to first instantiate it and set the es* fields. The issue is that I often need to use the error reporting in a utility class that could be static but now, with my change above, I have to instantiate it and set up the error reporting class in order to have the es* fields set before I call failTest().
To conclude, I don't like this solution either because I can't use static utility classes anymore and moreover some utility classes are already used in a static way so cannot be refactored to non-static and will end up being used sometimes static, sometimes instantiated.
So the question is, do you see a better solution in order to simplify the calling of failTest() in utility classes?
To give you an example, we have a client that
sets up the error reporting class and sets up its es* fields
This client calls utility method Utility.doSomething
public static doSomething(reportToES, esTestPath, esTestSet, esTestInstance)
{
try{
methodThatThrowsFatalException()
}
catch(Exception e){
failTest(log, e, "Some smart message",reportToES, esTestPath, esTestSet, esTestInstance);
}
}
Now, in order to reduce the number of parameters we can just add setErrorReportingInstance to the Utility class,
then in client instantiate the Utility, then utilityInstance. setErrorReportingInstance(configuredErrorReportingInstance). And doSomething becomes:
public static doSomethingRefactored()
{
try{
methodThatThrowsFatalException()
}
catch(Exception e){
errorReportingInstance.failTest(log, e, "Some smart message");
}
}
What is not ok, from my point of view, is that:
1. I have complicated the usage of Utility. Now I have to make sure it is instantiated before I use it. It's inconvenient when having a lot of Utility like classes.
2. I cannot make static methods in Utility if I have to do error reporting in their implementation.
3. The methods that are already used as static will remain with the es* parameters in their signature (due to backward compatibility). So I will have in the same class methods like doSomething and also methods like doSomethingRefactored.
4. I have created a dependency between utility classes and error reporting so I have an issue when I need to test the utility methods
The question is, how can I keep the simple design of utility classes as simple collection of static utility methods but in the same time use the error reporting class but without passing too many parameters since it is bad practice?
More details:
Actually the client is many TestNG test cases:
So first I had :
class TestClass1
{
static final boolean REPORT_TO_ES="true",
static final String ES_TEST_PATH="somePath", //and so on for the others
#Test
{
Utility1.doSomething(REPORT_TO_ES,ES_TEST_PATH,ES_TEST_SET,...
Utility2.doSomethingElse(REPORT_TO_ES,ES_TEST_PATH,ES_TEST_SET,...
Utility3.doSomethingMoreUseful(REPORT_TO_ES,ES_TEST_PATH,ES_TEST_SET,...
Utility4.doSomethingSomething(REPORT_TO_ES,ES_TEST_PATH,ES_TEST_SET,...
}
And then I would try to get rid of calling the doSomethings with the ES* values
by setting them once on the ErrorReporter instance (so I would also make ErrorReporter non-static).
class TestClass1
{
private ErrorReporter errorReporter = new ErrorReporter();
errorReporter.setReportToEs(true);
errorReporter.setEsTestPath("somePath");//and so on
Utility1 utility1Instance = new Utility1();
utility1Instance.setErrorReporter(errorReporter);
Utility2 utility1Instance = new Utility2();
utility2Instance.setErrorReporter(errorReporter);
#Test
{
utility1Instance.doSomething();
utility2Instance.doSomethingElse();
...
The title to your question should be "Static Mess".
Take a look at how real loggers work and you may get some ideas. Log4J and Slf4j are well respected ones. You need to control all of your static variables. You could create a Logger class that encapsulates the ES data and does the real work of logging:
// Does the real work of logging.
class Logger {
public Logger(all of your es data)
public fail(String msg) // Logs msg
}
Then you need a static collection of these Loggers referenced by name (I assume you have more than one set of es data). This gives you a central place to go get the loggers. Works if you're in a static method or somewhere else. The static collection goes inside the LogFactory object
class LogFactory {
private static Map<String, Logger> loggers ...
public static Logger get(String name) ...
}
Here is your static method using the new logger:
public static doSomething() {
try {
methodThatThrowsFatalException()
}
catch(Exception e){
LogFactory.get("Util").failTest(e, "Some smart message");
}
}
I would add a clear or reset method to LogFactory so that you have a chance of writing JUnit tests for your code. For the same reason I would write a NullLogger (in which case you might want to pull out an interface that the NullLogger and the EsLogger can both implement.
You need to decide how to add Loggers to LogFactory. I suggest doing it in your main class. Resist the temptation to do it in a static initializer.
LogFactory could also be written so it holds a collection and not a static collection. You then just keep a static reference (a Singleton) to it. Just keep in mind that you'll want a way to clear the Singleton to make unit testing possible.
Good luck.
Is there a way to add a static (not context dynamic) prefix to all logged messages when using slf4j without altering the formatting of the underlying logging framework? To be clear, that means that using MDC is not an option because this would need to be reflected in the formatting configuration of the underlying framework.
Is there another option than creating a custom wrapper for the slf4j logger and using it wherever I would normally simply initialize a slf4j logger?
public class CustomLogger {
private final String prefix = "custom-prefix";
private final Logger logger;
public CustomLogger(Class clazz) {
logger = LoggerFactory.getLogger(clazz);
}
public void info(String info) {
logger.info (prefix + info);
}
// other methods...
}
If some context is needed: I am working in an environment where multiple plugins run on a core application. Within such a plugin I would like to add the plugin's name as a prefix to logged messages.
Wrap your calls in another method that does a String.format(...) on the passed in String. Your formatter will have your static string, and will place the logging string somewhere before, within, or after that, before passing it onto the actual logger method.
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).