Java tutorial
/* * Copyright 2014-2017 JKOOL, LLC. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jkoolcloud.tnt4j.streams.fields; import java.lang.reflect.Field; import java.net.InetAddress; import java.text.MessageFormat; import java.text.ParseException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import com.jkoolcloud.tnt4j.core.*; import com.jkoolcloud.tnt4j.format.JSONFormatter; import com.jkoolcloud.tnt4j.sink.DefaultEventSinkFactory; import com.jkoolcloud.tnt4j.sink.EventSink; import com.jkoolcloud.tnt4j.source.SourceType; import com.jkoolcloud.tnt4j.streams.utils.StreamsConstants; import com.jkoolcloud.tnt4j.streams.utils.StreamsResources; import com.jkoolcloud.tnt4j.streams.utils.TimestampFormatter; import com.jkoolcloud.tnt4j.streams.utils.Utils; import com.jkoolcloud.tnt4j.tracker.TimeTracker; import com.jkoolcloud.tnt4j.tracker.Tracker; import com.jkoolcloud.tnt4j.tracker.TrackingActivity; import com.jkoolcloud.tnt4j.tracker.TrackingEvent; /** * This class represents an {@link Trackable} entity (e.g. activity/event/snapshot) to record to JKool Cloud. * * @version $Revision: 3 $ */ public class ActivityInfo { private static final EventSink LOGGER = DefaultEventSinkFactory.defaultEventSink(ActivityInfo.class); private static final Map<String, String> HOST_CACHE = new ConcurrentHashMap<>(); private static final String LOCAL_SERVER_NAME_KEY = "LOCAL_SERVER_NAME_KEY"; // NON-NLS private static final String LOCAL_SERVER_IP_KEY = "LOCAL_SERVER_IP_KEY"; // NON-NLS private static final String TRANSPARENT_PROP_TYPE = "<TRANSPARENT>"; // NON-NLS private String serverName = null; private String serverIp = null; private String applName = null; private String userName = null; private String resourceName = null; private String eventName = null; private OpType eventType = null; private ActivityStatus eventStatus = null; private UsecTimestamp startTime = null; private UsecTimestamp endTime = null; private long elapsedTime = -1L; private OpCompCode compCode = null; private int reasonCode = 0; private String exception = null; private OpLevel severity = null; private String location = null; private Collection<String> correlator = null; private String trackingId = null; private String parentId = null; private Collection<String> tag = null; private Object message = null; private String msgCharSet = null; private String msgEncoding = null; private Integer msgLength = null; private String msgMimeType = null; private Integer processId = null; private Integer threadId = null; private String category = null; private boolean filteredOut = false; private static final TimeTracker ACTIVITY_TIME_TRACKER = TimeTracker.newTracker(1000, TimeUnit.HOURS.toMillis(8)); private Map<String, Property> activityProperties; private List<ActivityInfo> children; /** * Constructs a new ActivityInfo object. */ public ActivityInfo() { } /** * Applies the given value(s) for the specified field to the appropriate internal data field for reporting field to * the JKool Cloud. * * @param field * field to apply * @param value * value to apply for this field, which could be an array of objects if value for field consists of * multiple locations * * @throws ParseException * if an error parsing the specified value based on the field definition (e.g. does not match defined * format, etc.) */ public void applyField(ActivityField field, Object value) throws ParseException { LOGGER.log(OpLevel.TRACE, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.applying.field"), field, value); Object[] values = Utils.makeArray(Utils.simplifyValue(value)); List<ActivityFieldLocator> locators = field.getLocators(); if (values != null && CollectionUtils.isNotEmpty(locators)) { if (locators.size() > 1 && locators.size() != values.length) { throw new ParseException(StreamsResources.getStringFormatted(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.failed.parsing", field), 0); } ActivityFieldLocator locator; Object fValue; List<Object> fvList = new ArrayList<>(locators.size()); for (int v = 0; v < values.length; v++) { locator = locators.size() == 1 ? locators.get(0) : locators.get(v); fValue = formatValue(field, locator, values[v]); if (fValue == null && locator.isOptional()) { continue; } fvList.add(fValue); } values = fvList.toArray(); if (field.isEnumeration() && values.length > 1) { throw new ParseException(StreamsResources.getStringFormatted(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.multiple.enum.values", field), 0); } } Object fieldValue = Utils.simplifyValue(values); if (fieldValue == null) { LOGGER.log(OpLevel.TRACE, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.field.value.null"), field); return; } LOGGER.log(OpLevel.TRACE, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.applying.field.value"), field, Utils.toString(fieldValue)); fieldValue = transform(field, fieldValue); fieldValue = filterFieldValue(field, fieldValue); if (!field.isTransparent()) { setFieldValue(field, fieldValue); } else { addActivityProperty(field.getFieldTypeName(), fieldValue, TRANSPARENT_PROP_TYPE); } } /** * Transforms the value for the field using defined field transformations. * <p> * Note that field value there is combination of all field locators resolved values. Transformations defined for * particular locator is already performed by parser while resolving locator value. * * @param field * field whose value is to be transformed * @param fieldValue * field data value to transform * @return transformed field value */ protected Object transform(ActivityField field, Object fieldValue) { try { fieldValue = field.transformValue(fieldValue); } catch (Exception exc) { LOGGER.log(OpLevel.WARNING, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.transformation.failed"), field.getFieldTypeName(), Utils.toString(fieldValue), exc); } return fieldValue; } /** * Applies filed defined filtering rules and marks this activity as filtered out or sets field value to * {@code null}, if field is set as "optional" using attribute {@code required=false}. * * @param field * field instance to use filter definition * @param fieldValue * value to apply filters * @return value after filtering applied: {@code null} if value gets filtered out and field is optional, or same as * passed over parameters - otherwise * * @see com.jkoolcloud.tnt4j.streams.fields.ActivityField#filterValue(ActivityInfo, Object) */ protected Object filterFieldValue(ActivityField field, Object fieldValue) { try { return field.filterValue(this, fieldValue); } catch (Exception exc) { LOGGER.log(OpLevel.WARNING, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.filtering.failed"), field.getFieldTypeName(), Utils.toString(fieldValue), exc); return fieldValue; } } /** * Formats the value for the field based on the required internal data type of the field and the definition of the * field. * * @param field * field whose value is to be formatted * @param locator * locator information for value * @param value * raw value of field * @return formatted value of field in required internal data type */ protected Object formatValue(ActivityField field, ActivityFieldLocator locator, Object value) { if (value == null) { return null; } if (field.isEnumeration()) { if (value instanceof String) { String strValue = (String) value; value = StringUtils.containsOnly(strValue, "0123456789") ? Integer.valueOf(strValue) // NON-NLS : strValue.toUpperCase().trim(); } } StreamFieldType fieldType = field.getFieldType(); if (fieldType != null) { switch (fieldType) { case ElapsedTime: try { // Elapsed time needs to be converted to usec TimeUnit units = StringUtils.isEmpty(locator.getUnits()) ? TimeUnit.MICROSECONDS : TimeUnit.valueOf(locator.getUnits().toUpperCase()); if (!(value instanceof Number)) { value = Long.valueOf(Utils.toString(value)); } value = TimestampFormatter.convert((Number) value, units, TimeUnit.MICROSECONDS); } catch (Exception e) { } break; case ServerIp: if (value instanceof InetAddress) { value = ((InetAddress) value).getHostAddress(); } break; case ServerName: if (value instanceof InetAddress) { value = ((InetAddress) value).getHostName(); } break; default: break; } } return value; } /** * Sets field to specified value, handling any necessary conversions based on internal data type for field. * * @param field * field whose value is to be set * @param fieldValue * formatted value based on locator definition for field * * @throws ParseException * if there are any errors with conversion to internal format */ public void setFieldValue(ActivityField field, Object fieldValue) throws ParseException { if (isValueEmpty(fieldValue)) { return; } StreamFieldType fieldType = field.getFieldType(); if (fieldType != null) { switch (fieldType) { case Message: message = substitute(message, fieldValue); break; case EventName: eventName = substitute(eventName, getStringValue(fieldValue, field)); break; case EventType: eventType = substitute(eventType, Utils.mapOpType(fieldValue)); break; case EventStatus: ActivityStatus as = fieldValue instanceof ActivityStatus ? (ActivityStatus) fieldValue : ActivityStatus.valueOf(fieldValue); eventStatus = substitute(eventStatus, as); case ApplName: applName = substitute(applName, getStringValue(fieldValue, field)); break; case Correlator: addCorrelator(Utils.getTags(fieldValue)); break; case ElapsedTime: elapsedTime = substitute(elapsedTime, getNumberValue(fieldValue, Long.class)); break; case EndTime: endTime = substitute(endTime, getTimestampValue(fieldValue, field)); break; case Exception: exception = substitute(exception, getStringValue(fieldValue, field)); break; case Location: location = substitute(location, getStringValue(fieldValue, field)); break; case ReasonCode: reasonCode = substitute(reasonCode, getNumberValue(fieldValue, Integer.class)); break; case ResourceName: resourceName = substitute(resourceName, getStringValue(fieldValue, field)); break; case ServerIp: serverIp = substitute(serverIp, getStringValue(fieldValue, field)); break; case ServerName: serverName = substitute(serverName, getStringValue(fieldValue, field)); break; case Severity: OpLevel sev = fieldValue instanceof OpLevel ? (OpLevel) fieldValue : OpLevel.valueOf(fieldValue); severity = substitute(severity, sev); break; case TrackingId: trackingId = substitute(trackingId, getStringValue(fieldValue, field)); break; case StartTime: startTime = substitute(startTime, getTimestampValue(fieldValue, field)); break; case CompCode: OpCompCode cc = fieldValue instanceof OpCompCode ? (OpCompCode) fieldValue : OpCompCode.valueOf(fieldValue); compCode = substitute(compCode, cc); break; case Tag: addTag(Utils.getTags(fieldValue)); break; case UserName: userName = substitute(userName, getStringValue(fieldValue, field)); break; case MsgCharSet: msgCharSet = substitute(msgCharSet, getStringValue(fieldValue, field)); break; case MsgEncoding: msgEncoding = substitute(msgEncoding, getStringValue(fieldValue, field)); break; case MsgLength: msgLength = substitute(msgLength, getNumberValue(fieldValue, Integer.class)); break; case MsgMimeType: msgMimeType = substitute(msgMimeType, getStringValue(fieldValue, field)); break; case ProcessId: processId = substitute(processId, getNumberValue(fieldValue, Integer.class)); break; case ThreadId: threadId = substitute(threadId, getNumberValue(fieldValue, Integer.class)); break; case Category: category = substitute(category, getStringValue(fieldValue, field)); break; case ParentId: parentId = substitute(parentId, getStringValue(fieldValue, field)); break; default: throw new IllegalArgumentException(StreamsResources.getStringFormatted( StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.unrecognized.field", field)); } LOGGER.log(OpLevel.TRACE, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.set.field"), field, fieldValue); } else { addCustomActivityProperty(field, fieldValue); } } private static boolean isValueEmpty(Object fieldValue) { if (fieldValue != null) { Object[] va = fieldValue instanceof Object[] ? (Object[]) fieldValue : new Object[] { fieldValue }; for (Object ve : va) { if (ve != null) { return false; } } } return true; } private void addCustomActivityProperty(ActivityField field, Object fieldValue) throws ParseException { if (fieldValue instanceof Trackable) { addActivityProperty(field.getFieldTypeName(), fieldValue); } else if (fieldValue instanceof Map) { addPropertiesMap((Map<?, ?>) fieldValue, ""); } else { addActivityProperty(field.getFieldTypeName(), getPropertyValue(fieldValue, field), field.getValueType()); } } private void addPropertiesMap(Map<?, ?> pMap, String propPrefix) { for (Map.Entry<?, ?> pme : pMap.entrySet()) { String pKey = propPrefix + String.valueOf(pme.getKey()); if (pme.getValue() instanceof Map) { addPropertiesMap((Map<?, ?>) pme.getValue(), pKey + StreamsConstants.DEFAULT_PATH_DELIM); } else { addActivityProperty(pKey, pme.getValue()); } } } private static Object getPropertyValue(Object fieldValue, ActivityField field) throws ParseException { ActivityFieldLocator fmLocator = field.getMasterLocator(); if (fmLocator != null) { switch (fmLocator.getDataType()) { case Number: return getNumberValue(fieldValue); case DateTime: case Timestamp: return getTimestampValue(fieldValue, field); case Binary: default: return getStringValue(fieldValue, field); } } return getStringValue(fieldValue, field); } private static UsecTimestamp getTimestampValue(Object fieldValue, ActivityField field) throws ParseException { ActivityFieldLocator fmLocator = field.getMasterLocator(); return fieldValue instanceof UsecTimestamp ? (UsecTimestamp) fieldValue : TimestampFormatter.parse(fmLocator == null ? null : fmLocator.getFormat(), getStringValue(fieldValue, field), fmLocator == null ? null : fmLocator.getTimeZone(), fmLocator == null ? null : fmLocator.getLocale()); } private static String substitute(String value, String newValue) { return StringUtils.isEmpty(newValue) ? value : newValue; } private static <T> T substitute(T value, T newValue) { return newValue == null ? value : newValue; } private static String getStringValue(Object value, ActivityField field) { if (value instanceof Object[]) { return formatValuesArray((Object[]) value, field); } else if (value instanceof byte[]) { Utils.encodeHex((byte[]) value); } else if (value != null && value.getClass().isArray()) { return ArrayUtils.toString(value); } return Utils.toString(value); } private static String formatValuesArray(Object[] vArray, ActivityField field) { if (StringUtils.isNotEmpty(field.getFormattingPattern())) { return formatArrayPattern(field.getFormattingPattern(), vArray); } else { StringBuilder sb = new StringBuilder(); for (int v = 0; v < vArray.length; v++) { if (v > 0) { sb.append(field.getSeparator()); } if (vArray[v] instanceof UsecTimestamp) { ActivityFieldLocator locator = field.getLocators().size() == 1 ? field.getLocators().get(0) : v >= 0 && v < field.getLocators().size() ? field.getLocators().get(v) : null; // TODO String format = locator == null ? null : locator.getFormat(); if (StringUtils.isNotEmpty(format)) { sb.append(((UsecTimestamp) vArray[v]).toString(format)); } } else { sb.append(vArray[v] == null ? "" : Utils.toString(vArray[v])); // NON-NLS } } return sb.toString(); } } private static String formatArrayPattern(String pattern, Object[] vArray) { MessageFormat mf = new MessageFormat(pattern); try { Field f = mf.getClass().getDeclaredField("maxOffset"); f.setAccessible(true); int maxOffset = f.getInt(mf); if (maxOffset >= 0) { f = mf.getClass().getDeclaredField("argumentNumbers"); f.setAccessible(true); int[] ana = (int[]) f.get(mf); int maxIndex = ana[maxOffset]; if (maxIndex >= vArray.length) { LOGGER.log(OpLevel.WARNING, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.formatting.arguments.mismatch"), pattern, maxIndex, ArrayUtils.getLength(vArray)); } } } catch (Exception exc) { } return mf.format(vArray); } /** * Adds activity item property to item properties map. Properties from map are transferred as tracking event * properties when {@link #buildTrackable(Tracker, Collection)} is invoked. Same as invoking * {@link #addActivityProperty(String, Object, String)} setting value type to {@code null}. * * @param propName * activity item property key * @param propValue * activity item property value * @return previous property value replaced by {@code propValue} or {@code null} if there was no such activity * property set * * @see Map#put(Object, Object) * @see #buildTrackable(Tracker, Collection) * @see #addActivityProperty(String, Object, String) */ public Object addActivityProperty(String propName, Object propValue) { return addActivityProperty(propName, propValue, null); } /** * Adds activity item property to item properties map. Properties from map are transferred as tracking event * properties when {@link #buildTrackable(Tracker, Collection)} is invoked. * * @param propName * activity item property key * @param propValue * activity item property value * @param valueType * activity item property value type from {@link com.jkoolcloud.tnt4j.core.ValueTypes} set * @return previous property value replaced by {@code propValue} or {@code null} if there was no such activity * property set * * @see Map#put(Object, Object) * @see #buildTrackable(Tracker, Collection) * @see com.jkoolcloud.tnt4j.core.ValueTypes */ public Object addActivityProperty(String propName, Object propValue, String valueType) { if (activityProperties == null) { activityProperties = new HashMap<>(); } Property p = new Property(propName, wrapPropertyValue(propValue), StringUtils.isEmpty(valueType) ? getDefaultValueType(propValue) : valueType); Property prevValue = activityProperties.put(propName, p); LOGGER.log(OpLevel.TRACE, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.set.property"), propName, Utils.toString(p.getValue()), p.getValueType(), Utils.toString(prevValue)); return prevValue; } private static String getDefaultValueType(Object propValue) { if (propValue instanceof UsecTimestamp) { return ValueTypes.VALUE_TYPE_TIMESTAMP; } return ValueTypes.VALUE_TYPE_NONE; } private static Object wrapPropertyValue(Object propValue) { if (propValue instanceof UsecTimestamp) { return ((UsecTimestamp) propValue).getTimeUsec(); } return propValue; } /** * Appends activity item tags collection with provided tag strings array contents. * * @param tags * tag strings array */ public void addTag(String... tags) { this.tag = addStrings(this.tag, tags); } /** * Appends activity item correlators collection with provided correlator strings array contents. * * @param correlators * correlator strings array */ public void addCorrelator(String... correlators) { this.correlator = addStrings(this.correlator, correlators); } private static Collection<String> addStrings(Collection<String> collection, String... strings) { if (ArrayUtils.isNotEmpty(strings)) { if (collection == null) { collection = new ArrayList<>(); } for (String str : strings) { if (StringUtils.isNotEmpty(str)) { collection.add(str.trim()); } } } return collection; } /** * Makes fully qualified name of activity source. Name is made from stream parsed data attributes. * * @param resolveOverDNS * flag indicating whether to use DNS to resolve server names and IP addresses * * @return fully qualified name of this activity source, or {@code null} if no source defining attributes where * parsed from stream. */ public String getSourceFQN(boolean resolveOverDNS) { resolveServer(resolveOverDNS); StringBuilder fqnB = new StringBuilder(); addSourceValue(fqnB, SourceType.APPL, applName); addSourceValue(fqnB, SourceType.USER, userName); addSourceValue(fqnB, SourceType.SERVER, serverName); addSourceValue(fqnB, SourceType.NETADDR, serverIp); addSourceValue(fqnB, SourceType.GEOADDR, location); String fqn = fqnB.toString(); return StringUtils.isEmpty(fqn) ? null : fqn; } private static void addSourceValue(StringBuilder sb, SourceType type, String value) { if (StringUtils.isNotEmpty(value)) { if (sb.length() > 0) { sb.append('#'); // NON-NLS } sb.append(type).append('=').append(value); // NON-NLS } } /** * Creates the appropriate data package {@link com.jkoolcloud.tnt4j.tracker.TrackingActivity}, * {@link com.jkoolcloud.tnt4j.tracker.TrackingEvent} or {@link com.jkoolcloud.tnt4j.core.PropertySnapshot} using * the specified tracker for this activity data entity to be sent to JKool Cloud. * * @param tracker * {@link com.jkoolcloud.tnt4j.tracker.Tracker} instance to be used to build * {@link com.jkoolcloud.tnt4j.core.Trackable} activity data package * @param chTrackables * collection to add built child trackables, not included into parent trackable and transmitted * separately, e.g., activity child events * * @return trackable instance made from this activity entity data * @throws java.lang.IllegalArgumentException * if {@code tracker} is null * @see com.jkoolcloud.tnt4j.streams.outputs.JKCloudActivityOutput#logItem(ActivityInfo) */ public Trackable buildTrackable(Tracker tracker, Collection<Trackable> chTrackables) { if (tracker == null) { throw new IllegalArgumentException( StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.tracker.null")); } resolveServer(false); determineTimes(); String trackId = StringUtils.isEmpty(trackingId) ? tracker.newUUID() : trackingId; if (eventType == OpType.ACTIVITY) { return buildActivity(tracker, eventName, trackId, chTrackables); } else if (eventType == OpType.SNAPSHOT) { return buildSnapshot(tracker, eventName, trackId); } else { return buildEvent(tracker, eventName, trackId, chTrackables); } } /** * Creates the appropriate data package {@link com.jkoolcloud.tnt4j.tracker.TrackingActivity}, * {@link com.jkoolcloud.tnt4j.tracker.TrackingEvent} or {@link com.jkoolcloud.tnt4j.core.PropertySnapshot} using * the specified tracker for this activity data entity to be sent to JKool Cloud. * <p> * Does same as {@link #buildTrackable(Tracker, Collection)} where {@code chTrackables} list is {@code null}. * * @param tracker * {@link com.jkoolcloud.tnt4j.tracker.Tracker} instance to be used to build * {@link com.jkoolcloud.tnt4j.core.Trackable} activity data package * * @return trackable instance made from this activity entity data * @throws java.lang.IllegalArgumentException * if {@code tracker} is null * @see #buildTrackable(Tracker, Collection) */ public Trackable buildTrackable(Tracker tracker) { return buildTrackable(tracker, null); } /** * Builds {@link TrackingEvent} for activity data recording. * * @param tracker * communication gateway to use to record activity * @param trackName * name of tracking event * @param trackId * identifier (signature) of tracking event * @param chTrackables * collection to add built child trackables, not included into parent event and transmitted separately * @return tracking event instance */ protected TrackingEvent buildEvent(Tracker tracker, String trackName, String trackId, Collection<Trackable> chTrackables) { TrackingEvent event = tracker.newEvent(severity == null ? OpLevel.INFO : severity, trackName, (String) null, (String) null, (Object[]) null); event.setTrackingId(trackId); event.setParentId(parentId); // event.setCorrelator(CollectionUtils.isEmpty(correlator) ? Collections.singletonList(trackId) : correlator); if (CollectionUtils.isNotEmpty(correlator)) { event.setCorrelator(correlator); } if (CollectionUtils.isNotEmpty(tag)) { event.setTag(tag); } if (message != null) { if (message instanceof byte[]) { byte[] binData = (byte[]) message; event.setMessage(binData, (Object[]) null); } else { String strData = Utils.toString(message); event.setMessage(strData, (Object[]) null); } if (msgLength != null) { event.setSize(msgLength); } } if (StringUtils.isNotEmpty(msgMimeType)) { event.setMimeType(msgMimeType); } if (StringUtils.isNotEmpty(msgEncoding)) { event.setEncoding(msgEncoding); } if (StringUtils.isNotEmpty(msgCharSet)) { event.setCharset(msgCharSet); } event.getOperation().setCompCode(compCode == null ? OpCompCode.SUCCESS : compCode); event.getOperation().setReasonCode(reasonCode); event.getOperation().setType(eventType == null ? OpType.EVENT : eventType); event.getOperation().setException(exception); if (StringUtils.isNotEmpty(location)) { event.getOperation().setLocation(location); } event.getOperation().setResource(resourceName); event.getOperation().setUser(StringUtils.isEmpty(userName) ? tracker.getSource().getUser() : userName); event.getOperation().setTID(threadId == null ? Thread.currentThread().getId() : threadId); event.getOperation().setPID(processId == null ? Utils.getVMPID() : processId); // event.getOperation().setSeverity(severity == null ? OpLevel.INFO : // severity); if (eventStatus != null) { addActivityProperty(JSONFormatter.JSON_STATUS_FIELD, eventStatus); } event.start(startTime); event.stop(endTime, elapsedTime); if (activityProperties != null) { for (Property ap : activityProperties.values()) { if (isNotTransparentProperty(ap)) { if (ap.getValue() instanceof Snapshot) { event.getOperation().addSnapshot((Snapshot) ap.getValue()); } else { event.getOperation().addProperty(ap); } } } } if (hasChildren()) { for (ActivityInfo child : children) { Trackable cTrackable = buildChild(tracker, child, trackId); boolean consumed = addTrackableChild(event, cTrackable); if (!consumed && chTrackables != null) { chTrackables.add(cTrackable); } } } return event; } /** * Builds {@link TrackingActivity} for activity data recording. * * @param tracker * communication gateway to use to record activity * @param trackName * name of tracking activity * @param trackId * identifier (signature) of tracking activity * @param chTrackables * collection to add built child trackables, not included into parent activity and transmitted separately * @return tracking activity instance */ private TrackingActivity buildActivity(Tracker tracker, String trackName, String trackId, Collection<Trackable> chTrackables) { TrackingActivity activity = tracker.newActivity(severity == null ? OpLevel.INFO : severity, trackName); activity.setTrackingId(trackId); activity.setParentId(parentId); // activity.setCorrelator(CollectionUtils.isEmpty(correlator) ? Collections.singletonList(trackId) : // correlator); if (CollectionUtils.isNotEmpty(correlator)) { activity.setCorrelator(correlator); } if (CollectionUtils.isNotEmpty(tag)) { addActivityProperty(JSONFormatter.JSON_MSG_TAG_FIELD, tag); } if (message != null) { String strData; if (message instanceof byte[]) { byte[] binData = (byte[]) message; strData = Utils.base64EncodeStr(binData); msgEncoding = "base64"; // NON-NLS msgMimeType = "application/octet-stream"; // NON-NLS } else { strData = Utils.toString(message); } addActivityProperty(JSONFormatter.JSON_MSG_TEXT_FIELD, strData); addActivityProperty(JSONFormatter.JSON_MSG_SIZE_FIELD, msgLength == null ? strData.length() : msgLength); } if (StringUtils.isNotEmpty(msgMimeType)) { addActivityProperty(JSONFormatter.JSON_MSG_MIME_FIELD, msgMimeType); } if (StringUtils.isNotEmpty(msgEncoding)) { addActivityProperty(JSONFormatter.JSON_MSG_ENC_FIELD, msgEncoding); } if (StringUtils.isNotEmpty(msgCharSet)) { addActivityProperty(JSONFormatter.JSON_MSG_CHARSET_FIELD, msgCharSet); } activity.setCompCode(compCode == null ? OpCompCode.SUCCESS : compCode); activity.setReasonCode(reasonCode); activity.setType(eventType); activity.setStatus(StringUtils.isNotEmpty(exception) ? ActivityStatus.EXCEPTION : eventStatus == null ? ActivityStatus.END : eventStatus); activity.setException(exception); if (StringUtils.isNotEmpty(location)) { activity.setLocation(location); } activity.setResource(resourceName); activity.setUser(StringUtils.isEmpty(userName) ? tracker.getSource().getUser() : userName); activity.setTID(threadId == null ? Thread.currentThread().getId() : threadId); activity.setPID(processId == null ? Utils.getVMPID() : processId); // activity.setSeverity(severity == null ? OpLevel.INFO : severity); activity.start(startTime); activity.stop(endTime, elapsedTime); if (activityProperties != null) { for (Property ap : activityProperties.values()) { if (isNotTransparentProperty(ap)) { if (ap.getValue() instanceof Trackable) { activity.add((Trackable) ap.getValue()); } else { activity.addProperty(ap); } } } } if (hasChildren()) { for (ActivityInfo child : children) { Trackable cTrackable = buildChild(tracker, child, trackId); boolean consumed = addTrackableChild(activity, cTrackable); if (!consumed && chTrackables != null) { chTrackables.add(cTrackable); } } } return activity; } private static boolean addTrackableChild(Trackable pTrackable, Trackable chTrackable) { if (pTrackable instanceof TrackingEvent) { return addEventChild((TrackingEvent) pTrackable, chTrackable); } else if (pTrackable instanceof Activity) { return addActivityChild((Activity) pTrackable, chTrackable); } else if (pTrackable instanceof Snapshot) { return addSnapshotChild((Snapshot) pTrackable, chTrackable); } else { LOGGER.log(OpLevel.WARNING, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.invalid.child"), chTrackable == null ? null : chTrackable.getClass(), pTrackable == null ? null : pTrackable.getClass()); } return false; } private static boolean addEventChild(TrackingEvent event, Trackable chTrackable) { if (chTrackable instanceof Snapshot) { event.getOperation().addSnapshot((Snapshot) chTrackable); return true; } else { LOGGER.log(OpLevel.WARNING, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.invalid.child"), chTrackable == null ? null : chTrackable.getClass(), event.getClass()); } return false; } private static boolean addActivityChild(Activity activity, Trackable chTrackable) { if (chTrackable != null) { activity.add(chTrackable); return chTrackable instanceof Snapshot; } else { LOGGER.log(OpLevel.WARNING, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.invalid.child"), chTrackable == null ? null : chTrackable.getClass(), activity.getClass()); } return false; } private static boolean addSnapshotChild(Snapshot snapshot, Trackable chTrackable) { LOGGER.log(OpLevel.WARNING, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.invalid.child"), chTrackable == null ? null : chTrackable.getClass(), snapshot.getClass()); return false; } private static Trackable buildChild(Tracker tracker, ActivityInfo child, String parentId) { child.parentId = parentId; // child.resolveServer(false); child.determineTimes(); return child.buildTrackable(tracker); } /** * Builds {@link Snapshot} for activity data recording. * * @param tracker * communication gateway to use to record snapshot * @param trackName * name of snapshot * @param trackId * identifier (signature) of snapshot * @return snapshot instance */ protected Snapshot buildSnapshot(Tracker tracker, String trackName, String trackId) { PropertySnapshot snapshot = category != null ? (PropertySnapshot) tracker.newSnapshot(category, trackName) : (PropertySnapshot) tracker.newSnapshot(trackName); snapshot.setTrackingId(trackId); snapshot.setParentId(parentId); snapshot.setSeverity(severity == null ? OpLevel.INFO : severity); // snapshot.setCorrelator(CollectionUtils.isEmpty(correlator) ? Collections.singletonList(trackId) : // correlator); if (CollectionUtils.isNotEmpty(correlator)) { snapshot.setCorrelator(correlator); } if (CollectionUtils.isNotEmpty(tag)) { snapshot.add(JSONFormatter.JSON_MSG_TAG_FIELD, tag); } if (message != null) { String strData; if (message instanceof byte[]) { byte[] binData = (byte[]) message; strData = Utils.base64EncodeStr(binData); msgEncoding = "base64"; // NON-NLS msgMimeType = "application/octet-stream"; // NON-NLS } else { strData = Utils.toString(message); } addActivityProperty(JSONFormatter.JSON_MSG_TEXT_FIELD, strData); addActivityProperty(JSONFormatter.JSON_MSG_SIZE_FIELD, msgLength == null ? strData.length() : msgLength); } if (StringUtils.isNotEmpty(msgMimeType)) { snapshot.add(JSONFormatter.JSON_MSG_MIME_FIELD, msgMimeType); } if (StringUtils.isNotEmpty(msgEncoding)) { snapshot.add(JSONFormatter.JSON_MSG_ENC_FIELD, msgEncoding); } if (StringUtils.isNotEmpty(msgCharSet)) { snapshot.add(JSONFormatter.JSON_MSG_CHARSET_FIELD, msgCharSet); } snapshot.add(JSONFormatter.JSON_COMP_CODE_FIELD, compCode == null ? OpCompCode.SUCCESS : compCode); snapshot.add(JSONFormatter.JSON_REASON_CODE_FIELD, reasonCode); snapshot.add(JSONFormatter.JSON_TYPE_FIELD, eventType); snapshot.add(JSONFormatter.JSON_EXCEPTION_FIELD, exception); if (StringUtils.isNotEmpty(location)) { snapshot.add(JSONFormatter.JSON_LOCATION_FIELD, location); } snapshot.add(JSONFormatter.JSON_RESOURCE_FIELD, resourceName); snapshot.add(JSONFormatter.JSON_USER_FIELD, StringUtils.isEmpty(userName) ? tracker.getSource().getUser() : userName); snapshot.add(JSONFormatter.JSON_TID_FIELD, threadId == null ? Thread.currentThread().getId() : threadId); snapshot.add(JSONFormatter.JSON_PID_FIELD, processId == null ? Utils.getVMPID() : processId); snapshot.setTimeStamp(startTime == null ? (endTime == null ? UsecTimestamp.now() : endTime) : startTime); if (eventStatus != null) { addActivityProperty(JSONFormatter.JSON_STATUS_FIELD, eventStatus); } if (activityProperties != null) { for (Property ap : activityProperties.values()) { if (isNotTransparentProperty(ap)) { snapshot.add(ap); } } } return snapshot; } private static boolean isNotTransparentProperty(Property prop) { return prop != null && !prop.getValueType().equals(TRANSPARENT_PROP_TYPE); } /** * Resolves server name and/or IP Address based on values specified. * * @param resolveOverDNS * flag indicating whether to use DNS to resolve server names and IP addresses */ private void resolveServer(boolean resolveOverDNS) { if (StringUtils.isEmpty(serverName) && StringUtils.isEmpty(serverIp)) { serverName = HOST_CACHE.get(LOCAL_SERVER_NAME_KEY); serverIp = HOST_CACHE.get(LOCAL_SERVER_IP_KEY); if (serverName == null) { serverName = Utils.getLocalHostName(); HOST_CACHE.put(LOCAL_SERVER_NAME_KEY, serverName); } if (serverIp == null) { serverIp = Utils.getLocalHostAddress(); HOST_CACHE.put(LOCAL_SERVER_IP_KEY, serverIp); } } else if (StringUtils.isEmpty(serverName)) { if (resolveOverDNS) { try { serverName = HOST_CACHE.get(serverIp); if (StringUtils.isEmpty(serverName)) { serverName = Utils.resolveAddressToHostName(serverIp); if (StringUtils.isEmpty(serverName)) { // Add entry so we don't repeatedly attempt to look // up unresolvable IP Address HOST_CACHE.put(serverIp, ""); } else { HOST_CACHE.put(serverIp, serverName); HOST_CACHE.put(serverName, serverIp); } } } catch (Exception e) { serverName = serverIp; } } else { serverName = serverIp; } } else if (StringUtils.isEmpty(serverIp)) { if (resolveOverDNS) { serverIp = HOST_CACHE.get(serverName); if (StringUtils.isEmpty(serverIp)) { serverIp = Utils.resolveHostNameToAddress(serverName); if (StringUtils.isEmpty(serverIp)) { // Add entry so we don't repeatedly attempt to look up // unresolvable host name HOST_CACHE.put(serverName, ""); } else { HOST_CACHE.put(serverIp, serverName); HOST_CACHE.put(serverName, serverIp); } } } } if (StringUtils.isEmpty(serverIp)) { serverIp = " "; // prevents streams API from resolving it to the local IP address } } /** * Computes the unspecified operation times and/or elapsed time based on the specified ones. */ private void determineTimes() { if (elapsedTime < 0L) { long elapsedTimeNano = StringUtils.isEmpty(resourceName) ? TimeTracker.hitAndGet() : ACTIVITY_TIME_TRACKER.hitAndGet(resourceName); elapsedTime = TimestampFormatter.convert(elapsedTimeNano, TimeUnit.NANOSECONDS, TimeUnit.MICROSECONDS); } if (endTime == null) { if (startTime != null) { endTime = new UsecTimestamp(startTime); endTime.add(0L, elapsedTime); } else { endTime = new UsecTimestamp(); } } if (startTime == null) { startTime = new UsecTimestamp(endTime); startTime.subtract(0L, elapsedTime); } } // private static Integer getIntValue(Object value) { // return value instanceof Number ? ((Number) value).intValue() : Integer.parseInt(Utils.toString(value)); // } // // private static Long getLongValue(Object value) { // return value instanceof Number ? ((Number) value).longValue() : Long.parseLong(Utils.toString(value)); // } private static Number getNumberValue(Object value) { return value instanceof Number ? (Number) value : NumberUtils.createNumber(Utils.toString(value)); } private static <T extends Number> T getNumberValue(Object value, Class<T> clazz) { Number num = value instanceof Number ? (Number) value : NumberUtils.createNumber(Utils.toString(value)); return Utils.castNumber(num, clazz); } /** * Merges activity info data fields values. Values of fields are changed only if they currently hold default * (initial) value. * * @param otherAi * activity info object to merge into this one */ public void merge(ActivityInfo otherAi) { if (StringUtils.isEmpty(serverName)) { serverName = otherAi.serverName; } if (StringUtils.isEmpty(serverIp)) { serverIp = otherAi.serverIp; } if (StringUtils.isEmpty(applName)) { applName = otherAi.applName; } if (StringUtils.isEmpty(userName)) { userName = otherAi.userName; } if (StringUtils.isEmpty(resourceName)) { resourceName = otherAi.resourceName; } if (StringUtils.isEmpty(eventName)) { eventName = otherAi.eventName; } if (eventType == null) { eventType = otherAi.eventType; } if (eventStatus == null) { eventStatus = otherAi.eventStatus; } if (startTime == null) { startTime = otherAi.startTime; } if (endTime == null) { endTime = otherAi.endTime; } if (elapsedTime == -1L) { elapsedTime = otherAi.elapsedTime; } if (compCode == null) { compCode = otherAi.compCode; } if (reasonCode == 0) { reasonCode = otherAi.reasonCode; } if (StringUtils.isEmpty(exception)) { exception = otherAi.exception; } if (severity == null) { severity = otherAi.severity; } if (StringUtils.isEmpty(location)) { location = otherAi.location; } if (otherAi.correlator != null) { if (correlator == null) { correlator = new ArrayList<>(); } correlator.addAll(otherAi.correlator); } if (StringUtils.isEmpty(trackingId)) { trackingId = otherAi.trackingId; } if (otherAi.tag != null) { if (tag == null) { tag = new ArrayList<>(); } tag.addAll(otherAi.tag); } if (message == null) { message = otherAi.message; } if (StringUtils.isEmpty(msgCharSet)) { msgCharSet = otherAi.msgCharSet; } if (StringUtils.isEmpty(msgEncoding)) { msgEncoding = otherAi.msgEncoding; } if (msgLength == null) { msgLength = otherAi.msgLength; } if (StringUtils.isEmpty(msgMimeType)) { msgMimeType = otherAi.msgMimeType; } if (processId == null) { processId = otherAi.processId; } if (threadId == null) { threadId = otherAi.threadId; } if (StringUtils.isEmpty(category)) { category = otherAi.category; } if (StringUtils.isEmpty(parentId)) { parentId = otherAi.parentId; } filteredOut |= otherAi.filteredOut; if (otherAi.activityProperties != null) { if (activityProperties == null) { activityProperties = new HashMap<>(); } activityProperties.putAll(otherAi.activityProperties); } } /** * Merges activity info data fields values and child activity entities. Values of fields are changed only if they * currently hold default (initial) value. * * @param otherAi * activity info object to merge into this one * * @see #merge(ActivityInfo) */ public void mergeAll(ActivityInfo otherAi) { merge(otherAi); if (otherAi.hasChildren()) { if (children == null) { children = new ArrayList<>(); } children.addAll(otherAi.children); } } /** * Gets server name. * * @return the server name */ public String getServerName() { return serverName; } /** * Gets server ip. * * @return the server ip */ public String getServerIp() { return serverIp; } /** * Gets application name. * * @return the application name */ public String getApplName() { return applName; } /** * Gets user name. * * @return the user name */ public String getUserName() { return userName; } /** * Gets resource name. * * @return the resource name */ public String getResourceName() { return resourceName; } /** * Gets event name. * * @return the event name */ public String getEventName() { return eventName; } /** * Sets event name. * * @param eventName * the event name */ public void setEventName(String eventName) { this.eventName = eventName; } /** * Gets event type. * * @return the event type */ public OpType getEventType() { return eventType; } /** * Gets event status. * * @return the event status */ public ActivityStatus getEventStatus() { return eventStatus; } /** * Gets start time. * * @return the start time */ public UsecTimestamp getStartTime() { return startTime; } /** * Gets end time. * * @return the end time */ public UsecTimestamp getEndTime() { return endTime; } /** * Gets elapsed time. * * @return the elapsed time */ public long getElapsedTime() { return elapsedTime; } /** * Gets activity completion code. * * @return the activity completion code */ public OpCompCode getCompCode() { return compCode; } /** * Gets reason code. * * @return the reason code */ public int getReasonCode() { return reasonCode; } /** * Gets exception/error message. * * @return the exception/error message */ public String getException() { return exception; } /** * Gets severity. * * @return the severity */ public OpLevel getSeverity() { return severity; } /** * Gets location. * * @return the location */ public String getLocation() { return location; } /** * Gets tracking identifier. * * @return the tracking identifier */ public String getTrackingId() { return trackingId; } /** * Gets activity tag strings collection. * * @return the activity tag strings collection */ public Collection<String> getTag() { return tag; } /** * Gets activity correlator strings collection. * * @return the activity correlator string collection */ public Collection<String> getCorrelator() { return correlator; } /** * Gets activity message data. * * @return the activity message data */ public Object getMessage() { return message; } /** * Gets message char set. * * @return the message char set */ public String getMsgCharSet() { return msgCharSet; } /** * Gets message encoding. * * @return the message encoding */ public String getMsgEncoding() { return msgEncoding; } /** * Gets message length. * * @return the message length */ public int getMsgLength() { return msgLength; } /** * Gets message MIME type. * * @return the message MIME type */ public String getMsgMimeType() { return msgMimeType; } /** * Gets process identifier. * * @return the process identifier */ public Integer getProcessId() { return processId; } /** * Gets thread identifier. * * @return the thread identifier */ public Integer getThreadId() { return threadId; } /** * Gets activity category (e.g., snapshot category). * * @return the activity category */ public String getCategory() { return category; } /** * Gets parent activity identifier. * * @return the parent activity identifier */ public String getParentId() { return parentId; } /** * Returns activity filtered out flag value. * * @return activity filtered out flag value */ public boolean isFilteredOut() { return filteredOut; } /** * Sets activity filtered out flag value. * * @param filteredOut * {@code true} if activity is filtered out, {@code false} otherwise */ public void setFiltered(boolean filteredOut) { this.filteredOut = filteredOut; } /** * Adds child activity entity data package. * * @param ai * activity entity object containing child data */ public void addChild(ActivityInfo ai) { if (children == null) { children = new ArrayList<>(); } children.add(ai); } /** * Returns list of child activity entities. * * @return list of child activity entities */ public List<ActivityInfo> getChildren() { return children; } /** * Checks whether this activity entity has any child activity entities added. * * @return {@code false} if children list is {@code null} or empty, {@code true} - otherwise */ public boolean hasChildren() { return CollectionUtils.isNotEmpty(children); } @Override public String toString() { final StringBuilder sb = new StringBuilder("ActivityInfo{"); // NON-NLS sb.append("serverName=").append(Utils.sQuote(serverName)); // NON-NLS sb.append(", serverIp=").append(Utils.sQuote(serverIp)); // NON-NLS sb.append(", applName=").append(Utils.sQuote(applName)); // NON-NLS sb.append(", userName=").append(Utils.sQuote(userName)); // NON-NLS sb.append(", resourceName=").append(Utils.sQuote(resourceName)); // NON-NLS sb.append(", eventName=").append(Utils.sQuote(eventName)); // NON-NLS sb.append(", eventType=").append(eventType); // NON-NLS sb.append(", eventStatus=").append(eventStatus); // NON-NLS sb.append(", startTime=").append(startTime); // NON-NLS sb.append(", endTime=").append(endTime); // NON-NLS sb.append(", elapsedTime=").append(elapsedTime); // NON-NLS sb.append(", compCode=").append(compCode); // NON-NLS sb.append(", reasonCode=").append(reasonCode); // NON-NLS sb.append(", exception=").append(Utils.sQuote(exception)); // NON-NLS sb.append(", severity=").append(severity); // NON-NLS sb.append(", location=").append(Utils.sQuote(location)); // NON-NLS sb.append(", correlator=").append(correlator); // NON-NLS sb.append(", trackingId=").append(Utils.sQuote(trackingId)); // NON-NLS sb.append(", parentId=").append(Utils.sQuote(parentId)); // NON-NLS sb.append(", tag=").append(tag); // NON-NLS sb.append(", message=").append(message); // NON-NLS sb.append(", msgCharSet=").append(Utils.sQuote(msgCharSet)); // NON-NLS sb.append(", msgEncoding=").append(Utils.sQuote(msgEncoding)); // NON-NLS sb.append(", msgLength=").append(msgLength); // NON-NLS sb.append(", msgMimeType=").append(Utils.sQuote(msgMimeType)); // NON-NLS sb.append(", processId=").append(processId); // NON-NLS sb.append(", threadId=").append(threadId); // NON-NLS sb.append(", category=").append(Utils.sQuote(category)); // NON-NLS sb.append(", filteredOut=").append(filteredOut); // NON-NLS sb.append(", activityProperties=").append(activityProperties == null ? "NONE" : activityProperties.size());// NON-NLS sb.append(", children=").append(children == null ? "NONE" : children.size()); // NON-NLS sb.append('}'); // NON-NLS return sb.toString(); } /** * Returns activity field value. * * @param fieldName * field name value to get * @return field contained value */ public Object getFieldValue(String fieldName) { try { StreamFieldType sft = Utils.valueOfIgnoreCase(StreamFieldType.class, fieldName); switch (sft) { case ApplName: return applName; case Category: return category; case CompCode: return compCode; case Correlator: return correlator; case ElapsedTime: return elapsedTime; case EndTime: return endTime; case EventName: return eventName; case EventStatus: return eventStatus; case EventType: return eventType; case Exception: return exception; case Location: return location; case Message: return message; case MsgCharSet: return msgCharSet; case MsgEncoding: return msgEncoding; case MsgLength: return msgLength; case MsgMimeType: return msgMimeType; case ParentId: return parentId; case ProcessId: return processId; case ReasonCode: return reasonCode; case ResourceName: return resourceName; case ServerIp: return serverIp; case ServerName: return serverName; case Severity: return severity; case StartTime: return startTime; case Tag: return tag; case ThreadId: return threadId; case TrackingId: return trackingId; case UserName: return userName; default: throw new IllegalArgumentException(StreamsResources.getStringFormatted( StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityInfo.unrecognized.field", fieldName)); } } catch (IllegalArgumentException exc) { Property p = activityProperties == null ? null : activityProperties.get(fieldName); return p == null ? null : p.getValue(); } } }