יום חמישי, 7 ביולי 2016

Logging in Kotlin

The post was moved to: https://medium.com/@OhadShai/logging-in-kotlin-95a4e76388f2#.oiwog6cyw

TL;DR - for logging in kotlin use https://github.com/MicroUtils/kotlin-logging

In this blog post I am going to discuss logging frameworks for Kotlin language and JVM environment. Actually, logging seems like a solved problem in the JVM world. The de-facto standard is slf4j and there are plenty of examples in the internet like that:

private static final Logger logger = LoggerFactory.getLogger(ThisClass.class);
...
logger.info("Hi, {}", name);

That works perfectly in Kotlin with the relevant syntax adjustment

val logger = LoggerFactory.getLogger(ThisClass::class)
...
logger.info("Hi, {}", name)

So why not use it as is???


Yes, that is a valid option. However, Kotlin tries to improve readability of the code by making it more concise and fluent. I addition removing boilerplates (ie: copy-paste code) also makes the code cleaner and less error prone.

Is it a real problem or did I just made it up for that blog post?


There are evidences it is a real problem, or at least people are talking about it:

In addition, other JVM languages also tackled that problem.
In Scala we have the StrictLogging trait that exposes a logger member, thus helps avoiding defining the logger name.
In addition, lazy evaluation of the message in Scala assists in avoiding the notorious isDebugEnabled() call before each debug logging to avoid expensive string concatenation and values evaluation.

Is there something similar in Kotlin?


Yes there is. I have created kotlin-logging.
To use it add maven dependency:
<dependency>
  <groupId>io.github.microutils</groupId>
  <artifactId>kotlin-logging</artifactId>
  <version>1.3.2</version>
</dependency>
Then it is possible to get all the benefits like in Scala, but in kotlin using a companion object (unlike Scala where you can define properties in an interface).
The usage is to have the Companion object extends KLogging() and using the logger member in the class like that:
companion object: KLogging()
Then using the logger:
logger.info("hello world")
For sequences that are expected to be frequently used prefer lazy evaluated messages:
logger.debug{"lazy evaluated $message"}
In the above example String is inside a method and gets evaluated only if debug log level is enabled at runtime.

So, what are the benefits over the slf4j original implementation:

  • Getting the logger without the class name itself (excellent for copy paste)
  • Much cleaner messages with String templates and lazy evaluation
  • That's it - it is a small lib!

Are there any other alternatives? Yes there are quite a few:
  • Using plain slf4j
  • Many libraries, with not much traction at the moment
  • Implement it yourself

Hope you enjoy and spread the word of kotlin.logging.
Getting involved and support:



יום שלישי, 26 באפריל 2016

computeIfAbsent() is your new friend for Map in Java

I had a case today I had to populate a map with that signature:

Map<Long, Map<Integer, Integer>> myMap = Maps.newHashMap();

The way I populate it is like that:


resultSet -> {
  resultSet.beforeFirst();
  while (resultSet.next()) {
    Integer count = ...;
    Integer reason = ...;
    Long id = ...;
    //now I have to add the values to myMap
  }
})

prior to Java 8 this is one way to do it:

Map<Integer, Integer> internalMap = myMap.get(id);
if (internalMap == null) {
  internalMap = Maps.newHashMap();
  myMap.put(id, internalMap);
}
internalMap.put(reason, count);


It is a boilerplate oriented code, did it many times in the past. Now with Java 8 it is possible to do something simpler:
How about that? (Hint: Not working)

myMap.getOrDefault(id, Maps.newHashMap()).put(reason, count);

Any guess what's the mistake here?
getOrDefault just returns another value, do not put it on the map, so myMap remains empty.

Another try:

myMap.putIfAbsent(id, Maps.newHashMap()).put(reason, count);

And here I got NullPointerException. Apparently the Java api implementor decided to keep it similar to Map.put and return the previous value. Not a good decision if you ask me since this method should help you prevent working with nulls.

Finally the solution:

myMap.computeIfAbsent(id, key -> Maps.newHashMap()).put(reason, count);

Unlike putIfAbsent, here the api returns the existing value or the new value, allowing you to continue working on it more fluently.

יום רביעי, 30 במרץ 2016

חוק הרמזור הקצר\המתחלף


והפעם קצת מתמטיקה שימושית.

המון פעמים קורה לי שאני עומד ברמזור קצר וצריך לחכות כמה סבבים של ירוק עד שאוכל לעבור.
שמתי לב לעובדה המעניינת הבאה: אם כל המכוניות שלפניי בטור מתחילות לנסוע באור הירוק הנוכחי לפני שהרמזור מתחלף לאדום, ואז הרמזור מתחלף לאדום אני אוכל להגיד בוודאות שאני אעבור את הצומת בפעם הבאה שהרמזור יהפוך לירוק.

לא חשבתי על זה הרבה זמן או באופן רציף, רק כשהייתי נתקע ברמזור קצר או עמוס, אבל תמיד סיקרן אותי אם יש לזה הסבר מתימטי ואם זה באמת ״חוק טבע״.
היום נראה לי שמצאתי הסבר מניח את הדעת למה זה קורה, לפחות מבחינה תיאורטית.

כאשר המכוניות במצב עמידה ונניח שכל המכוניות הן פרטיות באורך עד 5 מטר והן שומרות ביניהן על מרחק מינימלי נניח מטר אחד.
במצב של נסיעה איטית המרחק בין המכוניות גדל ומוכפל כך שאם במצב עמידה המרחק בין החלק הקדמי של מכונית לחלק הקדמי של המכונית אחריה הוא 6 מטר, בנסיעה איטית המרחק ביניהן הוא לפחות 12 מטר.
זאת אומרת שאורך טור המכוניות שלפני הוכפל.
מכיוון שאורך הטור שלפני הוכפל והמרחק לרמזור נשאר קבוע זה אומר שחצי מהמכוניות שלפני עברו את הרמזור, כך שאם כל הנתונים יישארו זהים ברמזור הירקו הבא אוכל לעבור את הצומת. אחחח......

כמובן שבעולם האמיתי יש משאיות, אוטובוסים, אנשים שנרדמו או מתעסקים עם הווטסאפ. אבל למה לבלבל עם העובדות.

תארו לכם עולם שבו כאשר הרמזור היה מתחלף לירוק כל המכוניות היו מתחילות לנסוע מיידית בתיאום - מאיצות בדיוק באותו קצב ושומרות על המרחק שהיה בינהן במנוחה. בעולם כזה פי שתיים מכוניות היו מצליחות לעבור בכל רמזור ירוק... זה היה פותר הרבה עומסי תנועה! עכשיו רק צריך להמציא מכשיר שייאפשר את זה או להכניס את זה לאלגוריתם של הרכב של גוגל. אני זוכר במעורפל שפעם ראיתי תוכנית (נראה לי זה היה תצפית עם יעל דן) שבה תכננו שמכוניות ייסעו אוטונומית בטורים ארוכים אחרי משאיות. לא שראיתי את זה קורה אבל בטח אפשר להתבסס על אותה טכנולוגיה.