package ddth.dasp.common.logging; import java.io.Serializable; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import ddth.dasp.common.id.IdGenerator; /** * This class represents a profiling log entry. * * Log information: * <ul> * <li>Log's id: unique of the log entry. * <li>Request id: id of the associated request. * <li>Node's id: id of the node (app server) where the log entry originates. * <li>Client's id: id of the client where the request originates. * <li>Enduser's id: id of the enduser. * <li>Timestamp: timestamp when the log occurs. * </ul> * * @author NBThanh <btnguyen2k@gmail.com> */ public class ProfileLogEntry implements Serializable, Cloneable { private static final long serialVersionUID = "$Revision$".hashCode(); private static final IdGenerator idGen = IdGenerator .getInstance(IdGenerator.getMacAddr()); public static final String KEY_NAME = "NAME"; private static final String KEY_START_TIMESTAMP = "START_TIMESTAMP"; private static final String KEY_END_TIMESTAMP = "END_TIMESTAMP"; public static final String KEY_EXECUTION_TIME = "EXECUTION_TIME"; public static final String KEY_EXECUTION_TIME_MILLIS = "EXECUTION_TIME_MILLIS"; public static final String KEY_CHILDREN = "CHILDREN"; private static final String KEY_PARENT = "PARENT"; private List<Map<String, Object>> profilingData = new LinkedList<Map<String, Object>>(); private Object root = profilingData; private Object current = null; // private Object parent = null; /** * Unique of the log entry. */ private long id = idGen.generateId64(); /** * ID of the associated request. */ private String requestId; /** * ID of the node (app server) where the log entry originates. */ private String nodeId; /** * ID of the client where the request originates. Usually it's the client's * IP address. */ private String clientId; /** * ID of the enduser where the request originates. Usually it's the * enduser's IP address. */ private String enduserId; /** * Timestamp when the log occurs */ private long timestamp = System.currentTimeMillis(); /** * Constructs a new {@link ProfileLogEntry} instance. */ public ProfileLogEntry() { } /** * Constructs a new {@link ProfileLogEntry} instance and specifies an ID. * * @param id */ public ProfileLogEntry(long id) { this.id = id; } /** * Pushes a profiling data to the tree. * * @param name * String name */ public void push(String name) { synchronized (root) { Map<String, Object> entry = createEntry(name, current); // parent = current; if (current != null) { addChildren(current, entry); } else { profilingData.add(entry); } current = entry; } } @SuppressWarnings("unchecked") private static void addChildren(Object current, Object child) { Map<String, Object> map = (Map<String, Object>) current; Object children = map.get(KEY_CHILDREN); if (!(children instanceof List<?>)) { children = new LinkedList<Object>(); map.put(KEY_CHILDREN, children); } ((List<Object>) children).add(child); } private static Map<String, Object> createEntry(String name, Object parent) { Map<String, Object> entry = new HashMap<String, Object>(); entry.put(KEY_CHILDREN, new LinkedList<Map<String, Object>>()); entry.put(KEY_NAME, name); entry.put(KEY_PARENT, parent); entry.put(KEY_START_TIMESTAMP, System.nanoTime()); return entry; } /** * Pops the last profiling data from the tree. */ public void pop() { if (current == null) { throw new IllegalStateException(); } synchronized (root) { setField(current, KEY_END_TIMESTAMP, System.nanoTime()); current = getField(current, KEY_PARENT); // current = getParent(current); // parent = getParent(this.current); } } // @SuppressWarnings("unchecked") // private static Object getParent(Object current) { // if (current instanceof Map<?, ?>) { // Map<String, Object> map = (Map<String, Object>) current; // return map.get(KEY_PARENT); // } // return null; // } // @SuppressWarnings("unchecked") // private static void updateEndTimestamp(Object current) { // if (current instanceof Map<?, ?>) { // Map<String, Object> map = (Map<String, Object>) current; // map.put(KEY_END_TIMESTAMP, System.nanoTime()); // } // } @SuppressWarnings("unchecked") private static Object getField(Object current, String fieldName) { if (current instanceof Map<?, ?>) { Map<String, Object> map = (Map<String, Object>) current; return map.get(fieldName); } return null; } @SuppressWarnings("unchecked") private static void setField(Object current, String fieldName, Object value) { if (current instanceof Map<?, ?>) { Map<String, Object> map = (Map<String, Object>) current; map.put(fieldName, value); } } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getRequestId() { return requestId; } public void setRequestId(String requestId) { this.requestId = requestId; } public String getNodeId() { return nodeId; } public void setNodeId(String nodeId) { this.nodeId = nodeId; } public String getClientId() { return clientId; } public void setClientId(String clientId) { this.clientId = clientId; } public String getEnduserId() { return enduserId; } public void setEnduserId(String enduserId) { this.enduserId = enduserId; } public long getTimestamp() { return timestamp; } public Object[] getProfiling() { List<Object> result = new LinkedList<Object>(); synchronized (root) { Object current = this.current; while (current != null) { Object endTimestamp = getField(current, KEY_END_TIMESTAMP); if (endTimestamp == null || !(endTimestamp instanceof Number)) { endTimestamp = System.nanoTime(); setField(current, KEY_END_TIMESTAMP, endTimestamp); } current = getField(current, KEY_PARENT); // current = getParent(current); } for (Object entry : (List<?>) root) { result.add(buildProfilingNode(entry)); } } return result.toArray(); } static Object buildProfilingNode(Object entry) { if (entry == null) { return null; } Map<String, Object> result = new HashMap<String, Object>(); Object name = getField(entry, KEY_NAME); if (name == null) { name = ""; } result.put(KEY_NAME, name.toString()); Object startTimestamp = getField(entry, KEY_START_TIMESTAMP); if (startTimestamp == null || !(startTimestamp instanceof Number)) { startTimestamp = 0; } Object endTimestamp = getField(entry, KEY_END_TIMESTAMP); if (endTimestamp == null || !(endTimestamp instanceof Number)) { endTimestamp = 0; } long executionTime = ((Number) endTimestamp).longValue() - ((Number) startTimestamp).longValue(); result.put(KEY_EXECUTION_TIME, executionTime); result.put(KEY_EXECUTION_TIME_MILLIS, executionTime / 1e6); List<Object> children = new LinkedList<Object>(); Object _children = getField(entry, KEY_CHILDREN); if (_children == null || !(_children instanceof List<?>)) { _children = new LinkedList<Object>(); } for (Object _child : (List<?>) _children) { children.add(buildProfilingNode(_child)); } result.put(KEY_CHILDREN, children.toArray()); return result; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; } /** * {@inheritDoc} */ @Override public ProfileLogEntry clone() throws CloneNotSupportedException { synchronized (this) { ProfileLogEntry result = (ProfileLogEntry) super.clone(); result.clientId = this.clientId; result.enduserId = this.enduserId; result.id = this.id; result.nodeId = this.nodeId; result.requestId = this.requestId; result.timestamp = this.timestamp; // FIXME result.profilingData = this.profilingData; result.current = this.current; result.root = this.root; return result; } } @Override public int hashCode() { return new HashCodeBuilder(19, 81).append(id).append(nodeId) .toHashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || !(obj instanceof ProfileLogEntry)) { return false; } ProfileLogEntry other = (ProfileLogEntry) obj; return new EqualsBuilder().append(id, other.id) .append(requestId, other.requestId) .append(nodeId, other.nodeId).append(clientId, other.clientId) .append(enduserId, other.enduserId) .append(timestamp, other.timestamp).isEquals(); } }