View Javadoc
1   package com.github.triceo.splitlog;
2   
3   import it.unimi.dsi.fastutil.longs.AbstractLongComparator;
4   import it.unimi.dsi.fastutil.longs.LongComparator;
5   
6   import java.lang.ref.WeakReference;
7   import java.util.ArrayList;
8   import java.util.Collection;
9   import java.util.Collections;
10  import java.util.Date;
11  import java.util.List;
12  
13  import com.github.triceo.splitlog.api.CommonFollower;
14  import com.github.triceo.splitlog.api.ExceptionDescriptor;
15  import com.github.triceo.splitlog.api.Message;
16  import com.github.triceo.splitlog.api.MessageSeverity;
17  import com.github.triceo.splitlog.api.MessageType;
18  import com.github.triceo.splitlog.api.TailSplitter;
19  import com.github.triceo.splitlog.splitters.SimpleTailSplitter;
20  
21  final class DefaultMessage implements Message {
22  
23      private static final LongComparator COMPARATOR = new AbstractLongComparator() {
24  
25          @Override
26          public int compare(final long k1, final long k2) {
27              if (k1 < k2) {
28                  return -1;
29              } else if (k1 == k2) {
30                  return 0;
31              } else {
32                  return 1;
33              }
34          }
35  
36      };
37      private static final TailSplitter DEFAULT_SPLITTER = new SimpleTailSplitter();
38  
39      private final ExceptionDescriptor exceptionDescriptor;
40      private final List<String> lines;
41      private final String logger;
42      private final long millisecondsSinceJanuary1st1970;
43      private final WeakReference<Message> previousMessage;
44      private final MessageSeverity severity;
45      private final TailSplitter splitter;
46      private final MessageType type;
47  
48      private final long uniqueId;
49  
50      /**
51       * Will call {@link #DefaultMessage(long, Collection, TailSplitter)} with
52       * {@link #DEFAULT_SPLITTER}.
53       *
54       * @param id
55       *            Unique ID for the message. No other instance may have this ID,
56       *            or else they will be considered equal.
57       * @param raw
58       *            DefaultMessage lines, expected without any pre-processing.
59       */
60      protected DefaultMessage(final long id, final Collection<String> raw) {
61          this(id, raw, DefaultMessage.DEFAULT_SPLITTER);
62      }
63  
64      /**
65       * Form a new message and infer its metadata using a given splitter.
66       *
67       * @param id
68       *            Unique ID for the message. No other instance may have this ID,
69       *            or else they will be considered equal.
70       * @param raw
71       *            DefaultMessage lines, expected without any pre-processing.
72       * @param timestamp
73       *            In milliseconds since January 1st 1970. Will be overriden if
74       *            {@link TailSplitter} can decode the timestamp from the log.
75       * @param splitter
76       *            Used to extract metadata out of the raw lines.
77       * @param previousMessage
78       *            DefaultMessage that preceded this one in the log file. Should
79       *            not include tags from {@link CommonFollower}.
80       */
81      protected DefaultMessage(final long id, final Collection<String> raw, final long timestamp,
82          final TailSplitter splitter, final Message previousMessage) {
83          if ((raw == null) || raw.isEmpty()) {
84              throw new IllegalArgumentException("DefaultMessage must not be null.");
85          } else if (splitter == null) {
86              throw new IllegalArgumentException("DefaultMessage requires a TailSplitter.");
87          }
88          if (previousMessage == null) {
89              this.previousMessage = null;
90          } else {
91              this.previousMessage = new WeakReference<Message>(previousMessage);
92          }
93          this.uniqueId = id;
94          this.splitter = splitter;
95          this.lines = Collections.unmodifiableList(new ArrayList<String>(raw));
96          final String logger = splitter.determineLogger(this.lines);
97          this.logger = (logger == null) ? "" : logger;
98          this.severity = splitter.determineSeverity(this.lines);
99          this.type = splitter.determineType(this.lines);
100         this.exceptionDescriptor = splitter.determineException(this.lines);
101         // determine message timestamp
102         final Date d = splitter.determineDate(this.lines);
103         if (d == null) {
104             this.millisecondsSinceJanuary1st1970 = timestamp;
105         } else {
106             this.millisecondsSinceJanuary1st1970 = d.getTime();
107         }
108     }
109 
110     /**
111      * Will call
112      * {@link #DefaultMessage(long, Collection, long, TailSplitter, DefaultMessage)}
113      * with current time and no previous message.
114      *
115      * @param id
116      *            Unique ID for the message. No other instance may have this ID,
117      *            or else they will be considered equal.
118      * @param raw
119      *            DefaultMessage lines, expected without any pre-processing.
120      * @param splitter
121      *            Used to extract metadata out of the raw lines.
122      */
123     protected DefaultMessage(final long id, final Collection<String> raw, final TailSplitter splitter) {
124         this(id, raw, System.currentTimeMillis(), splitter, null);
125     }
126 
127     /**
128      * Creates a one-line message of type {@link MessageType#TAG}. The message
129      * will point to no previous message.
130      *
131      * @param id
132      *            Unique ID for the message. No other instance may have this ID,
133      *            or else they will be considered equal.
134      * @param message
135      *            The only line in the message.
136      */
137     protected DefaultMessage(final long id, final String message) {
138         if ((message == null) || (message.length() == 0)) {
139             throw new IllegalArgumentException("DefaultMessage must not be empty.");
140         }
141         this.logger = "";
142         this.uniqueId = id;
143         this.previousMessage = null;
144         this.splitter = null;
145         this.lines = Collections.singletonList(message.trim());
146         this.severity = MessageSeverity.UNKNOWN;
147         this.type = MessageType.TAG;
148         this.millisecondsSinceJanuary1st1970 = System.currentTimeMillis();
149         this.exceptionDescriptor = null;
150     }
151 
152     @Override
153     public int compareTo(final Message o) {
154         return DefaultMessage.COMPARATOR.compare(this.getUniqueId(), o.getUniqueId());
155     }
156 
157     @Override
158     public boolean equals(final Object obj) {
159         if (this == obj) {
160             return true;
161         }
162         if (obj == null) {
163             return false;
164         }
165         if (this.getClass() != obj.getClass()) {
166             return false;
167         }
168         final DefaultMessage other = (DefaultMessage) obj;
169         if (this.uniqueId != other.uniqueId) {
170             return false;
171         }
172         return true;
173     }
174 
175     @Override
176     public Date getDate() {
177         return new Date(this.millisecondsSinceJanuary1st1970);
178     }
179 
180     @Override
181     public ExceptionDescriptor getExceptionDescriptor() {
182         return this.exceptionDescriptor;
183     }
184 
185     @Override
186     public List<String> getLines() {
187         return this.lines;
188     }
189 
190     @Override
191     public List<String> getLinesWithoutMetadata() {
192         if (this.type == MessageType.TAG) {
193             // nothing to split for tags
194             return this.lines;
195         }
196         final List<String> stripped = new ArrayList<String>();
197         for (final String line : this.lines) {
198             stripped.add(this.splitter.stripOfMetadata(line));
199         }
200         return Collections.unmodifiableList(stripped);
201     }
202 
203     @Override
204     public String getLogger() {
205         return this.logger;
206     }
207 
208     @Override
209     public Message getPreviousMessage() {
210         if (this.previousMessage == null) {
211             return null;
212         }
213         return this.previousMessage.get();
214     }
215 
216     @Override
217     public MessageSeverity getSeverity() {
218         return this.severity;
219     }
220 
221     @Override
222     public MessageType getType() {
223         return this.type;
224     }
225 
226     @Override
227     public long getUniqueId() {
228         return this.uniqueId;
229     }
230 
231     @Override
232     public boolean hasException() {
233         return this.exceptionDescriptor != null;
234     }
235 
236     @Override
237     public int hashCode() {
238         final int prime = 31;
239         int result = 1;
240         result = (prime * result) + (int) (this.uniqueId ^ (this.uniqueId >>> 32));
241         return result;
242     }
243 
244     @Override
245     public String toString() {
246         final StringBuilder sb = new StringBuilder();
247         sb.append('#');
248         sb.append(this.getUniqueId());
249         sb.append(" ");
250         sb.append(" [");
251         sb.append(this.logger);
252         sb.append("] ");
253         sb.append(this.getDate());
254         sb.append(" (");
255         sb.append(this.type);
256         sb.append(") ");
257         sb.append(this.severity);
258         sb.append(" '");
259         sb.append(this.lines.get(0));
260         sb.append("'");
261         if (this.lines.size() > 1) {
262             sb.append(" and ");
263             sb.append(this.lines.size() - 1);
264             sb.append(" more lines...");
265         }
266         return sb.toString();
267     }
268 
269 }