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.util.Map; 020 021 import org.apache.logging.log4j.util.EnglishEnums; 022 023 /** 024 * Represents a Message that conforms to RFC 5424 (http://tools.ietf.org/html/rfc5424). 025 */ 026 public class StructuredDataMessage extends MapMessage { 027 028 private static final long serialVersionUID = 1703221292892071920L; 029 private static final int MAX_LENGTH = 32; 030 private static final int HASHVAL = 31; 031 032 private StructuredDataId id; 033 034 private String message; 035 036 private String type; 037 038 /** 039 * Supported formats. 040 */ 041 public enum Format { 042 /** The map should be formatted as XML. */ 043 XML, 044 /** Full message format includes the type and message. */ 045 FULL 046 } 047 048 /** 049 * Constructor based on a String id. 050 * @param id The String id. 051 * @param msg The message. 052 * @param type The message type. 053 */ 054 public StructuredDataMessage(final String id, final String msg, final String type) { 055 this.id = new StructuredDataId(id, null, null); 056 this.message = msg; 057 this.type = type; 058 } 059 /** 060 * Constructor based on a String id. 061 * @param id The String id. 062 * @param msg The message. 063 * @param type The message type. 064 * @param data The StructuredData map. 065 */ 066 public StructuredDataMessage(final String id, final String msg, final String type, 067 final Map<String, String> data) { 068 super(data); 069 this.id = new StructuredDataId(id, null, null); 070 this.message = msg; 071 this.type = type; 072 } 073 074 /** 075 * Constructor based on a StructuredDataId. 076 * @param id The StructuredDataId. 077 * @param msg The message. 078 * @param type The message type. 079 */ 080 public StructuredDataMessage(final StructuredDataId id, final String msg, final String type) { 081 this.id = id; 082 this.message = msg; 083 this.type = type; 084 } 085 086 /** 087 * Constructor based on a StructuredDataId. 088 * @param id The StructuredDataId. 089 * @param msg The message. 090 * @param type The message type. 091 * @param data The StructuredData map. 092 */ 093 public StructuredDataMessage(final StructuredDataId id, final String msg, final String type, 094 final Map<String, String> data) { 095 super(data); 096 this.id = id; 097 this.message = msg; 098 this.type = type; 099 } 100 101 102 /** 103 * Constructor based on a StructuredDataMessage. 104 * @param msg The StructuredDataMessage. 105 * @param map The StructuredData map. 106 */ 107 private StructuredDataMessage(final StructuredDataMessage msg, final Map<String, String> map) { 108 super(map); 109 this.id = msg.id; 110 this.message = msg.message; 111 this.type = msg.type; 112 } 113 114 115 /** 116 * Basic constructor. 117 */ 118 protected StructuredDataMessage() { 119 120 } 121 122 /** 123 * Returns the supported formats. 124 * @return An array of the supported format names. 125 */ 126 @Override 127 public String[] getFormats() { 128 final String[] formats = new String[Format.values().length]; 129 int i = 0; 130 for (final Format format : Format.values()) { 131 formats[i++] = format.name(); 132 } 133 return formats; 134 } 135 136 /** 137 * Returns the id. 138 * @return the StructuredDataId. 139 */ 140 public StructuredDataId getId() { 141 return id; 142 } 143 144 /** 145 * Set the id from a String. 146 * @param id The String id. 147 */ 148 protected void setId(final String id) { 149 this.id = new StructuredDataId(id, null, null); 150 } 151 152 /** 153 * Set the id. 154 * @param id The StructuredDataId. 155 */ 156 protected void setId(final StructuredDataId id) { 157 this.id = id; 158 } 159 160 /** 161 * Set the type. 162 * @return the type. 163 */ 164 public String getType() { 165 return type; 166 } 167 168 protected void setType(final String type) { 169 if (type.length() > MAX_LENGTH) { 170 throw new IllegalArgumentException("Structured data type exceeds maximum length of 32 characters: " + type); 171 } 172 this.type = type; 173 } 174 /** 175 * Returns the message. 176 * @return the message. 177 */ 178 @Override 179 public String getFormat() { 180 return message; 181 } 182 183 protected void setMessageFormat(final String msg) { 184 this.message = msg; 185 } 186 187 188 @Override 189 protected void validate(final String key, final String value) { 190 if (key.length() > MAX_LENGTH) { 191 throw new IllegalArgumentException("Structured data keys are limited to 32 characters. key: " + key + 192 " value: " + value); 193 } 194 } 195 196 /** 197 * Format the Structured data as described in RFC 5424. 198 * 199 * @return The formatted String. 200 */ 201 @Override 202 public String asString() { 203 return asString(Format.FULL, null); 204 } 205 206 /** 207 * Format the Structured data as described in RFC 5424. 208 * 209 * @param format The format identifier. Ignored in this implementation. 210 * @return The formatted String. 211 */ 212 213 @Override 214 public String asString(final String format) { 215 try { 216 return asString(EnglishEnums.valueOf(Format.class, format), null); 217 } catch (final IllegalArgumentException ex) { 218 return asString(); 219 } 220 } 221 222 /** 223 * Format the Structured data as described in RFC 5424. 224 * 225 * @param format "full" will include the type and message. null will return only the STRUCTURED-DATA as 226 * described in RFC 5424 227 * @param structuredDataId The SD-ID as described in RFC 5424. If null the value in the StructuredData 228 * will be used. 229 * @return The formatted String. 230 */ 231 public final String asString(final Format format, final StructuredDataId structuredDataId) { 232 final StringBuilder sb = new StringBuilder(); 233 final boolean full = Format.FULL.equals(format); 234 if (full) { 235 final String type = getType(); 236 if (type == null) { 237 return sb.toString(); 238 } 239 sb.append(getType()).append(" "); 240 } 241 StructuredDataId id = getId(); 242 if (id != null) { 243 id = id.makeId(structuredDataId); 244 } else { 245 id = structuredDataId; 246 } 247 if (id == null || id.getName() == null) { 248 return sb.toString(); 249 } 250 sb.append("["); 251 sb.append(id); 252 sb.append(" "); 253 appendMap(sb); 254 sb.append("]"); 255 if (full) { 256 final String msg = getFormat(); 257 if (msg != null) { 258 sb.append(" ").append(msg); 259 } 260 } 261 return sb.toString(); 262 } 263 264 /** 265 * Format the message and return it. 266 * @return the formatted message. 267 */ 268 @Override 269 public String getFormattedMessage() { 270 return asString(Format.FULL, null); 271 } 272 273 /** 274 * Format the message according the the specified format. 275 * @param formats An array of Strings that provide extra information about how to format the message. 276 * StructuredDataMessage accepts only a format of "FULL" which will cause the event type to be 277 * prepended and the event message to be appended. Specifying any other value will cause only the 278 * StructuredData to be included. The default is "FULL". 279 * 280 * @return the formatted message. 281 */ 282 @Override 283 public String getFormattedMessage(final String[] formats) { 284 if (formats != null && formats.length > 0) { 285 for (final String format : formats) { 286 if (Format.XML.name().equalsIgnoreCase(format)) { 287 return asXML(); 288 } else if (Format.FULL.name().equalsIgnoreCase(format)) { 289 return asString(Format.FULL, null); 290 } 291 } 292 return asString(null, null); 293 } else { 294 return asString(Format.FULL, null); 295 } 296 } 297 298 private String asXML() { 299 final StringBuilder sb = new StringBuilder(); 300 final StructuredDataId id = getId(); 301 if (id == null || id.getName() == null || type == null) { 302 return sb.toString(); 303 } 304 sb.append("<StructuredData>\n"); 305 sb.append("<type>").append(type).append("</type>\n"); 306 sb.append("<id>").append(id).append("</id>\n"); 307 super.asXML(sb); 308 sb.append("</StructuredData>\n"); 309 return sb.toString(); 310 } 311 312 @Override 313 public String toString() { 314 return asString(null, null); 315 } 316 317 318 @Override 319 public MapMessage newInstance(final Map<String, String> map) { 320 return new StructuredDataMessage(this, map); 321 } 322 323 @Override 324 public boolean equals(final Object o) { 325 if (this == o) { 326 return true; 327 } 328 if (o == null || getClass() != o.getClass()) { 329 return false; 330 } 331 332 final StructuredDataMessage that = (StructuredDataMessage) o; 333 334 if (!super.equals(o)) { 335 return false; 336 } 337 if (type != null ? !type.equals(that.type) : that.type != null) { 338 return false; 339 } 340 if (id != null ? !id.equals(that.id) : that.id != null) { 341 return false; 342 } 343 if (message != null ? !message.equals(that.message) : that.message != null) { 344 return false; 345 } 346 347 return true; 348 } 349 350 @Override 351 public int hashCode() { 352 int result = super.hashCode(); 353 result = HASHVAL * result + (type != null ? type.hashCode() : 0); 354 result = HASHVAL * result + (id != null ? id.hashCode() : 0); 355 result = HASHVAL * result + (message != null ? message.hashCode() : 0); 356 return result; 357 } 358 }