001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j.message; 018 019 import java.io.Serializable; 020 import java.util.Locale; 021 import java.util.Map; 022 023 import org.apache.logging.log4j.message.MapMessage.MapFormat; 024 import org.apache.logging.log4j.util.EnglishEnums; 025 026 /** 027 * Represents a Message that conforms to RFC 5424 (http://tools.ietf.org/html/rfc5424). 028 */ 029 public class StructuredDataMessage extends MapMessage implements MultiformatMessage, Serializable { 030 031 private static final long serialVersionUID = 1703221292892071920L; 032 private static final int MAX_LENGTH = 32; 033 private static final int HASHVAL = 31; 034 035 private StructuredDataId id; 036 037 private String message; 038 039 private String type; 040 041 /** 042 * Supported formats. 043 */ 044 public enum Format { 045 /** The map should be formatted as XML. */ 046 XML, 047 /** Full message format includes the type and message. */ 048 FULL 049 } 050 051 /** 052 * Constructor based on a String id. 053 * @param id The String id. 054 * @param msg The message. 055 * @param type The message type. 056 */ 057 public StructuredDataMessage(final String id, final String msg, final String type) { 058 this.id = new StructuredDataId(id, null, null); 059 this.message = msg; 060 this.type = type; 061 } 062 /** 063 * Constructor based on a String id. 064 * @param id The String id. 065 * @param msg The message. 066 * @param type The message type. 067 * @param data The StructuredData map. 068 */ 069 public StructuredDataMessage(final String id, final String msg, final String type, 070 Map<String, String> data) { 071 super(data); 072 this.id = new StructuredDataId(id, null, null); 073 this.message = msg; 074 this.type = type; 075 } 076 077 /** 078 * Constructor based on a StructuredDataId. 079 * @param id The StructuredDataId. 080 * @param msg The message. 081 * @param type The message type. 082 */ 083 public StructuredDataMessage(final StructuredDataId id, final String msg, final String type) { 084 this.id = id; 085 this.message = msg; 086 this.type = type; 087 } 088 089 /** 090 * Constructor based on a StructuredDataId. 091 * @param id The StructuredDataId. 092 * @param msg The message. 093 * @param type The message type. 094 * @param data The StructuredData map. 095 */ 096 public StructuredDataMessage(final StructuredDataId id, final String msg, final String type, 097 Map<String, String> data) { 098 super(data); 099 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 }