diff --git a/slf4j-api/src/main/java/org/slf4j/StructuredData.java b/slf4j-api/src/main/java/org/slf4j/StructuredData.java index 378ce85ed..3ab8e5edd 100644 --- a/slf4j-api/src/main/java/org/slf4j/StructuredData.java +++ b/slf4j-api/src/main/java/org/slf4j/StructuredData.java @@ -55,15 +55,45 @@ public interface StructuredData extends Serializable { String getMessage(); /** - * Returns the data items and their values. Although the Map can contain Objects, the - * toString() method of the objects will be called when the objects are formatted for - * inclusion in log records. If the object is not serializable then the value of toString - * will be used when serialization is required. + * Returns an immutable Map of the data items and their values. In Java 5 this Map would + * be specified as Map. * * @return The data item names (32 characters maximum) and their values. */ Map getData(); + /** + * Add all the items from a Map to the data Map. + * @param map The map to copy. + */ + void putAll(Map map); + + /** + * Add an item to the data Map. + * @param key The name of the item. + * @param value The value of the item. + */ + void put(String key, String value); + + /** + * Get a specific value from the data Map. + * @param key The name of the item. + * @return The value of the item. + */ + String get(String key); + + /** + * Remove an item from the data Map. + * @param key The name of the item to remove. + * @return The value of the item removed. + */ + String remove(String key); + + /** + * Clears the data map. + */ + void clear(); + /** * Formats the structured data in the form [id key="value" ...] message as described in RFC 5424. * diff --git a/slf4j-api/src/main/java/org/slf4j/StructuredDataImpl.java b/slf4j-api/src/main/java/org/slf4j/StructuredDataImpl.java index c7020175f..ac8a51ae8 100644 --- a/slf4j-api/src/main/java/org/slf4j/StructuredDataImpl.java +++ b/slf4j-api/src/main/java/org/slf4j/StructuredDataImpl.java @@ -3,6 +3,7 @@ import java.util.Map; import java.util.HashMap; import java.util.Iterator; +import java.util.Collections; /** * @@ -68,7 +69,34 @@ protected void setMessage(String msg) { } public Map getData() { - return data; + return Collections.unmodifiableMap(data); + } + + public void clear() { + data.clear(); + } + + public void put(String key, String value) { + if (value == null) { + throw new IllegalArgumentException("No value provided for key " + key); + } + if (value.length() > 32) { + throw new IllegalArgumentException("Structured data values are limited to 32 characters. key: " + key + + " value: " + value); + } + data.put(key, value); + } + + public void putAll(Map map) { + data.putAll(map); + } + + public String get(String key) { + return (String) data.get(key); + } + + public String remove(String key) { + return (String) data.remove(key); } /** diff --git a/slf4j-ext/src/main/java/org/slf4j/ext/EventData.java b/slf4j-ext/src/main/java/org/slf4j/ext/EventData.java index b24f88175..98c31faa9 100755 --- a/slf4j-ext/src/main/java/org/slf4j/ext/EventData.java +++ b/slf4j-ext/src/main/java/org/slf4j/ext/EventData.java @@ -23,38 +23,38 @@ package org.slf4j.ext; import org.slf4j.StructuredDataImpl; -import org.slf4j.StructuredDataId; +import org.slf4j.StructuredData; import java.io.Serializable; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Date; +import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import java.util.Set; -import java.util.Collection; -import java.util.AbstractSet; -import java.util.AbstractCollection; -import java.util.HashMap; import java.beans.XMLDecoder; import java.beans.XMLEncoder; import java.beans.ExceptionListener; +import java.text.SimpleDateFormat; +import java.text.ParseException; /** * Base class for Event Data. Event Data contains data to be logged about an * event. Users may extend this class for each EventType they want to log. * * @author Ralph Goers + * @deprecated Use StructuredDataImpl instead. */ -public class EventData extends StructuredDataImpl implements Serializable { +public class EventData implements Serializable { private static final long serialVersionUID = 153270778642103985L; + private StructuredEventData eventData = new StructuredEventData(); public static final String EVENT_MESSAGE = "EventMessage"; public static final String EVENT_TYPE = "EventType"; public static final String EVENT_DATETIME = "EventDateTime"; public static final String EVENT_ID = "EventId"; - private EventMap eventData = new EventMap(getData()); + private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS"; /** * Default Constructor @@ -69,7 +69,12 @@ public EventData() { * The event data. */ public EventData(Map map) { - eventData.putAll(map); + Map tmp = new HashMap(map); + String id = (String) tmp.remove(EVENT_ID); + String msg = (String) tmp.remove(EVENT_MESSAGE); + String type = (String) tmp.remove(EVENT_TYPE); + eventData = new StructuredEventData(id, msg, type); + eventData.putAll(tmp); } /** @@ -84,7 +89,11 @@ public EventData(String xml) { ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes()); try { XMLDecoder decoder = new XMLDecoder(bais); - eventData.putAll((Map) decoder.readObject()); + Map map = (Map) decoder.readObject(); + String id = (String) map.remove(EVENT_ID); + String msg = (String) map.remove(EVENT_MESSAGE); + String type = (String) map.remove(EVENT_TYPE); + this.eventData = new StructuredEventData(id, msg, type); } catch (Exception e) { throw new EventException("Error decoding " + xml, e); } @@ -96,16 +105,20 @@ public EventData(String xml) { * @return an XML String containing all the EventDAta items. */ public String toXML() { - return toXML(eventData); + Map map = new HashMap(); + map.putAll(eventData.getData()); + map.put(EVENT_MESSAGE, eventData.getMessage()); + map.put(EVENT_TYPE, eventData.getType()); + map.put(EVENT_ID, eventData.getId().toString()); + return toXML(map); } /** * Serialize all the EventData items into an XML representation. - * @param map The map containing the event data. + * * @return an XML String containing all the EventDAta items. */ public static String toXML(Map map) { - Map copy = new HashMap(map); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { XMLEncoder encoder = new XMLEncoder(baos); @@ -114,7 +127,7 @@ public void exceptionThrown(Exception exception) { exception.printStackTrace(); } }); - encoder.writeObject(copy); + encoder.writeObject(map); encoder.close(); return baos.toString(); } catch (Exception e) { @@ -129,7 +142,7 @@ public void exceptionThrown(Exception exception) { * @return The event identifier */ public String getEventId() { - return getId().toString(); + return eventData.getId().toString(); } /** @@ -142,7 +155,7 @@ public void setEventId(String eventId) { if (eventId == null) { throw new IllegalArgumentException("eventId cannot be null"); } - setId(eventId); + eventData.setId(eventId); } /** @@ -152,7 +165,7 @@ public void setEventId(String eventId) { * none. */ public String getMessage() { - return super.getMessage(); + return eventData.getMessage(); } /** @@ -162,7 +175,7 @@ public String getMessage() { * The message text. */ public void setMessage(String message) { - super.setMessage(message); + eventData.setMessage(message); } /** @@ -171,7 +184,16 @@ public void setMessage(String message) { * @return The Date associated with the event. */ public Date getEventDateTime() { - return (Date) getData().get(EVENT_DATETIME); + String eventDate = (String) eventData.getData().get(EVENT_DATETIME); + if (eventDate == null) { + return null; + } + SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT); + try { + return format.parse(eventDate); + } catch (ParseException pe) { + return null; + } } /** @@ -182,7 +204,12 @@ public Date getEventDateTime() { * The event Date. */ public void setEventDateTime(Date eventDateTime) { - getData().put(EVENT_DATETIME, eventDateTime); + if (eventDateTime == null) { + return; + } + SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT); + String date = format.format(eventDateTime); + eventData.put(EVENT_DATETIME, date); } /** @@ -192,7 +219,7 @@ public void setEventDateTime(Date eventDateTime) { * The type of the event. */ public void setEventType(String eventType) { - setType(eventType); + eventData.setType(eventType); } /** @@ -201,7 +228,7 @@ public void setEventType(String eventType) { * @return The event type. */ public String getEventType() { - return getType(); + return eventData.getType(); } /** @@ -213,7 +240,7 @@ public String getEventType() { * The data associated with the key. */ public void put(String name, Serializable obj) { - getData().put(name, obj); + eventData.put(name, obj.toString()); } /** @@ -225,13 +252,7 @@ public void put(String name, Serializable obj) { * present. */ public Serializable get(String name) { - Object obj = eventData.get(name); - if (obj == null) { - return null; - } else if (obj instanceof Serializable) { - return (Serializable) obj; - } - return obj.toString(); + return (Serializable) eventData.getData().get(name); } /** @@ -250,7 +271,7 @@ public void putAll(Map data) { * @return the number of attributes in the EventData. */ public int getSize() { - return eventData.size(); + return eventData.getData().size(); } /** @@ -259,7 +280,7 @@ public int getSize() { * @return an Iterator that can be used to access all the event attributes. */ public Iterator> getEntrySetIterator() { - return eventData.entrySet().iterator(); + return eventData.getData().entrySet().iterator(); } /** @@ -269,6 +290,18 @@ public Iterator> getEntrySetIterator() { * @return The Map of attributes in this EventData instance. */ public Map getEventMap() { + Map map = new HashMap(eventData.getData()); + map.put(EVENT_ID, eventData.getId()); + map.put(EVENT_TYPE, eventData.getType()); + map.put(EVENT_MESSAGE, eventData.getMessage()); + return map; + } + + /** + * Return the underlying StructuredData object. + * @return The StructuredData Object. + */ + public StructuredData getEventData() { return eventData; } @@ -282,19 +315,6 @@ public String toString() { return toXML(); } - /** - * Format the EventData for printing. - * @param format The format identifier. This implementation supports "XML". - * @return The formatted EventData. - */ - @Override - public String asString(String format) { - if ("XML".equalsIgnoreCase(format)) { - return toXML(); - } - return super.asString(format); - } - /** * Compare two EventData objects for equality. * @@ -313,252 +333,48 @@ public boolean equals(Object o) { return false; } Map map = (o instanceof EventData) ? ((EventData) o) - .eventData : (Map) o; + .getEventMap() : (Map) o; - return eventData.equals(map); + return map.equals(getEventMap()); } /** - * The EventMap makes the underlying StructuredData look like a Map which is convenient for serialization to XML. + * Compute the hashCode for this EventData instance. + * + * @return The hashcode for this EventData instance. */ - private final class EventMap implements Map { - - private final Map parent; - - public EventMap(Map map) { - parent = map; - } - - public int size() { - int count = 1; - if (getMessage() != null) { - ++count; - } - return parent.size() + count; - } - - public boolean isEmpty() { - return parent.isEmpty() && getId() == null && getMessage() == null; - } - - public Object get(Object key) { - if (EVENT_ID.equals(key)) { - return getId(); - } else if (EVENT_MESSAGE.equals(key)) { - return getMessage(); - } else if (EVENT_TYPE.equals(key)) { - return getType(); - } - return parent.get(key); - } - - public boolean containsKey(Object key) { - if (EVENT_ID.equals(key)) { - return getId() == null; - } else if (EVENT_MESSAGE.equals(key)) { - return getMessage() == null; - } else if (EVENT_TYPE.equals(key)) { - return getType() == null; - } - return parent.containsKey(key); - } - - public Object put(String key, Object newValue) { - Object oldValue = get(key); - if (EVENT_ID.equals(key)) { - setId(newValue.toString()); - } else if (EVENT_MESSAGE.equals(key)) { - setMessage(newValue.toString()); - } else if (EVENT_TYPE.equals(key)) { - setType(newValue.toString()); - } else { - parent.put(key, newValue); - } - return oldValue; - } - - public void putAll(Map map) { - for (Map.Entry entry : map.entrySet()) { - put(entry.getKey(), entry.getValue()); - } - } - - public Object remove(Object key) { - Object oldValue = get(key); - if (EVENT_ID.equals(key)) { - setId((StructuredDataId) null); - } else if (EVENT_MESSAGE.equals(key)) { - setMessage(null); - } else if (EVENT_TYPE.equals(key)) { - setType(null); - } else { - parent.remove(key); - } - return oldValue; - } - - public void clear() { - parent.clear(); - setMessage(null); - } - - public boolean containsValue(Object obj) { - return parent.containsValue(obj) || getId().equals(obj.toString()) || getMessage().equals(obj.toString()); - } - - public Set keySet() { - return new EventKeySet(); - } - - public Collection values() { - return new EventValues(); - } - - public Set> entrySet() { - return new EventEntrySet(); - } - - private class Entry implements Map.Entry { - private String key; - private Object value; - - public Entry(String key, Object value) { - this.key = key; - this.value = value; - } - public String getKey() { - return key; - } - - public Object getValue() { - return value; - } - - public Object setValue(Object newValue) { - Object oldValue = value; - if (key.equals(EVENT_ID)) { - setEventId(newValue.toString()); - } else if (key.equals(EVENT_MESSAGE)) { - setMessage(newValue.toString()); - } else if (key.equals(EVENT_TYPE)) { - setType(newValue.toString()); - } - value = newValue; - return oldValue; - } - } - - private abstract class EventIterator implements Iterator { - protected Iterator> iterator; - protected boolean iterId; - protected boolean iterMessage; - protected boolean iterType; - private Map.Entry current = null; - - public EventIterator() { - iterator = getData().entrySet().iterator(); - iterId = getId() != null; - iterMessage = getMessage() != null; - iterType = getType() != null; - } - - public Map.Entry nextEntry() { - if (iterId) { - iterId = false; - current = new Entry(EVENT_ID, getId()); - } else if (iterMessage) { - iterMessage = false; - current = new Entry(EVENT_MESSAGE, getMessage()); - } else if (iterType) { - iterType = false; - current = new Entry(EVENT_TYPE, getType()); - } else { - current = iterator.next(); - } - return current; - } - - public boolean hasNext() { - return iterator.hasNext() || iterId || iterMessage || iterType; - } - - public void remove() { - if (current != null) { - EventMap.this.remove(current.getKey()); - } - } - } + @Override + public int hashCode() { + return this.eventData.hashCode(); + } - private final class EventKeyIterator extends EventIterator { + private class StructuredEventData extends StructuredDataImpl { + private static final long serialVersionUID = 1093221292892071920L; - public String next() { - return nextEntry().getKey(); - } + public StructuredEventData() { } - private final class EventKeySet extends AbstractSet { - public Iterator iterator() { - return new EventKeyIterator(); - } - public int size() { - return EventMap.this.size(); - } - public boolean contains(Object o) { - return containsKey(o); - } - public boolean remove(Object o) { - return EventMap.this.remove(o) != null; - } - public void clear() { - EventMap.this.clear(); - } + public StructuredEventData(final String id, final String msg, final String type) { + super(id, msg, type); } - private final class EventValueIterator extends EventIterator { - - public Object next() { - return nextEntry().getValue(); - } + public void setId(String id) { + super.setId(id); } - private final class EventValues extends AbstractCollection { - public Iterator iterator() { - return new EventValueIterator(); - } - public int size() { - return EventMap.this.size(); - } - public boolean contains(Object o) { - return containsValue(o); - } - public void clear() { - EventMap.this.clear(); - } + public void setMessage(String msg) { + super.setMessage(msg); } - private final class EventEntrySetIterator extends EventIterator> { - - public Map.Entry next() { - return nextEntry(); - } + public void setType(String type) { + super.setType(type); } - private final class EventEntrySet extends AbstractSet> { - public Iterator> iterator() { - return new EventEntrySetIterator(); - } - public int size() { - return EventMap.this.size(); - } - public boolean contains(Object o) { - return containsKey(o); - } - public boolean remove(Object o) { - return EventMap.this.remove(o) != null; - } - public void clear() { - EventMap.this.clear(); + public String asString(String format) { + if (format.equals("XML")) { + return toXML(); } + return asString(format, null); } } } \ No newline at end of file diff --git a/slf4j-ext/src/main/java/org/slf4j/ext/EventLogger.java b/slf4j-ext/src/main/java/org/slf4j/ext/EventLogger.java index 82511a1fc..feff12cb2 100755 --- a/slf4j-ext/src/main/java/org/slf4j/ext/EventLogger.java +++ b/slf4j-ext/src/main/java/org/slf4j/ext/EventLogger.java @@ -42,11 +42,12 @@ public static void logEvent(EventData data) { * @param data The EventData. * @param format the format to use when converting the data to a String in Loggers that do not * support structured data. + * @deprecated Use logEvent(StructuredData data) instead. */ public static void logEvent(EventData data, String format) { if (eventLogger.instanceofXLAL) { ((XLocationAwareLogger) eventLogger.logger).log(EVENT_MARKER, FQCN, - XLocationAwareLogger.INFO_INT, data, format, null); + XLocationAwareLogger.INFO_INT, data.getEventData(), format, null); } else if (eventLogger.instanceofLAL) { ((LocationAwareLogger) eventLogger.logger).log(EVENT_MARKER, FQCN, LocationAwareLogger.INFO_INT, data.toXML(), null); diff --git a/slf4j-ext/src/test/java/org/slf4j/dummyExt/EventLoggerTest.java b/slf4j-ext/src/test/java/org/slf4j/dummyExt/EventLoggerTest.java index a957fc192..9560413bf 100644 --- a/slf4j-ext/src/test/java/org/slf4j/dummyExt/EventLoggerTest.java +++ b/slf4j-ext/src/test/java/org/slf4j/dummyExt/EventLoggerTest.java @@ -33,6 +33,8 @@ import org.apache.log4j.spi.LocationInfo; import org.apache.log4j.spi.LoggingEvent; import org.slf4j.MDC; +import org.slf4j.StructuredData; +import org.slf4j.StructuredDataImpl; import org.slf4j.ext.EventData; import org.slf4j.ext.EventLogger; @@ -107,20 +109,14 @@ public void testEventLogger() { } public void testEventLogger2() { - EventData data[] = new EventData[2]; - data[0] = new EventData(); - data[0].setEventType("Login"); - data[0].setEventId("1"); - data[0].setEventDateTime(new Date()); + StructuredData data[] = new StructuredData[2]; + data[0] = new StructuredDataImpl("1", null, "Login"); data[0].put("Userid", "TestUser"); - EventLogger.logEvent(data[0], "full"); + EventLogger.logEvent(data[0]); - data[1] = new EventData(); - data[1].setEventType("Update"); - data[1].setEventId("2"); - data[1].setEventDateTime(new Date()); + data[1] = new StructuredDataImpl("2", null, "Update"); data[1].put("FileName", "/etc/hosts"); - EventLogger.logEvent(data[1], "full"); + EventLogger.logEvent(data[1]); assertEquals(2, listAppender.list.size()); for (int i=0; i < 2; ++i) {