package rocks.inspectit.ui.rcp.formatter; import java.text.DateFormat; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.preference.JFacePreferences; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.StyledString.Styler; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.TextStyle; import io.opentracing.References; import io.opentracing.tag.Tags; import rocks.inspectit.shared.all.cmr.model.JmxDefinitionDataIdent; import rocks.inspectit.shared.all.cmr.model.MethodIdent; import rocks.inspectit.shared.all.cmr.model.PlatformIdent; import rocks.inspectit.shared.all.cmr.service.ICachedDataService; import rocks.inspectit.shared.all.communication.data.ExceptionSensorData; import rocks.inspectit.shared.all.communication.data.HttpInfo; import rocks.inspectit.shared.all.communication.data.HttpTimerData; import rocks.inspectit.shared.all.communication.data.InvocationAwareData; import rocks.inspectit.shared.all.communication.data.SqlStatementData; import rocks.inspectit.shared.all.communication.data.TimerData; import rocks.inspectit.shared.all.communication.data.cmr.AgentStatusData; import rocks.inspectit.shared.all.tracing.constants.ExtraTags; import rocks.inspectit.shared.all.tracing.data.PropagationType; import rocks.inspectit.shared.all.tracing.data.Span; import rocks.inspectit.shared.cs.ci.assignment.ISensorAssignment; import rocks.inspectit.shared.cs.ci.assignment.impl.MethodSensorAssignment; import rocks.inspectit.shared.cs.ci.sensor.ISensorConfig; import rocks.inspectit.shared.cs.communication.data.cmr.Alert; import rocks.inspectit.shared.cs.communication.data.cmr.WritingStatus; import rocks.inspectit.shared.cs.storage.LocalStorageData; import rocks.inspectit.shared.cs.storage.StorageData; import rocks.inspectit.shared.cs.storage.StorageData.StorageState; import rocks.inspectit.shared.cs.storage.label.AbstractStorageLabel; import rocks.inspectit.shared.cs.storage.label.BooleanStorageLabel; import rocks.inspectit.shared.cs.storage.label.type.AbstractCustomStorageLabelType; import rocks.inspectit.shared.cs.storage.label.type.AbstractStorageLabelType; import rocks.inspectit.shared.cs.storage.label.type.impl.AssigneeLabelType; import rocks.inspectit.shared.cs.storage.label.type.impl.CreationDateLabelType; import rocks.inspectit.shared.cs.storage.label.type.impl.DataTimeFrameLabelType; import rocks.inspectit.shared.cs.storage.label.type.impl.ExploredByLabelType; import rocks.inspectit.shared.cs.storage.label.type.impl.RatingLabelType; import rocks.inspectit.shared.cs.storage.label.type.impl.StatusLabelType; import rocks.inspectit.shared.cs.storage.label.type.impl.UseCaseLabelType; import rocks.inspectit.ui.rcp.InspectIT; import rocks.inspectit.ui.rcp.model.AgentFolderFactory; import rocks.inspectit.ui.rcp.model.AgentLeaf; import rocks.inspectit.ui.rcp.repository.CmrRepositoryDefinition; import rocks.inspectit.ui.rcp.repository.RepositoryDefinition; import rocks.inspectit.ui.rcp.util.data.RegExAggregatedHttpTimerData; import rocks.inspectit.ui.rcp.validation.ValidationControlDecoration; import rocks.inspectit.ui.rcp.validation.ValidationState; /** * This class provides some static methods to create some common {@link String} and * {@link StyledString} objects. * * @author Patrice Bouillet * @author Stefan Siegl * @author Ivan Senic * @author Marius Oehler */ public final class TextFormatter { /** * Error {@link Styler}. */ private static final Styler ERROR_STYLED = StyledString.createColorRegistryStyler(JFacePreferences.ERROR_COLOR, null); /** * Hyperlink color {@link Styler}. */ private static final Styler HYPERLINK_STYLED = StyledString.createColorRegistryStyler(JFacePreferences.HYPERLINK_COLOR, null); /** * Styled string for client span. */ private static final StyledString CLIENT_SPAN = new StyledString("Client :: ", StyledString.DECORATIONS_STYLER); /** Logical Name for the font used for the error marker. */ public static final String FONT_ERROR_MARKER = "de.inspectit.font.errormarker"; /** * Default size of the font used in the error marker. This will be used if the size of default * system font can not be read. */ public static final int DEFAULT_FONT_ERROR_SIZE = 10; static { FontData[] fontData = JFaceResources.getDefaultFontDescriptor().getFontData(); if (fontData.length > 0) { FontData defaultFontData = fontData[0]; int height = (int) defaultFontData.height; JFaceResources.getFontRegistry().put(FONT_ERROR_MARKER, new FontData[] { new FontData("Arial", height, SWT.BOLD | SWT.ITALIC) }); } else { JFaceResources.getFontRegistry().put(FONT_ERROR_MARKER, new FontData[] { new FontData("Arial", DEFAULT_FONT_ERROR_SIZE, SWT.BOLD | SWT.ITALIC) }); } } /** * Private constructor. Prevent instantiation. */ private TextFormatter() { } /** * Returns a Styled String out of the {@link MethodIdent} objects which looks like: * 'name'('parameter') - 'package'.'class'. Additionally, as this returns a {@link StyledString} * , the last part is colored. * * @param methodIdent * The object which contains the information to create the styled method string. * @return The created styled method string. */ public static StyledString getStyledMethodString(MethodIdent methodIdent) { StyledString styledString = new StyledString(); styledString.append(getMethodWithParameters(methodIdent)); String decoration; if ((methodIdent.getPackageName() != null) && !methodIdent.getPackageName().equals("")) { decoration = MessageFormat.format("- {0}.{1}", new Object[] { methodIdent.getPackageName(), methodIdent.getClassName() }); } else { decoration = MessageFormat.format("- {0}", new Object[] { methodIdent.getClassName() }); } styledString.append(decoration, StyledString.QUALIFIER_STYLER); return styledString; } /** * Returns a method string which is appended by the parameters. * * @param methodIdent * The object which contains the information to create the styled method string. * @return The created method + parameters string. */ public static String getMethodWithParameters(MethodIdent methodIdent) { StringBuilder builder = new StringBuilder(); String parameterText = ""; if (null != methodIdent.getParameters()) { List<String> parameterList = new ArrayList<>(); for (String parameter : methodIdent.getParameters()) { String[] split = parameter.split("\\."); parameterList.add(split[split.length - 1]); } parameterText = parameterList.toString(); parameterText = parameterText.substring(1, parameterText.length() - 1); } builder.append(methodIdent.getMethodName()); builder.append('('); builder.append(parameterText); builder.append(") "); return builder.toString(); } /** * Returns a method string which is appended by the parameters. * * @param methodSensorAssignment * {@link MethodSensorAssignment} * @return The created method + parameters string. */ public static String getMethodWithParameters(MethodSensorAssignment methodSensorAssignment) { // can not create if we don't have name if ((null == methodSensorAssignment.getMethodName()) && !methodSensorAssignment.isConstructor()) { return ""; } StringBuilder builder = new StringBuilder(); if (methodSensorAssignment.isConstructor()) { builder.append("<init>"); } else { builder.append(methodSensorAssignment.getMethodName()); } // if not defined then just a start if (null != methodSensorAssignment.getParameters()) { if (methodSensorAssignment.getParameters().isEmpty()) { builder.append("()"); } else { String parameterText = methodSensorAssignment.getParameters().toString(); parameterText = parameterText.substring(1, parameterText.length() - 1); builder.append('('); builder.append(parameterText); builder.append(')'); } } return builder.toString(); } /** * Returns a String out of the {@link MethodIdent} objects which looks like: 'name'('parameter') * - 'package'.'class'. * * @param methodIdent * The object which contains the information to create the method string. * @return The created method string. */ public static String getMethodString(MethodIdent methodIdent) { return getStyledMethodString(methodIdent).getString(); } /** * Returns a {@link String} out of the {@link JmxDefinitionDataIdent} object which looks like: * 'attributeName' - 'packagename':'typeName'. * * @param jmxIdent * the object which contains the information to create the jmx string. * @return the created method string */ public static String getJmxDefinitionString(JmxDefinitionDataIdent jmxIdent) { return String.format("%1$s - %2$s:%3$s", jmxIdent.getmBeanAttributeName(), jmxIdent.getDerivedDomainName(), jmxIdent.getDerivedTypeName()); } /** * Returns styled string for invocation affilliation percentage. * * @param percentage * Percentage. * @param invocationsNumber * the number of invocation in total * @return Styled string. */ public static StyledString getInvocationAffilliationPercentageString(int percentage, int invocationsNumber) { StyledString styledString = new StyledString(); styledString.append(String.valueOf(percentage), StyledString.QUALIFIER_STYLER); styledString.append("% (in ", StyledString.QUALIFIER_STYLER); styledString.append(String.valueOf(invocationsNumber), StyledString.QUALIFIER_STYLER); styledString.append(" inv)", StyledString.QUALIFIER_STYLER); return styledString; } /** * Creates a <code>StyledString</code> containing a warning. * * @return a <code>StyledString</code> containing a warning. */ public static StyledString getWarningSign() { return new StyledString(" !", new Styler() { @Override public void applyStyles(TextStyle textStyle) { textStyle.foreground = JFaceResources.getColorRegistry().get(JFacePreferences.ERROR_COLOR); textStyle.font = JFaceResources.getFont(TextFormatter.FONT_ERROR_MARKER); } }); } /** * Get the textual representation of objects that will be displayed in the new view. * * @param invAwareData * Invocation aware object to get representation for. * @param repositoryDefinition * Repository definition. Needed for the method name retrival. * @return String. */ public static String getInvocationAwareDataTextualRepresentation(InvocationAwareData invAwareData, RepositoryDefinition repositoryDefinition) { if (invAwareData instanceof SqlStatementData) { SqlStatementData sqlData = (SqlStatementData) invAwareData; return "SQL: " + sqlData.getSql(); } else if (invAwareData instanceof RegExAggregatedHttpTimerData) { return "transformed URI: " + ((RegExAggregatedHttpTimerData) invAwareData).getTransformedUri(); } else if (invAwareData instanceof HttpTimerData) { HttpTimerData timerData = (HttpTimerData) invAwareData; // Print either URI or Usecase (tagged value) depending on the situation (which is // filled, that is) if (!HttpInfo.UNDEFINED.equals(timerData.getHttpInfo().getUri())) { return "URI: " + timerData.getHttpInfo().getUri(); } else { return "Usecase: " + timerData.getHttpInfo().getInspectItTaggingHeaderValue(); } } else if (invAwareData instanceof ExceptionSensorData) { ExceptionSensorData exData = (ExceptionSensorData) invAwareData; return "Exception: " + exData.getThrowableType(); } else if (invAwareData instanceof TimerData) { TimerData timerData = (TimerData) invAwareData; MethodIdent methodIdent = repositoryDefinition.getCachedDataService().getMethodIdentForId(timerData.getMethodIdent()); return TextFormatter.getMethodString(methodIdent); } return ""; } /** * Returns the styled string for the storage data and its CMR repository definition. * * @param storageData * {@link StorageData}. * @param cmrRepositoryDefinition * {@link CmrRepositoryDefinition}. * @return Styled string for nicer representation. */ public static StyledString getStyledStorageDataString(StorageData storageData, CmrRepositoryDefinition cmrRepositoryDefinition) { StyledString styledString = new StyledString(); styledString.append(storageData.getName()); styledString.append(" "); styledString.append("[" + cmrRepositoryDefinition.getName() + "]", StyledString.QUALIFIER_STYLER); styledString.append(" - "); styledString.append(getStorageStateTextualRepresentation(storageData.getState()), StyledString.DECORATIONS_STYLER); if (InspectIT.getDefault().getInspectITStorageManager().isFullyDownloaded(storageData)) { styledString.append(", Downloaded", StyledString.DECORATIONS_STYLER); } styledString.append(", " + NumberFormatter.humanReadableByteCount(storageData.getDiskSize()), StyledString.DECORATIONS_STYLER); return styledString; } /** * Returns the styled string for the {@link LocalStorageData}. * * @param localStorageData * Local storage data. * @return Styled string for nicer representation. */ public static StyledString getStyledStorageDataString(LocalStorageData localStorageData) { StyledString styledString = new StyledString(); styledString.append(localStorageData.getName()); styledString.append(" "); styledString.append("[Local Disk]", StyledString.QUALIFIER_STYLER); styledString.append(" - "); styledString.append(NumberFormatter.humanReadableByteCount(localStorageData.getDiskSize()), StyledString.DECORATIONS_STYLER); return styledString; } /** * Returns {@link StyledString} for the {@link AgentLeaf}. * * @param agentLeaf * {@link AgentLeaf}. * @return Returns {@link StyledString} for the {@link AgentLeaf}. */ public static StyledString getStyledAgentLeafString(AgentLeaf agentLeaf) { StyledString styledString = new StyledString(); if (agentLeaf.isInFolder()) { styledString.append(AgentFolderFactory.getAgentDisplayNameInFolder(agentLeaf.getPlatformIdent().getAgentName())); } else { styledString.append(agentLeaf.getPlatformIdent().getAgentName()); } styledString.append(getStyledAgentDescription(agentLeaf.getPlatformIdent(), agentLeaf.getAgentStatusData())); return styledString; } /** * Returns the styled information about the agent version and connection status. * * @param platformIdent * {@link PlatformIdent} * @param agentStatusData * {@link AgentStatusData} * @return {@link StyledString} */ private static StyledString getStyledAgentDescription(PlatformIdent platformIdent, AgentStatusData agentStatusData) { StyledString styledString = new StyledString(); styledString.append(" "); styledString.append("[" + platformIdent.getVersion() + "]", StyledString.QUALIFIER_STYLER); styledString.append(" - "); if (null != agentStatusData) { switch (agentStatusData.getAgentConnection()) { case CONNECTED: if (null != agentStatusData.getMillisSinceLastData()) { long millis = agentStatusData.getMillisSinceLastData().longValue(); // at last one minute of not sending data to display as the non active if (millis > 60000) { styledString.append("Connected :: Last data sent " + NumberFormatter.humanReadableMillisCount(millis, true) + " ago", StyledString.DECORATIONS_STYLER); } else { styledString.append("Connected :: Sending data", StyledString.DECORATIONS_STYLER); } } else { styledString.append("Connected :: No data sent", StyledString.DECORATIONS_STYLER); } break; case NO_KEEP_ALIVE: long timeSinceLastKeepAlive = System.currentTimeMillis() - agentStatusData.getLastKeepAliveTimestamp(); styledString.append("No keep-alive signal for " + NumberFormatter.humanReadableMillisCount(timeSinceLastKeepAlive, true), StyledString.DECORATIONS_STYLER); break; case DISCONNECTED: styledString.append("Disconnected", StyledString.DECORATIONS_STYLER); break; default: styledString.append("Not connected", StyledString.DECORATIONS_STYLER); break; } } else { styledString.append("Not connected", StyledString.DECORATIONS_STYLER); } return styledString; } /** * @param storageState * Storage state. * @return Returns the textual representation of the storage state. */ public static String getStorageStateTextualRepresentation(StorageState storageState) { if (storageState == StorageState.CREATED_NOT_OPENED) { return "Created"; } else if (storageState == StorageState.OPENED) { return "Writable"; } else if (storageState == StorageState.CLOSED) { return "Readable"; } else if (storageState == StorageState.RECORDING) { return "Recording"; } return "UNKNOWN STATE"; } /** * Returns the name of the label, based on it's type. If label is <code>null</code>, string * "null" will be returned. * * @param label * Label to get name for. * @return Returns the name of the label, based on it's class. */ public static String getLabelName(AbstractStorageLabel<?> label) { if (null == label) { return "null"; } else { return getLabelName(label.getStorageLabelType()); } } /** * Returns the name of the label type. If label type is <code>null</code>, string "null" will be * returned. * * @param labelType * Label type to get name for. * @return Returns the name of the label, based on it's class. */ public static String getLabelName(AbstractStorageLabelType<?> labelType) { if (null == labelType) { return "null"; } else if (AssigneeLabelType.class.equals(labelType.getClass())) { return "Assignee"; } else if (CreationDateLabelType.class.equals(labelType.getClass())) { return "Creation Date"; } else if (ExploredByLabelType.class.equals(labelType.getClass())) { return "Explored By"; } else if (RatingLabelType.class.equals(labelType.getClass())) { return "Rating"; } else if (StatusLabelType.class.equals(labelType.getClass())) { return "Status"; } else if (UseCaseLabelType.class.equals(labelType.getClass())) { return "Use Case"; } else if (DataTimeFrameLabelType.class.equals(labelType.getClass())) { return "Data Timeframe"; } else if (AbstractCustomStorageLabelType.class.isAssignableFrom(labelType.getClass())) { return ((AbstractCustomStorageLabelType<?>) labelType).getName(); } else { return "Unknown Label"; } } /** * Returns the class type of the label type. * * @param labelType * Label type to get name for. * @return Returns the class type of the label type. */ public static String getLabelValueType(AbstractStorageLabelType<?> labelType) { if (null == labelType) { return "null"; } else if (Boolean.class.equals(labelType.getValueClass())) { return "Yes/No"; } else if (Date.class.equals(labelType.getValueClass())) { return "Date"; } else if (Number.class.equals(labelType.getValueClass())) { return "Number"; } else if (String.class.equals(labelType.getValueClass())) { return "Text"; } else { return "Unknown Label Type"; } } /** * Returns the name of the label, based on it's class. If label is <code>null</code>, string * "null" will be returned. * * @param label * Label to get name for. * @param grouped * Is is a representation for the grouped labels. * @return Returns the name of the label, based on it's class. */ public static String getLabelValue(AbstractStorageLabel<?> label, boolean grouped) { if (null == label) { return "null"; } else if (CreationDateLabelType.class.equals(label.getStorageLabelType().getClass())) { Date date = (Date) ((AbstractStorageLabel<?>) label).getValue(); if (grouped) { return DateFormat.getDateInstance().format(date); } else { return DateFormat.getDateTimeInstance().format(date); } } else if (BooleanStorageLabel.class.equals(label.getClass())) { BooleanStorageLabel booleanStorageLabel = (BooleanStorageLabel) label; if (booleanStorageLabel.getValue().booleanValue()) { return "Yes"; } else { return "No"; } } else { return label.getFormatedValue(); } } /** * Returns text representation for the {@link WritingStatus} or empty string if status is * <code>null</code>. * * @param recordingWritingStatus * Status of writing. * @return String that represent the status. */ public static String getWritingStatusText(WritingStatus recordingWritingStatus) { if (null == recordingWritingStatus) { return ""; } switch (recordingWritingStatus) { case GOOD: return "[OK] There are no problems."; case MEDIUM: return "[WARN] Amount of data tried to be recorded is slightly higer than what CMR can support. However, no data loss should be expected."; case BAD: return "[ALERT] Amount of data tried to be recorded is too high for CMR to manage. Data loss should be expected."; default: return ""; } } /** * Description of the agent. * * @param agent * {@link PlatformIdent} * @return Description of the agent. */ public static String getAgentDescription(PlatformIdent agent) { return agent.getAgentName() + " [" + agent.getVersion() + "]"; } /** * Description of the agent with the connection information. * * @param agent * {@link PlatformIdent} * @param agentStatusData * {@link AgentStatusData}. * @return Description of the agent. */ public static String getAgentDescription(PlatformIdent agent, AgentStatusData agentStatusData) { return agent.getAgentName() + getStyledAgentDescription(agent, agentStatusData).getString(); } /** * Description of the {@link CmrRepositoryDefinition}. * * @param cmrRepositoryDefinition * {@link CmrRepositoryDefinition}. * @return Description in form http://ip:port */ public static String getCmrRepositoryDescription(CmrRepositoryDefinition cmrRepositoryDefinition) { return "Central Management Repository @ http://" + cmrRepositoryDefinition.getIp() + ":" + cmrRepositoryDefinition.getPort(); } /** * Returns formated {@link String} for the {@link SqlStatementData} parameter values list. * <p> * Elements that are <code>null</code> in the list will be printed as '?'. * * @param parameterValues * List of parameter values. * @return Formated string in form [param1, param2,.., paramN]. */ public static String getSqlParametersText(List<String> parameterValues) { if ((null == parameterValues) || parameterValues.isEmpty()) { return "[]"; } else { Iterator<String> it = parameterValues.iterator(); StringBuilder sb = new StringBuilder(); sb.append('['); while (it.hasNext()) { String param = it.next(); if (null != param) { sb.append(param); } else { sb.append('?'); } if (it.hasNext()) { sb.append(", "); } } return sb.append(']').toString(); } } /** * The original text will be cleaned from the line breaks. * <p> * If string passed is <code>null</code>, null will be returned. * * @param originalText * Original text to modify. * @return Returns text without any line breaks. */ public static String clearLineBreaks(String originalText) { if (null == originalText) { return originalText; } boolean lastCharWhitespace = false; StringBuilder stringBuilder = new StringBuilder(originalText.length()); for (int i = 0; i < originalText.length(); i++) { char c = originalText.charAt(i); if ((c == '\r') || (c == '\n')) { if (!lastCharWhitespace) { stringBuilder.append(' '); lastCharWhitespace = true; } } else if (Character.isWhitespace(c)) { if (!lastCharWhitespace) { stringBuilder.append(' '); lastCharWhitespace = true; } } else { stringBuilder.append(c); lastCharWhitespace = false; } } return stringBuilder.toString(); } /** * Crops the string to the maxLength. The string will have '...' appended at the end. This * method delegates to the {@link StringUtils#abbreviate(String, int)} method. * * @param string * String to crop. * @param maxLength * Wanted maximum length. * @see StringUtils#abbreviate(String, int) * @return Cropped {@link String}. */ public static String crop(String string, int maxLength) { return StringUtils.abbreviate(string, maxLength); } /** * Returns a new StyledString that contains the given text or "" if the given text was in fact * <code>null</code>. * * @param text * the text to display, may be null. * @return a new StyledString that contains the given text or "" if the given text was in fact * <code>null</code>. */ public static StyledString emptyStyledStringIfNull(String text) { return new StyledString(StringUtils.defaultString(text)); } /** * Returns name of the {@link ISensorConfig}. * * @param sensorConfig * {@link ISensorConfig}. * @return Name or empty string if sensor name can be resolved. */ public static String getSensorConfigName(ISensorConfig sensorConfig) { return getSensorConfigName(sensorConfig.getClass()); } /** * Returns name of the {@link ISensorConfig class}. * * @param sensorClass * {@link ISensorConfig} class. * @return Name or empty string if sensor name can be resolved. */ public static String getSensorConfigName(Class<? extends ISensorConfig> sensorClass) { // we expect that all configurations have SENSOR_NAME field try { ISensorConfig sensorInstance = sensorClass.newInstance(); return sensorInstance.getName(); } catch (Exception e) { InspectIT.getDefault().log(IStatus.WARNING, "Can not load sensor name for class " + sensorClass.getSimpleName()); return null; } } /** * Returns short (1 line) error message for the assignment based on the validation states. * * @param sensorAssignment * assignment * @param states * {@link ValidationControlDecoration} * @return short error message */ public static String getErroMessageShort(ISensorAssignment<?> sensorAssignment, Collection<ValidationState> states) { StringBuilder builder = new StringBuilder(); builder.append(TextFormatter.getSensorConfigName(sensorAssignment.getSensorConfigClass())); builder.append(" Assignment ("); String singleStateMessage = StringUtils.EMPTY; int count = 0; for (ValidationState state : states) { if (!state.isValid()) { count++; singleStateMessage = state.getMessage(); } } if (count > 1) { builder.append(count); builder.append(" fields contain validation errors)"); } else { builder.append(singleStateMessage); builder.append(')'); } return builder.toString(); } /** * Returns full error message for the assignment based on the validation states. In this message * each line will contain error reported by any invalid {@link ValidationState} * * @param sensorAssignment * assignment * @param states * {@link ValidationState}s * @return fill error message */ public static String getErroMessageFull(ISensorAssignment<?> sensorAssignment, Collection<ValidationState> states) { StringBuilder builder = new StringBuilder(); builder.append(TextFormatter.getSensorConfigName(sensorAssignment.getSensorConfigClass())); builder.append(" Assignment:"); builder.append(getValidationConcatenatedMessage(states)); return builder.toString(); } /** * Returns a validation errors count text for the given set of {@link ValidationState}s. * * @param states * set of {@link ValidationState}s * @param element * name of the element (e.g. filed, part, etc.) * @return the validation message, or null if the given set is empty. */ public static String getValidationErrorsCountText(Set<ValidationState> states, String element) { if (CollectionUtils.isNotEmpty(states)) { if (states.size() == 1) { return states.iterator().next().getMessage(); } else if (states.size() > 1) { return states.size() + " " + element + "s contain validation errors"; } } return null; } /** * Returns a concatenated validation message for the given set of {@link ValidationState}s. * * @param states * set of {@link ValidationState}s * @return a concatenated validation message, or an empty string if the given set is empty. */ public static String getValidationConcatenatedMessage(Collection<ValidationState> states) { StringBuilder builder = new StringBuilder(); boolean first = true; for (ValidationState state : states) { if (!state.isValid()) { if (!first) { builder.append('\n'); } builder.append(state.getMessage()); first = false; } } return builder.toString(); } /** * Returns string representation of an alert. * * @param alert * {@link Alert} instance to create string for. * @return Returns string representation of an alert. */ public static String getAlertDescription(Alert alert) { String closing = alert.isOpen() ? "Open" : DateFormat.getDateTimeInstance().format(new Date(alert.getStopTimestamp())); return alert.getAlertingDefinition().getName() + " (" + DateFormat.getDateTimeInstance().format(new Date(alert.getStartTimestamp())) + " - " + closing + ")"; } /** * Returns styled string including the span propagation. * * @param propagationType * Propagation type. Can be <code>null</code>. Returns "Unknown" as the result. * @return Styled string. */ public static StyledString getPropagationStyled(PropagationType propagationType) { StyledString styledString = new StyledString(); if (null != propagationType) { styledString.append(propagationType.toString()); } else { // if it's null we assume it's our SDK as we always set the propagation styledString.append("SDK"); } return styledString; } /** * Returns the full span details in form of StyledString. This method returns details if on of * these situations is matched: * * <ul> * <li>Propagation type is known for a span. * <li>Operation name exists as a tag. * <li>Method ident exists in the span. * </ul> * * @param span * {@link Span} * @param cachedDataService * Cached data service to load the method ident information from. * @return Returns {@link StyledString} if information in the span is enough or * <code>null</code> if details can not be constructed. */ public static StyledString getSpanDetailsFull(Span span, ICachedDataService cachedDataService) { return getSpanDetailsFull(span.isCaller(), span.getPropagationType(), span.getMethodIdent(), span.getTags(), cachedDataService); } /** * Helper method to generate span details out of full available information. * * @param isCaller * if it's client span * @param propagationType * Propagation type. Can be <code>null</code>. * @param methodId * method ident id if available. * @param tags * Tags * @param cachedDataService * Cached data service to load the method ident information from. * @return Returns {@link StyledString} if information given is enough or <code>null</code> if * details can not be constructed. */ private static StyledString getSpanDetailsFull(boolean isCaller, PropagationType propagationType, long methodId, Map<String, String> tags, ICachedDataService cachedDataService) { StyledString styledString = new StyledString(); if (isCaller) { styledString.append(CLIENT_SPAN); } boolean isError = MapUtils.getBoolean(tags, Tags.ERROR.getKey(), false); if (isError) { styledString.append('[', StyledString.DECORATIONS_STYLER); styledString.append("Err", ERROR_STYLED); styledString.append("] ", StyledString.DECORATIONS_STYLER); } // first by propagation if (null != propagationType) { switch (propagationType) { case HTTP: if (!isError) { styledString.append('[', StyledString.DECORATIONS_STYLER); String status = tags.get(Tags.HTTP_STATUS.getKey()); styledString.append(getHttpStatusStyledString(status)); styledString.append(" | ", StyledString.DECORATIONS_STYLER); styledString.append(StringUtils.defaultString(tags.get(Tags.HTTP_METHOD.getKey()), "?"), StyledString.DECORATIONS_STYLER); styledString.append("] ", StyledString.DECORATIONS_STYLER); } styledString.append(StringUtils.defaultString(tags.get(Tags.HTTP_URL.getKey()))); return styledString; case JMS: styledString.append(StringUtils.defaultString(tags.get(ExtraTags.JMS_MESSAGE_DESTINATION), "?")); return styledString; default: } } // then by operation name String op = tags.get(ExtraTags.OPERATION_NAME); if (StringUtils.isNotEmpty(op)) { styledString.append(op); return styledString; } // then by method id MethodIdent methodIdent = cachedDataService.getMethodIdentForId(methodId); if (null != methodIdent) { styledString.append(getMethodWithParameters(methodIdent)); } return styledString; } /** * Returns the short span details in form of StyledString. This method returns details if on of * these situations is matched: * * <ul> * <li>Propagation type is known for a span. * <li>Operation name exists as a tag. * <li>Method ident exists in the span. * </ul> * * @param span * {@link Span} * @param cachedDataService * Cached data service to load the method ident information from. * @return Returns {@link StyledString} if information in the span is enough or * <code>null</code> if details can not be constructed. */ public static StyledString getSpanDetailsShort(Span span, ICachedDataService cachedDataService) { return getSpanDetailsShort(span.getPropagationType(), span.getMethodIdent(), span.getTags(), cachedDataService); } /** * Helper method to generate span details out of some available information. * * @param propagationType * Propagation type. Can be <code>null</code>. * @param methodId * method ident id if available. * @param tags * Tags * @param cachedDataService * Cached data service to load the method ident information from. * * @return Returns {@link StyledString} if information given is enough or <code>null</code> if * details can not be constructed. */ private static StyledString getSpanDetailsShort(PropagationType propagationType, long methodId, Map<String, String> tags, ICachedDataService cachedDataService) { StyledString styledString = new StyledString(); boolean isError = MapUtils.getBoolean(tags, Tags.ERROR.getKey(), false); if (isError) { styledString.append('[', StyledString.DECORATIONS_STYLER); styledString.append("Err", ERROR_STYLED); styledString.append("] ", StyledString.DECORATIONS_STYLER); } // first by propagation if (null != propagationType) { switch (propagationType) { case HTTP: if (!isError) { styledString.append('[', StyledString.DECORATIONS_STYLER); String status = tags.get(Tags.HTTP_STATUS.getKey()); styledString.append(getHttpStatusStyledString(status)); styledString.append("] ", StyledString.DECORATIONS_STYLER); } styledString.append(StringUtils.defaultString(tags.get(Tags.HTTP_URL.getKey()))); return styledString; case JMS: String destination = tags.get(ExtraTags.JMS_MESSAGE_DESTINATION); if (StringUtils.isNotEmpty(destination)) { styledString.append(destination); return styledString; } default: } } // then by operation name String op = tags.get(ExtraTags.OPERATION_NAME); if (StringUtils.isNotEmpty(op)) { styledString.append(op); return styledString; } // then by method id MethodIdent methodIdent = cachedDataService.getMethodIdentForId(methodId); if (null != methodIdent) { styledString.append(getMethodWithParameters(methodIdent)); } return styledString; } /** * Returns styled string for status tag. * * @param status * http status as string * @return StyledString */ private static StyledString getHttpStatusStyledString(String status) { if (null == status) { return new StyledString("?", StyledString.DECORATIONS_STYLER); } try { int statusInt = Integer.parseInt(status); if ((statusInt >= 400) && (statusInt < 500)) { return new StyledString(status, HYPERLINK_STYLED); } else if ((statusInt >= 500) & (statusInt < 600)) { return new StyledString(status, ERROR_STYLED); } else { return new StyledString(status, StyledString.COUNTER_STYLER); } } catch (NumberFormatException e) { return new StyledString(status, StyledString.DECORATIONS_STYLER); } } /** * Returns descriptive reference for a span if one is provided. * * @param span * span * @return Reference more descriptive * @see Reference more descriptive */ public static String getDescriptiveReference(Span span) { String referenceType = span.getReferenceType(); if (References.CHILD_OF.equals(referenceType)) { return "Synchronous"; } else if (References.FOLLOWS_FROM.equals(referenceType)) { return "Non-Synchronous"; } return "N/A"; } }