I have a class method that logs output to a file and I want more control over format specifying.
public class Logger {
private static boolean FIRST_CALL = true;
public static void log(String content) {
try {
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("debug.txt", !FIRST_CALL)));
if(FIRST_CALL) {
FIRST_CALL = false;
}
out.println(content);
out.close();
} catch (IOException e) {
//exception handling left as an exercise for the reader
}
}
}
I'm not sure how to go about it. changing println to printf creates a whole host of problems.
E.g I call the method as follows:
Logger.log("testval=" + testVal);
Where testVal would be a double for example. It would output 1.9547E-5 but I actually want it in decimal format instead.
Any help appreciated. I'm new to Java.
Take a look at the MessageFormat class.
You can do something like
Logger.log(MessageFormat.format("testval={0}", testVal));
Like printf you can configure the format of your output. See the Javadoc for all the options.
You can use String.format().
In your example:
Logger.log(String.format("testval=%f", testVal));
Well, if to fix your code for a double, I'd write in this way:
private void log(double )
{
// use try-with-resource - it's better
try(PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("debug.txt", !FIRST_CALL))))
{
if(FIRST_CALL) {
FIRST_CALL = false;
}
out.printf("value = %5.7f\n", d);
out.close();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
and call this code:
log(1.9547E-5)
Related
I am working on a debuging csv output inside an event driven java application. I define my filewriter like this on init.
public File csvFile;
public FileWriter fileWriter;
then I initialies them
this.csvFile = new File("c:\\missingitems.csv");
this.fileWriter = null;
try {
this.fileWriter = new FileWriter(this.csvFile);
StringBuilder line = new StringBuilder();
line.append("Date, ItemId");
line.append("\n");
this.fileWriter.write(line.toString());
} catch (IOException e) {
e.printStackTrace();
}
then within my actual program logic this gets called for every timestep in my data
for(Long item : this.items) {
StringBuilder line = new StringBuilder();
line.append(event.getDateTime().toLocalDate().toString() + "," +item.intValue());
line.append("\n");
try {
this.fileWriter.write(line.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
on exit of my program I call
try {
this.fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
However this seems to be only working if i close the filewriter after each append. Is there a way of keeping the file open and just append to it, I would also like to not loose my data in case my application crashes. I am a python guy and not super familiar with java.
I have PVPStats objects stored in PlayerMeta.java:
public static Map <UUID, PVPstats> sPVPStats = new HashMap<>();
I know for sure the map is getting populated with objects that contain the expected vars for each uuid.
I'm trying to write these objects (converted to single lines of strings) into plugins/core/killstats.txt when the server calls onDisable() in Main.java
Along with the Map object, in PlayerMeta.java are also the methods to update and retrieve PVPStats objects from the Map. Those are all working.
The part that is not is working is the write method:
public static void writePVPStats() throws IOException {
BufferedWriter w = new BufferedWriter(new FileWriter("plugins/core/killstats.txt"));
sPVPStats.keySet().forEach(user -> {
try {
System.out.println(sPVPStats);
// stdout = {a6b6e3a1-a1ec-4fee-9d6d-f5e495c3e9d7=a6b6e3a1-a1ec-4fee-9d6d-f5e495c3e9d7:1:0}
w.write(user.toString() + "\n");
w.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
w.close();
}
kill.txt after onDisable() is done:
a6b6e3a1-a1ec-4fee-9d6d-f5e495c3e9d7
Instead it needs to be:
{a6b6e3a1-a1ec-4fee-9d6d-f5e495c3e9d7=a6b6e3a1-a1ec-4fee-9d6d-f5e495c3e9d7:1:0}
For reference, here is the complete PVPStats class.
Lastly, in case it matters / helps, the reader on server launch:
Files.readAllLines(killstats_user_database.toPath()).forEach(line -> {
PVPstats stats = PVPstats.fromString(line);
PlayerMeta.sPVPStats.put(stats.playerid, stats);
});
Source Code:
backend.FileManager.java
backend.PlayerMeta.java
backend.PVPstats.java
events.PVP.java
EDIT
I just tried this with killstats.txt file type nad killstats.txt doesnt have anything in it now.
public static void writePVPStats() throws IOException {
BufferedWriter w = new BufferedWriter(new FileWriter("plugins/core/killstats.txt"));
for (PVPstats object: sPVPStats.values()) {
try {
System.out.println(sPVPStats);
w.write(object.toString() + "\n");
w.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
w.close();
}
Ok so there were multiple issues. I wasn't controlling the flushing of the buffer, I declared the hasmap incorrectly, I wasn't accessing the value part of the hash map, and I wasn't correctly enforcing plain text.
SOLUTION
public static Map <UUID, PVPstats> sPVPStats = new HashMap<UUID, PVPstats>();
public static void writePVPStats() throws IOException {
BufferedWriter w = new BufferedWriter(new FileWriter("plugins/core/killstats.txt"));
for (PVPstats object: sPVPStats.values()) {
try {
System.out.println(sPVPStats);
System.out.println(object.toString());
w.write(object.toString() + "\n");
w.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
w.close();
}
I'm looking for a way to pass a code block to a method, which will then perform actions on other arguments passed to method, take the results of those actions, and pass those results to the code block passed to the method. For clarity:
private static void method1(String filename, int sheetNum) {
runOnSheet(filename, () -> {
doStuffWithStream(FileInputStream fileStream); // Does something with a file stream
doOtherStuffWithStream(FileInputStream fileStream); // Does something else with a file stream
});
}
// Elsewhere
private static void runOnFile(String fileName, Runnable block1) {
try {
fileStream = new FileInputStream(fileName);
} catch (IOException e) {
e.printStackTrace();
}
block1.run(); // I'd like to pass fileStream to this code block. Ideally i could do block1.run(fileStream );
fileStream.close();
}
I want to be able to reuse runOnFile anywhere I need to open a file, run some code on the stream, and close the stream.
What I actually want to do is more complicated, and uses other libraries in addition to FileInputStream, but the structure of what I wish to accomplish is the same.
Thanks for any help!
Java 8+ has a Class called Consumer that can be used for your usecase:
https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html
private static void method1(String filename, int sheetNum) {
runOnFile(filename, (fileStream) -> {
doStuffWithStream(fileStream);
doOtherStuffWithStream(fileStream);
});
}
// Elsewhere
private static void runOnFile(String fileName, Consumer<FileInputStream> block1) {
try {
fileStream = new FileInputStream(fileName);
} catch (IOException e) {
e.printStackTrace();
}
block1.accept(fileStrean);
fileStream.close();
}
EDIT: As suggested by Dimitri using the try-with-resource syntax:
// Elsewhere
private static void runOnFile(String fileName, Consumer<FileInputStream> block1) {
try (FileInputStream fis = new FileInputStream(fileName)) {
block1.accept(fis);
} catch (IOException e) {
e.printStackTrace();
}
}
Try something like this:
private static void method1(String filename, int sheetNum)
{
try ( final FileInputStream fileStream = new FileInputStream(filename))
{
runOnSheet(filename, () ->
{
doStuffWithStream(fileStream); // Does something with a file stream
doOtherStuffWithStream(fileStream); // Does something else with a file stream
});
}
}
I have a Problem with commons-net FTPClient. If I download a file from my ftp server wirth retrieveFileStream() it works, but I get the result '150 Opening BINARY mode data connection for ...'. If I call noop() I get '226 Transfer complete' as result. For every following operation I get the result of the prvious operation.
I found out, that FTPClient reads results until end of line, if there are two result lines (as after retrieveFileStream()), I get the second one after the next command. I did a workaround by overriding FTPClient.retrieveFileStream() like this:
public static void main(String[] args){
try {
MyFTPClient ftpClient = new MyFTPClient();
try {
ftpClient.connect(ftphost, 21);
if(!ftpClient.login( ftpuser, ftppassword )){
throw new RuntimeException(ftpClient.getReplyString());
}
if(!ftpClient.changeWorkingDirectory("in")){
throw new RuntimeException(ftpClient.getReplyString());
}
FTPFile[] files = ftpClient.listFiles();
for(FTPFile file: files){
if(file.getName().startsWith(FILENAME) && (file.getType() == FTPFile.FILE_TYPE)){
InputStream in = ftpClient.retrieveFileStream(file.getName());
CsvFile csvFile = new CsvFile(in, "ISO-8859-1", ';', "yyyyMMdd", "#.00", Locale.US, false);
in.close();
in = null;
System.out.println(ftpClient.getReplyString());
System.out.println(ftpClient.readLine());
System.out.println(ftpClient.rnfr(file.getName()));
System.out.println(ftpClient.getReplyString());
System.out.println(ftpClient.rnto("x" + file.getName()));
System.out.println(ftpClient.getReplyString());
}
}
if(!ftpClient.logout()){
throw new RuntimeException(ftpClient.getReplyString());
}
} finally {
ftpClient.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static class MyFTPClient extends FTPClient{
public String readLine(){
try {
__getReplyNoReport();
} catch (IOException e) {
}
return getReplyString();
}
}
The call of the method readLine() gets me the additional Line of result.
But is this a bug of FTPClient or is it a problem of my ftp-server? The Problem of that workaround is, that the method blocks, if there is only one line of response.
Thanx for your help
Stephan
Sometimes it helps, reading the manual. A call of completePendingCommand() works
I don't know if my mind just fools me or this is really not working.
I need different type of Logging-classes so I created a abstract-class, the only definition that all classes will have the same is the way the writeToLog is handled:
public abstract class LoggerTemplate {
protected String filename ="log/";
protected File logfile;
protected FileWriter fw;
public void writeToLog(String message) {
if(fw != null) {
try {
message = new SimpleDateFormat("dd-MM-hh:mm").format(new Date()) + " " + message;
fw.write(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The concrete sub-classes will implement rest of the logic in their constructor, ie one of them:
public class JitterBufferLogger extends LoggerTemplate {
public JitterBufferLogger() {
super();
filename += new SimpleDateFormat("yyyyddMMhhmm'.log'").format(new Date());
if(!new File("log/").exists())
new File("log").mkdir();
logfile = new File(filename);
try {
logfile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
try {
fw = new FileWriter(logfile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
But when I debug I can see that when calling the writeToLog for a specific logger, it jumps into the LoggerTemplate method, and therefore fw and logfile are null. So it's not working.
Isn't it supposed to work or do I just mess something a bit up and should go into weekend ;-)
It should work, it is normal, that the debugger stepped into the LoggerTemplate class upon entering the writeToLog() method. What is strange that the attributes in the base class have null values.
I have tested your code with the following short test program:
public class Test {
public static void main(String[] args) {
LoggerTemplate lt = new JitterBufferLogger();
lt.writeToLog("Hello");
}
}
After adding fw.flush() to the LoggerTemplate.writeToLog() method just after the fw.write() call, it worked for me, the log file had been created and it contained the log message.
Maybe the new File("log").mkdir() or some other calls throw an exception which you cannot see, because stderr had been redirected somewhere.
So what may be missing?
- filewriter flushing could have helped.
- I can't reproduce the null values with the original code, don't know what happened.
- but as everybody, including me, said: it should work and it does.
Why was nothing in the logfile?
- maybe the flush of fw was missing..
anyhow I wrapped it with a Printwriter:
public abstract class LoggerTemplate {
protected String filename ="log/";
protected File logfile;
protected PrintWriter pw;
public void writeToLog(String message) {
try {
pw = new PrintWriter(new FileWriter(logfile,true));
message = new SimpleDateFormat("dd-MM-hh:mm").format(new Date()) + " " + message + "\n";
pw.write(message);
pw.flush();
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
and now it's working like it should and was expected to be.
Note that the fw instantiation in the concrete sub-classes is not needed anymore.