By Alumer M.

2018-10-11 14:35:32 8 Comments

I have an app for android that has a customized log format that I prefix with the Date for better searching. Currently I'm using SimpleDateFormat but the results are inconsistent.

A few examples of this inconsistency, it is giving me:

2018/10/11 10:40:21.229 ____Verbose:
2018/0010/0011 0010:40:21.230 ____Verbose:
2018/10/11 10:40:21.232 ____Verbose:
2018/10/11 10:40:42.784 ____Verbose:
2018/10/11 010:040:042.786 ____Verbose:
2018/10/11 10:40:42.786 ____Verbose:

The code I'm using is:

private static final DateFormat LOGDATEFULL = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
private static String strErrorLevel(int errorLevel){
    if (errorLevel == VERBOSE  ) return " ____ VERBOSE: ";
    if (errorLevel == DEBUG    ) return " ____ DEBUG  : ";
    if (errorLevel == INFO     ) return " _.._ INFO   : ";
    if (errorLevel == WARN     ) return " _--_ WARN   : ";
    if (errorLevel == ERROR    ) return " _!!_ ERROR  : ";
    if (errorLevel == ANALYTICS) return " _++_ ANALYTC: ";
    return                              " _**_ ASSERT : ";
public static void customLog(String message, int level){
    Date currDate = Calendar.getInstance().getTime();
    Log.d(LOGDATEFULL.format(currDate) + " " + strErrorLevel(level) + message);

My system Locale is Portuguese Brazilian (pt-BR), and this occurs both when called from the main thread and calling from other threads.

Is there any other solution besides creating a function to manually make the string?

By the way, it occurs even in generating names for files like 'Output_2018-010-11.txt.

Answer: It was really what Tom G and lelloman pointed, in all the places that I used SimpleDateFormat, this one was the only that I had, which might be accessed by more than one thread at a time. I chose Tom G answer for the practicality of implementing in the existing code.



@Tom G 2018-10-11 17:44:33

An alternative to this answer is to wrap the formatter in a ThreadLocal as so:

private static final ThreadLocal<DateFormat> LOGDATEFULL = new ThreadLocal<>() {
  @Override protected DateFormat initialValue() {
    return new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");

Then you would access it by calling .get().format() instead.

This lets you avoid the synchronization overhead of coordinating access to a single instance, without worrying about creating separate copies for separate threads manually.

@lelloman 2018-10-11 17:27:35

If you are using SimpleDateFormat instance from multiple thread there is a chance that you have a race condition there, and as it's been pointed out, that class is not thread-safe. From official docs:

Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

You should synchronize the access to that instance then:

public static void customLog(String message, int level){
    Date currDate = Calendar.getInstance().getTime();
    synchronized(LOGDATEFULL) {
        Log.d(LOGDATEFULL.format(currDate) + " " + strErrorLevel(level) + message);

@Tom G 2018-10-11 17:41:14

The alternative to synchronization is to wrap the formatter in a ThreadLocal.

Related Questions

Sponsored Content

Sponsored Content