design an error reporting utility - java

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.

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

How to change values of "private static final String filePath" for unittesting

I have my class as
public class MappingLoader
{
private static final String filepath = "/tmp/mapping.properties" // unix path of production system
private static Map<String,String> mapping = new HashMap<String,String>()
static
{
loadMappingFile()
}
#VisibleForTesting
static void loadMappingFile()
{
//reading properties files here
final Properties prop = new Properties();
try (final InputStream input = Files.newInputStream(Paths.get(filepath)))
{
prop.load(input);
}
catch (final Exception e)
{
...
...
throw new RuntimeException(e);
}
//now load "mapping" from properties file
....
....
}
}
For testing, I need to change the value of string variable "filepath" such that it should take development system path(say c:\project\target\mapping.properties)
I have tried powermocks in junits, but it always throws exception and terminates.
annotations at class level:
#RunWith(PowerMockRunner.class)
#SuppressStaticInitializationFor("some.package.ClassWithStaticInit")
and in test case:
Whitebox.setInternalState(Mappingloader.class, "filepath", testfilepath);
Mappingloader.loadMappingFile();
I also tried to change this via reflection as given in(Change private static final field using Java reflection) but it always throws the FileNotFoundException for "filepath" and does not takes the changed path "testFilePath"
Is there any way i can change this variable such that it does not throw FileNotFoundException without any modification in source code?
If I remove "throw new RuntimeException(e);" in source code, powermocks works for me. but i want to achieve this without modifying source code, either via powermock, reflection api.
Well, you can try your luck with Powermock; and that should work (maybe if you spend some more hours reading its documentation and making experiments); but honestly: your problem is not testing. Your problem is that you created untestable code. And now you are trying to use the big powermock hammer to "fix" what is your broken design.
You see, when using static methods and constants; people think they "save" on performance (which is true; but to a very small degree; which probably doesn't matter for 99.999% of all applications); but they keep forgetting that using static leads to direct coupling of different functionalities. static is an abnormality in good OO design; and should be used with great care.
So, in your case, you could replace the whole thing with something along these lines:
interface MappingLoader {
Map<String, String> loadMappingsFrom(String fileName);
}
class MappingLoaderImpl implements MappingLoader {
...
and you see, all of a sudden you are only dealing with "real" interfaces and classes; and non-static methods; and surprise: now you can fully unit-test the whole thing; and most likely, you don't even need a mocking framework. You just create a temp file with mappings somewhere; and then you make sure that your impl class gives you the mappings from that file.
Zero mocking; zero testing of internal implementation details; just a few asserts.
And another advantage on top: all your client code that should only be using the MappingLoader interface can also be tested. Because ordinary frameworks like EasyMock or Mockito will allow you to mock out instances of that interface ... because: no static calls any more!
That is how you change the value of private final static fields - by not using them!
(and if I made you curious: watch this to learn how to write testable code from the beginning)
While I completely agree with #GhostCat's response, I understand you are looking for a solution not involving to change the source code. Have you thought of changing the contents of /tmp/mapping.properties before the test runs (and restore them later)?
static final String fields or any final static primitives fields cannot be modified in runtime. If speed honestly you can modify these fields, but your change will not affect code which uses these fields, because during compile time reference is replaced by value.
My suggestion: use static mock to Files.newInputStream()call and then return ByteArrayInputStream with expected data. In this case you will avoid fragile disk IO operation which could affect your test stability.

Clarification in Java Code Testing

I have started reading the Spring in Action book.
I have no knowledge of JUnit which I think my doubt is about.
There is a code fragment where the author refers to and says that it is difficult to test:
package com.springinaction.knights;
public classDamselRescuingKnight implements Knight {
private RescueDamselQuest quest;
public DamselRescuingKnight() {
quest = new RescueDamselQuest();
}
public voidembarkOnQuest() throwsQuestException {
quest.embark();
}
}
The author says that:
It’d be terribly difficult to write a unit test for DamselRescuingKnight. In such a test, you’d like to be able to assert that the quest’s embark() method is called when the knight’s embarkOnQuest() is called. But there’s no clear way to accomplish that here. Unfortunately, DamselRescuingKnight will remain untested.
What does the author mean by this?
Why is the code difficult to test here?
My initial thought is that it is difficult to test because the "RescureDamselQuest" object is initialized in the constructor. This makes it difficult to for example insert a mock object. A mock object would help you test that the embark() method is called on the "RescueDamselQuest" object.
A better way to solve this can be to either include a parameter in the constructor (usually I prefer this method):
public DamselRescuingKnight(RescueDamselQuest quest){
this.quest = quest;
}
Or add a setter:
public void setDamselRescuingKnight(RescueDamselQuest quest){
this.quest = quest;
}
A common example I give is consider that you want to open a file, parse it, and get a data class out. Most will do something like:
Data openAndParse(String filename) {
...openFile
...parse
}
By doing it this way, the file open methodology and parse is highly coupled and difficult to test. If you have a problem in open and parse is it with the parse or the open?
By writing JUnit test, you are forced, for simplicity sake, to do something like...
BufferedReader openFile(String filename) {
...open file and return reader
}
Data parse(BufferedReader input) {
...parse and return data
}
JUnit leads us to a more cohesive solution. We write JUnit test simply by creating a string, constructing a StringReader, and then a BufferedReader. Well guess what? Very similarly we can now use parse to accept input from a variety of sources not just the file.
It's difficult to test because the quest implementation cannot be swapped out. Without byte code modification there's no trivial way to see if embark is called.
If you could set the quest implementation in a constructor or setter you could pass in an implementation that can spy on the call to embark.
One need to increase accessibility of fields and method of class to test. For example if one is testing a method which is package-private (default) then test cases which are generally in different package will not able to test this method. Therefore it is advised to to change in accessibility of fields to test the method. DamselRescuingKnight class can be tested which is not using DI by modifying the accessibility of RescueDamselQuest field from private to default. Then writing test case using mockito. Here is code for test case
#Test
public void knightShouldEmbarkOnQuest() throws QuestException {
DamselRescuingKnight knight = new DamselRescuingKnight();
RescueDamselQuest quest = mock(RescueDamselQuest.class);
knight.quest = quest;
knight.embarkOnQuest();
verify(quest, times(1)).embark();
}
And line which was changed in DamselRescuingKnight class to remove private accessibility
RescueDamselQuest quest;

Sustainable Method to Provide Additional Information in Exceptions?

I'm looking for a way to provide multiple pieces of information for exceptions back to end users. The obvious solution of extending Exception ends up with text distributed throughput the code, for example
throw new MyException("Bad data", "The data you entered is incorrect", "http://www.example.com/moreinfo/baddata");
and this quickly becomes unworkable.
I then looked at a catalogue approach but that's too centralized and requires jumping around from one file to another every time an exception is thrown. I'm now considering a hybrid approach with a static ErrorInfoMap class that contains mappings from a key to the more detailed information. Each class then has a static section that contains its own error mappings, so using the class which throws the above exception as an example I'd change it to:
throw new MyException("Bad data");
and at the bottom of the class there would be something like:
static {
ErrorInfoMap.put("Bad data", new ErrorInfo("The data you entered is incorrect", "http://www.example.com/moreinfo/baddata"));
// Information for other exceptions thrown by this class goes here
}
which allows an exception handler to fetch the additional information if required. Is this a good way of solving this issue, or is there a better way to handle this?
I'm not sure what exactly you mean by "the catalog approach" (could you provide a reference or more detailed description?) but from the information you provided, it's not clear to me how a static ErrorInfoMap avoids the problem of being "too centralized and [requiring] jumping around from one file to another every time an exception is thrown".
To me there are several options, depending on exactly what you need to accomplish:
Create a root class, ExceptionTemplate that extends Exception and does whatever repeatable behavior you'd like all your exceptions to do. Formatted toString() methods are a good example. Depending on your exact goals, you might like having your exceptions implement a builder pattern, like so:
throw new BadDataException("Supplied data is not valid")
.expected("a positive integer")
.referenceUrl("http://www.example.com/moreinfo/baddata");
Avoid stringly-typed solutions where an enum or subclass will do. If you don't need to define new exception types at runtime (and if you do, that should be a red flag that there's something deeper wrong with your design) and have an enum that contains all the necessary information to construct your exceptions:
public class EnumException extends Exception {
private EnumException() {} // prevent objects from being instantiated elsewhere
public enum Type {
BAD_DATA("Bad Data","Supplied data is not valid", "http://www.example.com/moreinfo/baddata"),
MISSING_DATA("Missing Data","Required data not found", "http://www.example.com/moreinfo/missingdata");
Type(String title, String genericMessage, String url) {
// Store input
}
public EnumException exception() {
// construct your exception
return new EnumException();
}
}
}
Which can be called with something like:
// note no `new` - the exception is returned by the enum
throw EnumException.Type.BAD_DATA.exception().expected("a positive integer");
This has the advantages of ensuring compile-time type safety, while still giving you the flexibility to define different types of Exceptions in one place.
Create lots of exceptions. I'm still not totally sure what objection you have to just creating a bunch of exceptions. You're looking for ways to "provide additional information" but claim that "the obvious solution of extending Exception ends up with text distributed throughput the code". This shouldn't be the case. Every subclass of Exception should hold all the necessary information except what can only be provided at construction time. Therefore there should be minimal "text distributed throughout the code" as any boiler-plate / reusable strings should be in the Exception class, and nowhere else.
public class DocumentedException extends Exception
{
private String title;
private String genericMessage;
private String detailedMessage;
private String url;
// Generally avoid this long constructor, and define subclasses that only need detailedMessage
public DocumentedException(String t, String g, String d, String u) {
super(g + " - " + d); // sets the getMessage() return value to something sane
title = t;
genericMessage = g;
detailedMessage = d;
url = u;
}
public String toString() {
return title.toUpperCase()+"\n"+
genericMessage+"\n"+
detailedMessage+"\n"+
"More info on this error: "+url+"\n";
}
public static class BadData extends DocumentedException {
public BadData(String details) {
super("Bad Data", "Supplied data is not valid", details, "http://www.example.com/moreinfo/baddata");
}
}
public static class MissingData extends DocumentedException {
public MissingData(String details) {
super("Missing Data", "Required data not found", details, "http://www.example.com/moreinfo/missingdata");
}
}
}
Which you can then call simply with:
throw new DocumentedException.BadData("Username cannot contain whitespace");
Of course, if you expected to need to warn against username errors regularly, you could create an additional class:
public static class BadUsername extends BadData {
public BadUsername() {
super("Usernames can only contain letters, numbers, and underscores");
}
}
The goal, again, is to explicitly define a hierarchy of exceptions that handle all cases you anticipate needing to deal with, such that you avoid repeatedly defining the same strings throughout your application. I personally like the group-exceptions-into-inner-classes pattern I used above, it lets you be very explicit with your errors without creating hundreds of silly stub java files you need to look through constantly. I would say that every major package should have an associated exception-holding class that defines all necessary exceptions for that package.
An alternative to your hybrid approach would be to put the error mapping in the exception itself. When MyException is initialised with Bad data add in the ErrorInfo that you've shown, but also provide a range of constructors for MyException that allows you to override or supplement the default definition of what Bad data means.
You could always have "MyException" as the superclass and have the specific types of errors as subtypes of that. In terms of error messages, you can use static constants on the subtypes to store the different types of errors.
E.g
Exception
-> MyException
-> BadDataException
-> InvalidUserException
etc.
Would be throw like so:
throw new BadDataException(BadDataException.DATA_TOO_LONG);

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.

Categories

Resources