diff --git a/core/java/src/net/i2p/stat/BufferedStatLog.java b/core/java/src/net/i2p/stat/BufferedStatLog.java index a41ef869936721926e5b59c738c247c91072fe93..fa131b9a3c4442a0329256e8d3f560324984b71c 100644 --- a/core/java/src/net/i2p/stat/BufferedStatLog.java +++ b/core/java/src/net/i2p/stat/BufferedStatLog.java @@ -12,24 +12,34 @@ import java.util.StringTokenizer; import net.i2p.I2PAppContext; import net.i2p.util.I2PThread; +import net.i2p.util.Log; /** * */ public class BufferedStatLog implements StatLog { private I2PAppContext _context; - private List _events; + private Log _log; + private StatEvent _events[]; + private int _eventNext; + private int _lastWrite; /** flush stat events to disk after this many events (or 30s)*/ private int _flushFrequency; private List _statFilters; + private String _lastFilters; private BufferedWriter _out; private String _outFile; public BufferedStatLog(I2PAppContext ctx) { _context = ctx; - _events = new ArrayList(1000); + _log = ctx.logManager().getLog(BufferedStatLog.class); + _events = new StatEvent[1000]; + for (int i = 0; i < 1000; i++) + _events[i] = new StatEvent(); + _eventNext = 0; + _lastWrite = _events.length-1; _statFilters = new ArrayList(10); - _flushFrequency = 1000; + _flushFrequency = 500; I2PThread writer = new I2PThread(new StatLogWriter(), "StatLogWriter"); writer.setDaemon(true); writer.start(); @@ -37,9 +47,22 @@ public class BufferedStatLog implements StatLog { public void addData(String scope, String stat, long value, long duration) { synchronized (_events) { - _events.add(new StatEvent(scope, stat, value, duration)); - if (_events.size() > _flushFrequency) - _events.notifyAll(); + _events[_eventNext].init(scope, stat, value, duration); + _eventNext = (_eventNext + 1) % _events.length; + + if (_eventNext == _lastWrite) + _lastWrite = (_lastWrite + 1) % _events.length; // drop an event + + if (_log.shouldLog(Log.DEBUG)) + _log.debug("AddData next=" + _eventNext + " lastWrite=" + _lastWrite); + + if (_eventNext > _lastWrite) { + if (_eventNext - _lastWrite >= _flushFrequency) + _events.notifyAll(); + } else { + if (_events.length - 1 - _lastWrite + _eventNext >= _flushFrequency) + _events.notifyAll(); + } } } @@ -52,12 +75,17 @@ public class BufferedStatLog implements StatLog { private void updateFilters() { String val = _context.getProperty("stat.logFilters"); if (val != null) { - StringTokenizer tok = new StringTokenizer(val, ","); - synchronized (_statFilters) { - _statFilters.clear(); - while (tok.hasMoreTokens()) - _statFilters.add(tok.nextToken().trim()); + if ( (_lastFilters != null) && (_lastFilters.equals(val)) ) { + // noop + } else { + StringTokenizer tok = new StringTokenizer(val, ","); + synchronized (_statFilters) { + _statFilters.clear(); + while (tok.hasMoreTokens()) + _statFilters.add(tok.nextToken().trim()); + } } + _lastFilters = val; } else { synchronized (_statFilters) { _statFilters.clear(); } } @@ -79,45 +107,58 @@ public class BufferedStatLog implements StatLog { private class StatLogWriter implements Runnable { private SimpleDateFormat _fmt = new SimpleDateFormat("yyyyMMdd HH:mm:ss.SSS"); public void run() { - List cur = new ArrayList(1000); + int writeStart = -1; + int writeEnd = -1; while (true) { synchronized (_events) { - if (_events.size() < _flushFrequency) { - try { _events.wait(30*1000); } catch (InterruptedException ie) {} - } - cur.addAll(_events); - _events.clear(); + if (_eventNext > _lastWrite) { + if (_eventNext - _lastWrite < _flushFrequency) + try { _events.wait(30*1000); } catch (InterruptedException ie) {} + } else { + if (_events.length - 1 - _lastWrite + _eventNext < _flushFrequency) + try { _events.wait(30*1000); } catch (InterruptedException ie) {} + } + writeStart = (_lastWrite + 1) % _events.length; + writeEnd = _eventNext; + _lastWrite = (writeEnd == 0 ? _events.length-1 : writeEnd - 1); } - if (cur.size() > 0) { - writeEvents(cur); - cur.clear(); + if (writeStart != writeEnd) { + try { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("writing " + writeStart +"->"+ writeEnd); + writeEvents(writeStart, writeEnd); + } catch (Exception e) { + _log.error("error writing " + writeStart +"->"+ writeEnd, e); + } } } } - private void writeEvents(List events) { + private void writeEvents(int start, int end) { try { updateFilters(); - for (int i = 0; i < events.size(); i++) { - StatEvent evt = (StatEvent)events.get(i); - if (!shouldLog(evt.getStat())) continue; - String when = null; - synchronized (_fmt) { - when = _fmt.format(new Date(evt.getTime())); + int cur = start; + while (cur != end) { + if (shouldLog(_events[cur].getStat())) { + String when = null; + synchronized (_fmt) { + when = _fmt.format(new Date(_events[cur].getTime())); + } + _out.write(when); + _out.write(" "); + if (_events[cur].getScope() == null) + _out.write("noScope "); + else + _out.write(_events[cur].getScope() + " "); + _out.write(_events[cur].getStat()+" "); + _out.write(_events[cur].getValue()+" "); + _out.write(_events[cur].getDuration()+"\n"); } - _out.write(when); - _out.write(" "); - if (evt.getScope() == null) - _out.write("noScope "); - else - _out.write(evt.getScope() + " "); - _out.write(evt.getStat()+" "); - _out.write(evt.getValue()+" "); - _out.write(evt.getDuration()+"\n"); + cur = (cur + 1) % _events.length; } _out.flush(); } catch (IOException ioe) { - ioe.printStackTrace(); + _log.error("Error writing out", ioe); } } } @@ -129,18 +170,18 @@ public class BufferedStatLog implements StatLog { private long _value; private long _duration; - public StatEvent(String scope, String stat, long value, long duration) { + public long getTime() { return _time; } + public String getScope() { return _scope; } + public String getStat() { return _stat; } + public long getValue() { return _value; } + public long getDuration() { return _duration; } + + public void init(String scope, String stat, long value, long duration) { _scope = scope; _stat = stat; _value = value; _duration = duration; _time = _context.clock().now(); } - - public long getTime() { return _time; } - public String getScope() { return _scope; } - public String getStat() { return _stat; } - public long getValue() { return _value; } - public long getDuration() { return _duration; } } }