/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.f1x.log.file;
import org.f1x.api.session.SessionID;
import org.f1x.log.LogFormatter;
import org.f1x.util.TimeSource;
import java.io.File;
import java.io.OutputStream;
import java.util.Calendar;
import java.util.TimeZone;
public class DailyFileMessageLog extends PeriodicFlushingMessageLog {
private final OutputStreamRollover dailyFileRoller;
/**
* @param logDir directory where log files will reside
*/
public DailyFileMessageLog(SessionID sessionID, File logDir, OutputStreamFactory streamFactory, LogFormatter formatter, TimeSource timeSource, TimeZone tz) {
super(null, formatter);
this.dailyFileRoller = new OutputStreamRollover(logDir, streamFactory, sessionID, timeSource, tz);
this.os = dailyFileRoller.createCurrentStream();
}
@Override
public void start(SessionID sessionID, TimeSource timeSource, int flushPeriod) {
super.start(sessionID, timeSource, flushPeriod);
this.dailyFileRoller.start();
}
@Override
public void close() {
dailyFileRoller.interrupt();
super.close();
}
private final class OutputStreamRollover extends Thread {
private final StringBuilder nextFileName = new StringBuilder();
private final Calendar calendar;
private final SessionID sessionID;
private final TimeSource timeSource;
private final File logDir;
private final OutputStreamFactory streamFactory;
OutputStreamRollover(File logDir, OutputStreamFactory streamFactory, SessionID sessionID, TimeSource timeSource, TimeZone tz) {
super("Daily roll for " + sessionID);
setDaemon(true);
setPriority(Thread.NORM_PRIORITY - 1);
this.streamFactory = streamFactory;
this.logDir = logDir;
this.sessionID = sessionID;
this.timeSource = timeSource;
this.calendar = getCurrentDayStart(timeSource, tz);
}
@Override
public void run() {
while (true) {
try {
calendar.add(Calendar.DAY_OF_MONTH, 1);
timeSource.sleep(Math.max(1, calendar.getTimeInMillis() - timeSource.currentTimeMillis()));
final OutputStream newStream = createCurrentStream();
final OutputStream oldStream = os;
synchronized (lock) {
DailyFileMessageLog.this.os = newStream;
}
onRollover();
safeClose(oldStream);
} catch (InterruptedException e) {
break;
} catch (Exception e) {
LOGGER.error().append("Error writing FIX log").append(e).commit();
}
}
}
private void generateCurrentFileName() {
nextFileName.setLength(0);
nextFileName.append (sessionID.getSenderCompId().toString());
nextFileName.append ('-');
nextFileName.append (sessionID.getTargetCompId().toString());
nextFileName.append ('-');
nextFileName.append(calendar.get(Calendar.YEAR));
appendPadZero(calendar.get(Calendar.MONTH) + 1);
appendPadZero(calendar.get(Calendar.DAY_OF_MONTH));
nextFileName.append(".log");
}
private void appendPadZero(int number) {
if (number < 10)
nextFileName.append('0');
nextFileName.append(number);
}
private OutputStream createCurrentStream() {
generateCurrentFileName();
File file = new File (logDir, nextFileName.toString());
return streamFactory.create(file);
}
}
protected void onRollover() {
// by default does nothing
}
private static Calendar getCurrentDayStart(TimeSource timeSource, TimeZone tz) {
// set time of day used to switch
Calendar calendar = Calendar.getInstance(tz);
calendar.setTimeInMillis(timeSource.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
if (calendar.getTimeInMillis() > System.currentTimeMillis())
calendar.add(Calendar.DAY_OF_MONTH, -1);
return calendar;
}
}