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 free software, licensed under MIT license. See LICENSE
* for more information.
*
* 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: 2021275 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();
}
}
3 Answers 3
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.
-
\$\begingroup\$ Oh understood. Guessing I also shouldn't have the whole
boolean n
...n ? '\n' : ''
then? \$\endgroup\$CATboardBETA– CATboardBETA2021年05月29日 12:08:30 +00:00Commented May 29, 2021 at 12:08 -
\$\begingroup\$ Depends on what you want to achieve. In real life, you just grab an existing framework for logging and you're done. \$\endgroup\$mtj– mtj2021年05月29日 18:22:49 +00:00Commented May 29, 2021 at 18:22
-
\$\begingroup\$ Logging a primitive is trivial, since autoboxing was introduced in Java 1.5 \$\endgroup\$Mark Bluemel– Mark Bluemel2021年05月30日 09:02:19 +00:00Commented May 30, 2021 at 9:02
-
\$\begingroup\$ 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. \$\endgroup\$mtj– mtj2021年05月30日 11:33:17 +00:00Commented May 30, 2021 at 11:33
-
\$\begingroup\$ It will autobox to Object... See my answer. \$\endgroup\$Mark Bluemel– Mark Bluemel2021年05月30日 15:14:40 +00:00Commented May 30, 2021 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.
-
\$\begingroup\$ I read in another Stack Overflow post that if you don't have a domain you should use personal.xyzxyz \$\endgroup\$CATboardBETA– CATboardBETA2021年05月28日 15:58:43 +00:00Commented May 28, 2021 at 15:58
-
\$\begingroup\$ But you do have a GitHub account, right? So you probably already have a subdomain under
github.io
. \$\endgroup\$Reinderien– Reinderien2021年05月28日 16:01:59 +00:00Commented May 28, 2021 at 16:01 -
\$\begingroup\$ I don't have GitHub Pages, and this project is hosted on GitLab. \$\endgroup\$CATboardBETA– CATboardBETA2021年05月28日 16:02:01 +00:00Commented May 28, 2021 at 16:02
-
\$\begingroup\$ 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. \$\endgroup\$Reinderien– Reinderien2021年05月28日 16:03:42 +00:00Commented May 28, 2021 at 16:03
-
1\$\begingroup\$ 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. \$\endgroup\$Reinderien– Reinderien2021年05月28日 17:24:27 +00:00Commented May 28, 2021 at 17:24