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