Java logger (of doom!)

I wrote my own logger, but I dislike how many times I overloaded the log() function. If anyone knows of another way of doing that, feel free to share.

/*
* Syml, a free and open source programming language.
* Copyright (C) 2021 William Nelson
* mailto: catboardbeta AT gmail DOT com
*
* Syml is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

package personal.williamnelson;

import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class Logger implements Closeable {

public File logfile = new File("");

protected FileOutputStream fos;
protected OutputStreamWriter osw;

public void init(File fileToLog) {
try {
logfile = fileToLog;

if (fileToLog.exists()) {
clearFile(fileToLog);
} else {
fileToLog.createNewFile();
}

} catch (IOException e) { //NOSONAR
System.out.println("An error occurred while creating a logfile. Are you sure that '" +
logfile.toString() +
"' is a valid filename?");
System.exit(1);
}

try {
fos = new FileOutputStream(fileToLog, false);
osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
clearFile(fileToLog);x
} catch (IOException e) {
System.out.println("An unrecognized error occurred while start logging stream and " +
"writing to log stream, despite passing all tests.");
}

}

public void log(String s, boolean n) throws IOException {
osw.write(getTime() + '\n' + s + (n ? '\n' : ""));
}

public void log(char c, boolean n) throws IOException {
osw.write(getTime() + '\n' + c + (n ? '\n' : ""));
}

public void log(File f, boolean n) throws IOException {
osw.write(getTime() + '\n' + f.getAbsolutePath() + (n ? '\n' : ""));
}

public void log(StringBuilder sb, boolean n) throws IOException {
osw.write(getTime() + '\n' + sb.toString() + (n ? '\n' : ""));
}

public void log(int i, boolean n) throws IOException {
osw.write(getTime() + '\n' + i + (n ? '\n' : ""));
}

public void log(Integer i, boolean n) throws IOException {
osw.write(getTime() + '\n' + i + (n ? '\n' : ""));
}

public void log(float f, boolean n) throws IOException {
osw.write(getTime() + '\n' + f + (n ? '\n' : ""));
}

public void log(double d, boolean n) throws IOException {
osw.write(getTime() + '\n' + d + (n ? '\n' : ""));
}

public void log(long l, boolean n) throws IOException {
osw.write(getTime() + '\n' + l + (n ? '\n' : ""));
}

public void log(short s, boolean n) throws IOException {
osw.write(getTime() + '\n' + s + (n ? '\n' : ""));
}

public void log(boolean b, boolean n) throws IOException {
osw.write(getTime() + '\n' + (b ? "true" : "false") + (n ? '\n' : ""));
}

public void log(byte b, boolean n) throws IOException {
osw.write(getTime() + '\n' + b + (n ? '\n' : ""));
}

private String getTime() {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); // example: 2021/27/5 12:07:23
LocalDateTime ldtNow = LocalDateTime.now();
return ldtNow.format(dtf);
}

private void clearFile(File file) throws IOException {
OutputStreamWriter oswClearer = new OutputStreamWriter(fos);
oswClearer.write("");
oswClearer.close();
}

public void close() throws IOException {
osw.close();
fos.close();
}
}


Auto-boxing has been around long enough that I think you only need one log method. Even here I'd avoid single letter names.

  public void log(Object toLog, Boolean newLine) {
osw.write(getTime() + '\n' + String.valueOf(toLog) + (newLine? '\n' : ""));
}


You don't need to instantiate a date formatter each time you get the time, do you?

You don't need to create or truncate the file explicitly.

Closing the OutputStreamWriter will close the FileOutputStream it wraps, so you needn't close the latter yourself.

I'd prefer longer, more expressive field names than "osw", personally.

Your logging format seems to involve a lot of newlines. Do you really need them after the date/time stamp or would some other delimiter work as well? Also, you would find it hard to distinguish an empty string from all spaces. Should you consider putting delimiters around the logged value?

Finally, as already mentioned, you may want to consider using one of the plethora of existing loggers rather than writing your own.

If you have a look at all the existing logging frameworks out there, you will notice that they basically only log Strings. This way, every decendant of Object (which will be auto converted using the toString() method) can be logged.

Granted, this does not work for primitives, so if you really really want to log int, short, etc., there will not be another solution.

But, usually you don't do this. In more than two decades of full-time java development, I never had the desire to log just a primitive. You simply don't do:

log(i); // current iteration


in reality, this will rather be something like

log("current iteration: " + i);


which will be a String again.

Therefore, I think you wrote a lot of code to solve a problem that does not exist.

• Oh understood. Guessing I also shouldn't have the whole boolean n ... n ? '\n' : '' then? May 29 at 12:08
• Depends on what you want to achieve. In real life, you just grab an existing framework for logging and you're done.
– mtj
May 29 at 18:22
• Logging a primitive is trivial, since autoboxing was introduced in Java 1.5 May 30 at 9:02
• log(int) will not autobox to log(Integer) to fit log(String). You'd have to convert to Integer manually, or declare an alternative method with the primitive argument.
– mtj
May 30 at 11:33
• It will autobox to Object... See my answer. May 30 at 15:14

Minor, but: by convention package paths are supposed to be reverse domain traversals, so

personal.williamnelson


would actually be

io.github.catboardbeta


Said another way, "personal" is not a top-level domain.

• I read in another Stack Overflow post that if you don't have a domain you should use personal.xyzxyz May 28 at 15:58
• But you do have a GitHub account, right? So you probably already have a subdomain under github.io. May 28 at 16:01
• I don't have GitHub Pages, and this project is hosted on GitLab. May 28 at 16:02
• The package nomenclature is less about hosting and more about unique namespace identification. Even if you haven't initialized your Pages content, the subdomain is still assigned, and so any Java packages you attribute to that namespace are conceptually yours. May 28 at 16:03
• Like it or not, personal. is not a convention that most people follow. Refer stackoverflow.com/questions/292169/… to see recommendations that are all over the map. May 28 at 17:24