2

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.

Thanks

Alumer M.
  • 23
  • 3
  • What SimpleDateFormat are you using, the `java.text` or `android.icu.text` one? I've used that exact format with `java.text.SimpleDateFormat` many times and never saw this weird thing :\ – lelloman Oct 11 '18 at 14:51
  • Do you use multiple thread or something like an AsyncTask? If yes you may simple run into the problem that SimpleDateFormat is not thread safe. – Robert Oct 11 '18 at 15:15
  • Yes I used the java.text.SimpleDateFormat and the date from java.util.Date as the parameter. Yes I have threads running, there's a bug in the Android Phone I'm using that if I don't call a startActivity for a new app on a separate thread it the OS simply won't call. – Alumer M. Oct 11 '18 at 16:53
  • As an aside consider throwing away the long outmoded and notoriously troublesome `SimpleDateFormat` and friends, and adding [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) to your Android project in order to use `java.time`, the modern Java date and time API. It is so much nicer to work with. – Ole V.V. Oct 12 '18 at 07:30

2 Answers2

1

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);
    }
}
lelloman
  • 12,742
  • 5
  • 55
  • 75
0

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.

Tom G
  • 11,950
  • 2
  • 38
  • 54