View Javadoc

1   package com.github.triceo.splitlog;
2   
3   import java.util.concurrent.ExecutorService;
4   import java.util.concurrent.Executors;
5   import java.util.concurrent.Future;
6   import java.util.concurrent.TimeUnit;
7   import java.util.concurrent.atomic.AtomicLong;
8   
9   import org.apache.commons.io.input.Tailer;
10  import org.slf4j.Logger;
11  
12  import com.github.triceo.splitlog.api.Follower;
13  import com.github.triceo.splitlog.api.LogWatchBuilder;
14  import com.github.triceo.splitlog.api.Message;
15  import com.github.triceo.splitlog.logging.SplitlogLoggerFactory;
16  import com.github.triceo.splitlog.util.SplitlogTailer;
17  import com.github.triceo.splitlog.util.SplitlogThreadFactory;
18  
19  /**
20   * Has a sole responsibility of starting and stopping {@link Tailer} thread when
21   * told so by the {@link DefaultLogWatch}.
22   */
23  final class LogWatchTailingManager {
24  
25      private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(new SplitlogThreadFactory("tails"));
26  
27      private static final Logger LOGGER = SplitlogLoggerFactory.getLogger(LogWatchTailingManager.class);
28      private final int bufferSize;
29      private final long delayBetweenReads;
30      private final AtomicLong numberOfTimesThatTailerWasStarted = new AtomicLong(0);
31      private final boolean reopenBetweenReads, ignoreExistingContent;
32      private SplitlogTailer tailer;
33      private Future<?> tailerFuture;
34      private final DefaultLogWatch watch;
35  
36      public LogWatchTailingManager(final DefaultLogWatch watch, final LogWatchBuilder builder) {
37          this.watch = watch;
38          this.delayBetweenReads = builder.getDelayBetweenReads();
39          this.bufferSize = builder.getReadingBufferSize();
40          this.reopenBetweenReads = builder.isClosingBetweenReads();
41          this.ignoreExistingContent = !builder.isReadingFromBeginning();
42      }
43  
44      public synchronized boolean isRunning() {
45          return (this.tailer != null);
46      }
47  
48      /**
49       * Start the tailer on a separate thread. Only when a tailer is running can
50       * {@link Follower}s be notified of new {@link Message}s from the log.
51       *
52       * @return True if the start was scheduled, false if scheduled already.
53       */
54      public synchronized boolean start() {
55          if (this.isRunning()) {
56              return false;
57          }
58          final boolean willReadFromEnd = this.willReadFromEnd();
59          LogWatchTailingManager.LOGGER.debug("Tailer {} ignore existing file contents.", willReadFromEnd ? "will"
60                  : "won't");
61          this.tailer = new SplitlogTailer(this.watch.getWatchedFile(), new LogWatchTailerListener(this.watch),
62                  this.delayBetweenReads, this.willReadFromEnd(), this.reopenBetweenReads, this.bufferSize);
63          this.tailerFuture = LogWatchTailingManager.EXECUTOR.submit(this.tailer);
64          final long start = System.nanoTime();
65          this.tailer.waitUntilStarted();
66          final long duration = System.nanoTime() - start;
67          LogWatchTailingManager.LOGGER.debug("It took {} ms for the tailing to actually start.",
68                  TimeUnit.NANOSECONDS.toMillis(duration));
69          final long iterationNum = this.numberOfTimesThatTailerWasStarted.incrementAndGet();
70          LogWatchTailingManager.LOGGER.info("Tailing #{} started for {}.", iterationNum, this.watch);
71          return true;
72      }
73  
74      /**
75       * Stop the tailer thread, preventing any {@link Follower}s from receiving
76       * {@link Message}s.
77       *
78       * @return True if stopped, false if never running.
79       */
80      public synchronized boolean stop() {
81          if (!this.isRunning()) {
82              LogWatchTailingManager.LOGGER.debug("Tailer not running, therefore not terminating.");
83              return false;
84          }
85          // forcibly terminate tailer
86          this.tailer.stop();
87          this.tailerFuture.cancel(true);
88          // cleanup
89          this.tailer = null;
90          this.tailerFuture = null;
91          LogWatchTailingManager.LOGGER.info("Terminated tailing #{} for {}.",
92                  this.numberOfTimesThatTailerWasStarted.get(), this.watch);
93          return true;
94      }
95  
96      private synchronized boolean willReadFromEnd() {
97          if (this.numberOfTimesThatTailerWasStarted.get() > 0) {
98              return true;
99          } else {
100             return this.ignoreExistingContent;
101         }
102     }
103 
104 }