View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.message;
18  
19  import java.io.Serializable;
20  import java.util.Locale;
21  import java.util.Map;
22  
23  import org.apache.logging.log4j.message.MapMessage.MapFormat;
24  import org.apache.logging.log4j.util.EnglishEnums;
25  
26  /**
27   * Represents a Message that conforms to RFC 5424 (http://tools.ietf.org/html/rfc5424).
28   */
29  public class StructuredDataMessage extends MapMessage implements MultiformatMessage, Serializable {
30  
31      private static final long serialVersionUID = 1703221292892071920L;
32      private static final int MAX_LENGTH = 32;
33      private static final int HASHVAL = 31;
34  
35      private StructuredDataId id;
36  
37      private String message;
38  
39      private String type;
40  
41      /**
42       * Supported formats.
43       */
44      public enum Format {
45          /** The map should be formatted as XML. */
46          XML,
47          /** Full message format includes the type and message. */
48          FULL
49      }
50  
51      /**
52       * Constructor based on a String id.
53       * @param id The String id.
54       * @param msg The message.
55       * @param type The message type.
56       */
57      public StructuredDataMessage(final String id, final String msg, final String type) {
58          this.id = new StructuredDataId(id, null, null);
59          this.message = msg;
60          this.type = type;
61      }
62      /**
63       * Constructor based on a String id.
64       * @param id The String id.
65       * @param msg The message.
66       * @param type The message type.
67       * @param data The StructuredData map.
68       */
69      public StructuredDataMessage(final String id, final String msg, final String type,
70                                   Map<String, String> data) {
71          super(data);
72          this.id = new StructuredDataId(id, null, null);
73          this.message = msg;
74          this.type = type;
75      }
76  
77      /**
78       * Constructor based on a StructuredDataId.
79       * @param id The StructuredDataId.
80       * @param msg The message.
81       * @param type The message type.
82       */
83      public StructuredDataMessage(final StructuredDataId id, final String msg, final String type) {
84          this.id = id;
85          this.message = msg;
86          this.type = type;
87      }
88  
89      /**
90       * Constructor based on a StructuredDataId.
91       * @param id The StructuredDataId.
92       * @param msg The message.
93       * @param type The message type.
94       * @param data The StructuredData map.
95       */
96      public StructuredDataMessage(final StructuredDataId id, final String msg, final String type,
97                                   Map<String, String> data) {
98          super(data);
99          this.id = id;
100         this.message = msg;
101         this.type = type;
102     }
103 
104 
105     /**
106      * Constructor based on a StructuredDataMessage.
107      * @param msg The StructuredDataMessage.
108      * @param map The StructuredData map.
109      */
110     private StructuredDataMessage(StructuredDataMessage msg, Map<String, String> map) {
111         super(map);
112         this.id = msg.id;
113         this.message = msg.message;
114         this.type = msg.type;
115     }
116 
117 
118     /**
119      * Basic constructor.
120      */
121     protected StructuredDataMessage() {
122 
123     }
124 
125     /**
126      * Returns the supported formats.
127      * @return An array of the supported format names.
128      */
129     @Override
130     public String[] getFormats() {
131         String[] formats = new String[Format.values().length];
132         int i = 0;
133         for (Format format : Format.values()) {
134             formats[i++] = format.name();
135         }
136         return formats;
137     }
138 
139     /**
140      * Returns the id.
141      * @return the StructuredDataId.
142      */
143     public StructuredDataId getId() {
144         return id;
145     }
146 
147     /**
148      * Set the id from a String.
149      * @param id The String id.
150      */
151     protected void setId(String id) {
152         this.id = new StructuredDataId(id, null, null);
153     }
154 
155     /**
156      * Set the id.
157      * @param id The StructuredDataId.
158      */
159     protected void setId(StructuredDataId id) {
160         this.id = id;
161     }
162 
163     /**
164      * Set the type.
165      * @return the type.
166      */
167     public String getType() {
168         return type;
169     }
170 
171     protected void setType(String type) {
172         if (type.length() > MAX_LENGTH) {
173             throw new IllegalArgumentException("Structured data type exceeds maximum length of 32 characters: " + type);
174         }
175         this.type = type;
176     }
177     /**
178      * Returns the message.
179      * @return the message.
180      */
181     @Override
182     public String getFormat() {
183         return message;
184     }
185 
186     protected void setMessageFormat(String msg) {
187         this.message = msg;
188     }
189 
190 
191     @Override
192     protected void validate(String key, String value) {
193         if (value.length() > MAX_LENGTH) {
194             throw new IllegalArgumentException("Structured data values are limited to 32 characters. key: " + key +
195                 " value: " + value);
196         }
197     }
198 
199     /**
200      * Format the Structured data as described in RFC 5424.
201      *
202      * @return The formatted String.
203      */
204     @Override
205     public String asString() {
206         return asString(Format.FULL, null);
207     }
208 
209     /**
210      * Format the Structured data as described in RFC 5424.
211      *
212      * @param format The format identifier. Ignored in this implementation.
213      * @return The formatted String.
214      */
215 
216     @Override
217     public String asString(String format) {
218         try {
219             return asString(EnglishEnums.valueOf(Format.class, format), null);
220         } catch (IllegalArgumentException ex) {
221             return asString();
222         }
223     }
224 
225     /**
226      * Format the Structured data as described in RFC 5424.
227      *
228      * @param format           "full" will include the type and message. null will return only the STRUCTURED-DATA as
229      *                         described in RFC 5424
230      * @param structuredDataId The SD-ID as described in RFC 5424. If null the value in the StructuredData
231      *                         will be used.
232      * @return The formatted String.
233      */
234     public final String asString(Format format, StructuredDataId structuredDataId) {
235         StringBuilder sb = new StringBuilder();
236         boolean full = Format.FULL.equals(format);
237         if (full) {
238             String type = getType();
239             if (type == null) {
240                 return sb.toString();
241             }
242             sb.append(getType()).append(" ");
243         }
244         StructuredDataId id = getId();
245         if (id != null) {
246             id = id.makeId(structuredDataId);
247         } else {
248             id = structuredDataId;
249         }
250         if (id == null || id.getName() == null) {
251             return sb.toString();
252         }
253         sb.append("[");
254         sb.append(id);
255         sb.append(" ");
256         appendMap(sb);
257         sb.append("]");
258         if (full) {
259             String msg = getFormat();
260             if (msg != null) {
261                 sb.append(" ").append(msg);
262             }
263         }
264         return sb.toString();
265     }
266 
267     /**
268      * Format the message and return it.
269      * @return the formatted message.
270      */
271     @Override
272     public String getFormattedMessage() {
273         return asString(Format.FULL, null);
274     }
275 
276     /**
277      * Format the message according the the specified format.
278      * @param formats An array of Strings that provide extra information about how to format the message.
279      * StructuredDataMessage accepts only a format of "FULL" which will cause the event type to be
280      * prepended and the event message to be appended. Specifying any other value will cause only the
281      * StructuredData to be included. The default is "FULL".
282      *
283      * @return the formatted message.
284      */
285     @Override
286     public String getFormattedMessage(String[] formats) {
287         if (formats != null && formats.length > 0) {
288             for (String format : formats) {
289                 if (Format.XML.name().equalsIgnoreCase(format)) {
290                     return asXML();
291                 } else if (Format.FULL.name().equalsIgnoreCase(format)) {
292                     return asString(Format.FULL, null);
293                 }
294             }
295             return asString(null, null);
296         } else {
297             return asString(Format.FULL, null);
298         }
299     }
300 
301     private String asXML() {
302         StringBuilder sb = new StringBuilder();
303         StructuredDataId id = getId();
304         if (id == null || id.getName() == null || type == null) {
305             return sb.toString();
306         }
307         sb.append("<StructuredData>\n");
308         sb.append("<type>").append(type).append("</type>\n");
309         sb.append("<id>").append(id).append("</id>\n");
310         super.asXML(sb);
311         sb.append("</StructuredData>\n");
312         return sb.toString();
313     }
314 
315     @Override
316     public String toString() {
317         return asString(null, null);
318     }
319 
320 
321     @Override
322     public MapMessage newInstance(Map<String, String> map) {
323         return new StructuredDataMessage(this, map);
324     }
325 
326     @Override
327     public boolean equals(Object o) {
328         if (this == o) {
329             return true;
330         }
331         if (o == null || getClass() != o.getClass()) {
332             return false;
333         }
334 
335         StructuredDataMessage that = (StructuredDataMessage) o;
336 
337         if (!super.equals(o)) {
338             return false;
339         }
340         if (type != null ? !type.equals(that.type) : that.type != null) {
341             return false;
342         }
343         if (id != null ? !id.equals(that.id) : that.id != null) {
344             return false;
345         }
346         if (message != null ? !message.equals(that.message) : that.message != null) {
347             return false;
348         }
349 
350         return true;
351     }
352 
353     @Override
354     public int hashCode() {
355         int result = super.hashCode();
356         result = HASHVAL * result + (type != null ? type.hashCode() : 0);
357         result = HASHVAL * result + (id != null ? id.hashCode() : 0);
358         result = HASHVAL * result + (message != null ? message.hashCode() : 0);
359         return result;
360     }
361 }