All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wavefront.common.MetricMangler Maven / Gradle / Ivy

There is a newer version: 2023-22.3
Show newest version
package com.wavefront.common;

import com.google.common.base.Splitter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Nullable;

/**
 * Handles updating the metric and source names by extracting components from the metric name.
 * There are several options considered:
 * 
    *
  • source name:
  • *
      *
    • extracted from one or more components of the metric name * (where each component is separated by a '.')
    • *
    • allow characters to be optionally replaced in the components extracted * as source name with '.'
    • *
    *
  • metric name:
  • *
      *
    • remove components (in addition to the source name) * (this allows things like 'hosts.sjc1234.cpu.loadavg.1m' to get * changed to cpu.loadavg.1m after extracting sjc1234)
    • *
    *
* This code was originally mostly contained in GraphiteFormatter class and moved into a single * re-usable class. * @author Mike McLaughlin ([email protected]) */ public class MetricMangler { // Fields to extract and assemble, in order, as the host name private final List hostIndices = new ArrayList<>(); private int maxField = 0; // Lookup set for which indices are hostname related private final Set hostIndexSet = new HashSet<>(); // Characters which should be interpreted as dots @Nullable private final String delimiters; // Fields to remove private final Set removeIndexSet = new HashSet<>(); /** * Constructor. * * @param sourceFields comma separated field index(es) (1-based) where the source name will be * extracted * @param delimiters characters to be interpreted as dots * @param removeFields comma separated field index(es) (1-based) of fields to remove from the * metric name * @throws IllegalArgumentException when one of the field index is <= 0 */ public MetricMangler(@Nullable String sourceFields, @Nullable String delimiters, @Nullable String removeFields) { if (sourceFields != null) { // Store ordered field indices and lookup set Iterable fields = Splitter.on(",").omitEmptyStrings().trimResults().split(sourceFields); for (String field : fields) { if (field.trim().length() > 0) { int fieldIndex = Integer.parseInt(field); if (fieldIndex <= 0) { throw new IllegalArgumentException("Can't define a field of index 0 or less; indices must be 1-based"); } hostIndices.add(fieldIndex - 1); hostIndexSet.add(fieldIndex - 1); if (fieldIndex > maxField) { maxField = fieldIndex; } } } } if (removeFields != null) { Iterable fields = Splitter.on(",").omitEmptyStrings().trimResults().split(removeFields); for (String field : fields) { if (field.trim().length() > 0) { int fieldIndex = Integer.parseInt(field); if (fieldIndex <= 0) { throw new IllegalArgumentException("Can't define a field to remove of index 0 or less; indices must be 1-based"); } removeIndexSet.add(fieldIndex - 1); } } } // Store as-is; going to loop through chars anyway this.delimiters = delimiters; } /** * Simple struct to store and return the source, annotations and the updated metric. * * @see {@link #extractComponents(String)} */ public static class MetricComponents { @Nullable public String source; @Nullable public String metric; @Nullable public String[] annotations; } /** * Extracts the source from the metric name and returns the new metric name and the source name. * * @param metric the metric name * @return the updated metric name and the extracted source * @throws IllegalArgumentException when the number of segments (split on '.') is less than the * maximum source component index */ public MetricComponents extractComponents(final String metric) { final String[] segments = metric.split("\\."); final MetricComponents rtn = new MetricComponents(); // Is the metric name long enough? if (segments.length < maxField) { throw new IllegalArgumentException( String.format("Metric data |%s| provided was incompatible with format.", metric)); } // Assemble the newly shorn metric name, in original order StringBuilder buf = new StringBuilder(); for (int i = 0; i < segments.length; i++) { final String segment = segments[i]; if (!hostIndexSet.contains(i) && !removeIndexSet.contains(i)) { if (buf.length() > 0) { buf.append('.'); } buf.append(segment); } } rtn.metric = buf.toString(); // Extract Graphite 1.1+ tags, if present if (rtn.metric.indexOf(";") > 0) { final String[] annotationSegments = rtn.metric.split(";"); rtn.annotations = Arrays.copyOfRange(annotationSegments, 1, annotationSegments.length); rtn.metric = annotationSegments[0]; } // Loop over host components in configured order, and replace all delimiters with dots if (hostIndices != null && !hostIndices.isEmpty()) { buf = new StringBuilder(); for (int f = 0; f < hostIndices.size(); f++) { char[] segmentChars = segments[hostIndices.get(f)].toCharArray(); if (delimiters != null && !delimiters.isEmpty()) { for (int i = 0; i < segmentChars.length; i++) { for (int c = 0; c < delimiters.length(); c++) { if (segmentChars[i] == delimiters.charAt(c)) { segmentChars[i] = '.'; // overwrite it } } } } if (f > 0) { // join host segments with dot, if you're after the first one buf.append('.'); } buf.append(segmentChars); } rtn.source = buf.toString(); } else { rtn.source = null; } return rtn; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy