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
21
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
50
51
52
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
76
77
78
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
86 this.tailer.stop();
87 this.tailerFuture.cancel(true);
88
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 }