View Javadoc

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