/*******************************************************************************
* Copyright (c) quickfixengine.org All rights reserved.
*
* This file is part of the QuickFIX FIX Engine
*
* This file may be distributed under the terms of the quickfixengine.org
* license as defined by quickfixengine.org and appearing in the file
* LICENSE included in the packaging of this file.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
* THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE.
*
* See http://www.quickfixengine.org/LICENSE for licensing information.
*
* Contact ask@quickfixengine.org if any conditions of this licensing
* are not clear to you.
******************************************************************************/
package quickfix;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import org.quickfixj.CharsetSupport;
import quickfix.field.converter.UtcTimestampConverter;
/**
* File log implementation. THIS CLASS IS PUBLIC ONLY TO MAINTAIN COMPATIBILITY
* WITH THE QUICKFIX JNI. IT SHOULD ONLY BE CREATED USING A FACTORY.
*
* @see quickfix.FileLogFactory
*/
public class FileLog extends AbstractLog {
private static final byte[] TIME_STAMP_DELIMITER;
static {
try {
TIME_STAMP_DELIMITER = ": ".getBytes(CharsetSupport.getCharset());
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
private String messagesFileName;
private String eventFileName;
private boolean syncAfterWrite;
private FileOutputStream messages;
private FileOutputStream events;
private boolean includeMillis;
private boolean includeTimestampForMessages;
FileLog(String path, SessionID sessionID, boolean includeMillis, boolean includeTimestampForMessages, boolean logHeartbeats) throws FileNotFoundException {
String sessionName = FileUtil.sessionIdFileName(sessionID);
setLogHeartbeats(logHeartbeats);
String prefix = FileUtil.fileAppendPath(path, sessionName + ".");
messagesFileName = prefix + "messages.log";
eventFileName = prefix + "event.log";
File directory = new File(messagesFileName).getParentFile();
if (!directory.exists()) {
directory.mkdirs();
}
this.includeMillis = includeMillis;
this.includeTimestampForMessages = includeTimestampForMessages;
openLogStreams(true);
}
private void openLogStreams(boolean append) throws FileNotFoundException {
messages = new FileOutputStream(messagesFileName, append);
events = new FileOutputStream(eventFileName, append);
}
protected void logIncoming(String message) {
writeMessage(messages, message, false);
}
protected void logOutgoing(String message) {
writeMessage(messages, message, false);
}
private void writeMessage(FileOutputStream stream, String message, boolean forceTimestamp) {
try {
if (forceTimestamp || includeTimestampForMessages) {
writeTimeStamp(stream);
}
stream.write(message.getBytes(CharsetSupport.getCharset()));
stream.write('\n');
stream.flush();
if (syncAfterWrite) {
stream.getFD().sync();
}
} catch (IOException e) {
//QFJ-459: no point trying to log the error in the file if we had an IOException
//we will end up with a java.lang.StackOverflowError
System.err.println("error writing message to log : "+message);
e.printStackTrace(System.err);
}
}
public void onEvent(String message) {
writeMessage(events, message, true);
}
public void onErrorEvent(String message) {
writeMessage(events, message, true);
}
private void writeTimeStamp(OutputStream out) throws IOException {
String formattedTime = UtcTimestampConverter.convert(SystemTime.getDate(), includeMillis);
out.write(formattedTime.getBytes(CharsetSupport.getCharset()));
out.write(TIME_STAMP_DELIMITER);
}
String getEventFileName() {
return eventFileName;
}
String getMessagesFileName() {
return messagesFileName;
}
public void setSyncAfterWrite(boolean syncAfterWrite) {
this.syncAfterWrite = syncAfterWrite;
}
/**
* Closes the messages and events files.
*
* @deprecated Use close instead.
* @throws IOException
*/
public void closeFiles() throws IOException {
close();
}
/**
* Closed the messages and events files.
* @throws IOException
*/
@Override
public void close() throws IOException {
messages.close();
events.close();
}
/**
* Deletes the log files. Do not perform any log operations while performing
* this operation.
*/
public void clear() {
try {
closeFiles();
openLogStreams(false);
} catch (IOException e) {
System.err.println("Could not clear log: "+getClass().getName());
}
}
}