Skip to content

Repository files navigation

Pattern : Expectation miss record

this is the java lib for Expectation miss record

The problem

Log analysis is one of the main daily tasks for the programmers. some scenario is that we find the point the error occurred by analyzing the log files, but we are not sure why the error occurred, what cause the error there. sometimes the crime scene is far away from the root cause, and we can't find out the root cause just from the log around the point the error occurred. so we have to walk up the log files carefully, sometimes even though we spend a lot of time to walk up the log files, we still can't find the root cause, just because there are not enough loges to confirm the root cause or just clear the suspicion of some condition.

Example

look at the below code:

classDataInfo
{
// ..... 
}
DataInfogetDataInfoFromOther(Otherso)
{
if (o != null && someConditionIsTrue(o))
{
returnnewDataInfo(o);
}
returnnull;
}
DataInfo2getDataInfo2(DataInfodataInfo)
{
if(dataInfo != null && someConditionIsTrue(dataInfo))
{
returnnewDataInfo2(dataInfo);
}
returnnull;
}
booleanhandleOthers2(Others2o2)
{
if(o2 != null)
{
//handle with o2returntrue;
}
returnfalse;
}
voiddoAction(Otherso, Others2o2)
{
DataInfo2dataInfo2= getDataInfo2(getDataInfoFromOther(o));
if(dataInfo2 != null && handleOthers2(o2))
{
showDialogWith(dataInfo2);
}
}

If the dialog wasn't showed in the user flow, what was the root cause , could you find out it from the code logic ? it could be the o is null or o is not satisfied the condition check, or the dataInfo is not satisfied the condition check or just the Others2 is null , etc. if it is the error occurred in the online product, there is no way to locate the root cause from the log files, you have to reproduce it yourself or add log than ask the customer to reproduce it and catch the loges again.

Solution1

there are already some pattern or principle for that annoyance, one of them is the Defensive programming or Fail fast , we can refine the above code like this :

classDataInfo
{
// ....
}
DataInfogetDataInfoFromOther(Otherso)
{
checkNotNull(o);
if (someConditionIsTrue(o))
{
returnnewDataInfo(o);
}
else
{
Log("some condition is not true of o");
}
returnnull;
}
DataInfo2getDataInfo2(DataInfodataInfo)
{
checkNotNull(dataInfo);
if(someConditionIsTrue(dataInfo))
{
returnnewDataInfo2(dataInfo);
}
else
{
Log("some condition is not true of dataInfo");
}
returnnull;
}
booleanhandleOhters2(Others2o2)
{
checkNotNull(o2);
// handle somethingreturntrue;
}
voiddoAction(Otherso, Others2o2)
{
DataInfo2dataInfo2= getDataInfo2(getDataInfoFromOther(o));
checkNotNull(dataInfo2);
if(handleOthers2(o2))
{
showDialogWith(dataInfo2);
}
}

The checkNotNull function is from the Condition class of google Guava lib. It throws the NPE if the parameter is null. now if the dialog didn't show, we could find the root cause by the log files immediately , because there is no NPE , the parameters mustn't be null, and then ,we can check the log files, which condition is not true.

but , the NPE is too roughly to the real products. sometimes, we only need record the exception but not the crash. we can write code like this :

Solution2

classDataInfo
{
// ....
}
DataInfogetDataInfoFromOther(Otherso)
{
if(o == null)
{
Log("o is null in getDataInfoFromOther");
returnnull;
}
if (someConditionIsTrue(o))
{
returnnewDataInfo(o);
}
else
{
Log("some condition is not true of o");
}
returnnull;
}
DataInfo2getDataInfo2(DataInfodataInfo)
{
if(dataInfo == null)
{
Log("dataInfo is null in getDataInfo2");
returnnull
}
if(someConditionIsTrue(dataInfo))
{
returnnewDataInfo2(dataInfo);
}
else
{
Log("some condition is not true of dataInfo");
}
returnnull;
}
booleanhandleOhters2(Others2o2)
{
if(o2 == null)
{
Log("o2 is null in handleOthers2");
returnfalse;
}
// handle somethingreturntrue;
}
voiddoAction(Otherso, Others2o2)
{
DataInfo2dataInfo2= getDataInfo2(getDataInfoFromOther(o));
if(dataInfo2 == null)
{
Log("dataInfo2 is null in doAction");
return;
}
if(handleOthers2(o2))
{
showDialogWith(dataInfo2);
}
}

It works, but tedious. all the programmers are lazy, we can make our life more easier. we can create the EMR lib for ourselves :

The Expectation miss record

importorg.junit.jupiter.api.BeforeEach;
importorg.junit.jupiter.api.Test;
importstaticindi.arrowyi.emr.Emr.*;
importstaticorg.junit.jupiter.api.Assertions.*;
classDataInfo
{
// ....
}
DataInfogetDataInfoFromOther(Otherso)
{
if (notNULL(o) && beTrue(someConditionCheck(o)))
{
returnnewDataInfo(o);
}
returnnull;
}
DataInfo2getDataInfo2(DataInfodataInfo)
{
if(notNull(dataInfo) && beTrue(someConditionIsTrue(dataInfo)))
{
returnnewDataInfo2(dataInfo);
}
returnnull;
}
booleanhandleOhters2(Others2o2)
{
if(notNull(o2)){
// handle somethingreturntrue;
}
returnfalse;
}
voiddoAction(Otherso, Others2o2)
{
if(notNull(getDataInfo2(getDataInfoFromOther(o)))
&& handleOthers2(o2)) {
showDialogWith(dataInfo2);
}
}

See, the world is fluency and the code is more readable than before.

If there is some status not satisfy the condition, we will never lose the info , and from the log, we will get the info like this :

shouldnotbenullExpectationmissrecord : atEmrTest.javaindi.arrowyi.emr.EmrTesttestBeTrue53

with what , we will locate the expectation miss immediately.

How to use

reference the lib

Gradle

Add it in your root build.gradle at the end of repositories:

allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}

Add the dependency

dependencies {
implementation 'com.github.Arrowyi:EMRJava:main-SNAPSHOT'
}

Maven

Add the JitPack repository to your build file

<repositories><repository><id>jitpack.io</id><url>https://jitpack.io</url></repository></repositories>

Add the dependency

<dependency><groupId>com.github.Arrowyi</groupId><artifactId>EMRJava</artifactId><version>main-SNAPSHOT</version></dependency>

The API

/** * Init log interface. * * @param logInterface the log interface */publicstaticvoidinitLogInterface(LogInterfacelogInterface)
/** * check if the parameter is null. * * @param o the object you want to check * @return the boolean , if o is not null return true , else false with outputting the log */publicstaticbooleannotNull(Objecto)
/** * check the parameter is true * * @param check value to be check * @return the boolean, true if the value is true, or false with outputting the log */publicstaticbooleanbeTrue(booleancheck)
/** * check the parameter is false * * @param check value to be check * @return the boolean, true if the value is false, or false with outputting the log */publicstaticbooleanbeFalse(booleancheck)
/** * check the value is null * * @param check the value to be check * @return the boolean, true if the value is null, or false with outputting the log */publicstaticbooleanbeNull(Objectcheck)

More usage you could ref to the test class , it is so simple. I wish it could make your lift easier.

The kotlin version lib for the expectation miss record please refer to EmrKotlin

About

Expectation miss record lib for java

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages