diff options
Diffstat (limited to 'plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/log/RollingLog.java')
-rw-r--r-- | plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/log/RollingLog.java | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/log/RollingLog.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/log/RollingLog.java new file mode 100644 index 0000000000..011f1e59c7 --- /dev/null +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/log/RollingLog.java @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2018 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.net4j.util.om.log; + +import org.eclipse.net4j.internal.util.bundle.OM; +import org.eclipse.net4j.util.collection.AbstractIterator; +import org.eclipse.net4j.util.collection.CloseableIterator; +import org.eclipse.net4j.util.concurrent.Worker; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.om.log.RollingLog.LogLine; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author Eike Stepper + * @since 3.8 + */ +public class RollingLog extends Worker implements Log, Iterable<LogLine> +{ + private final String logFile; + + private final long logSize; + + private final AtomicLong logLineCounter = new AtomicLong(0); + + private int fileNumber; + + private boolean fileAppend; + + private List<LogLine> queue = new ArrayList<LogLine>(); + + public RollingLog(String logFile, long logSize) + { + this.logFile = logFile; + this.logSize = logSize; + + setDaemon(true); + } + + public final String getLogFile() + { + return logFile; + } + + public final long getLogSize() + { + return logSize; + } + + public final void log(String line) + { + LogLine logLine = createLogLine(line); + + synchronized (this) + { + logLine.id = logLineCounter.incrementAndGet(); + queue.add(logLine); + notifyAll(); + } + } + + @Override + protected final void work(WorkContext context) throws Exception + { + List<LogLine> logLines; + synchronized (this) + { + if (queue.isEmpty()) + { + try + { + wait(100); + } + catch (InterruptedException ex) + { + context.terminate(); + } + + context.nextWork(); + } + + logLines = queue; + queue = new ArrayList<LogLine>(); + } + + writeLogLines(logLines); + } + + protected LogLine createLogLine(String line) + { + long millis = System.currentTimeMillis(); + String thread = getThreadInfo(); + + return new LogLine(millis, thread, line); + } + + protected void writeLogLines(List<LogLine> logLines) + { + if (logFile != null) + { + PrintStream out = null; + + try + { + File file; + + for (;;) + { + file = getFile(logFile, fileNumber); + + if (fileAppend && file.length() > logSize) + { + fileNumber++; + fileAppend = false; + continue; + } + + break; + } + + out = new PrintStream(new FileOutputStream(file, fileAppend)); + writeLogLines(logLines, out); + out.close(); + } + catch (IOException ex) + { + OM.LOG.error(ex); + } + finally + { + fileAppend = true; + IOUtil.closeSilent(out); + } + } + else + { + writeLogLines(logLines, System.out); + } + } + + protected void writeLogLines(List<LogLine> logLines, PrintStream out) + { + for (LogLine logLine : logLines) + { + writeLogLine(logLine, out); + } + } + + protected void writeLogLine(LogLine logLine, PrintStream out) + { + out.println(logLine); + } + + protected String getThreadInfo() + { + return Thread.currentThread().getName(); + } + + public final CloseableIterator<LogLine> iterator() + { + return iterator(logFile); + } + + public static CloseableIterator<LogLine> iterator(String logFile) + { + return new LogIterator(logFile); + } + + private static File getFile(String logFile, int fileNumber) + { + return new File(logFile + String.format("-%04d", fileNumber) + ".txt"); + } + + /** + * @author Eike Stepper + */ + private static final class LogIterator extends AbstractIterator<LogLine> implements CloseableIterator<LogLine> + { + private static final int CLOSED = -1; + + private final String logFile; + + private int fileNumber; + + private BufferedReader reader; + + public LogIterator(String logFile) + { + this.logFile = logFile; + } + + @Override + protected Object computeNextElement() + { + if (fileNumber == CLOSED) + { + return END_OF_DATA; + } + + if (reader == null) + { + File file = getFile(logFile, fileNumber++); + if (file.isFile()) + { + try + { + reader = new BufferedReader(new FileReader(file)); + } + catch (FileNotFoundException ex) + { + OM.LOG.error(ex); + return END_OF_DATA; + } + } + else + { + return END_OF_DATA; + } + } + + try + { + String string = reader.readLine(); + if (string == null) + { + reader.close(); + reader = null; + return computeNextElement(); + } + + return new LogLine(string); + } + catch (IOException ex) + { + OM.LOG.error(ex); + return END_OF_DATA; + } + } + + public void close() + { + IOUtil.close(reader); + reader = null; + fileNumber = CLOSED; + } + + public boolean isClosed() + { + return fileNumber == CLOSED; + } + } + + /** + * @author Eike Stepper + */ + public static final class LogLine + { + private static final String TAB = "\t"; + + private long id; + + private final long millis; + + private final String thread; + + private final String line; + + public LogLine(long millis, String thread, String line) + { + this.millis = millis; + this.thread = thread; + this.line = line; + } + + public LogLine(String string) + { + StringTokenizer tokenizer = new StringTokenizer(string, TAB); + id = Long.parseLong(tokenizer.nextToken()); + millis = Long.parseLong(tokenizer.nextToken()); + thread = tokenizer.nextToken(); + line = tokenizer.nextToken("").substring(1); + } + + public long getID() + { + return id; + } + + public long getMillis() + { + return millis; + } + + public String getThread() + { + return thread; + } + + public String getLine() + { + return line; + } + + @Override + public String toString() + { + return id + TAB + millis + TAB + thread + TAB + line; + } + } +} |