Skip to content

Commit d53e491

Browse files
committed
add and remove dimensions
1 parent 04cb0c4 commit d53e491

File tree

2 files changed

+248
-56
lines changed

2 files changed

+248
-56
lines changed

src/main/java/com/arpnetworking/metrics/mad/sources/TransformingSource.java

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@
3636
import com.google.common.base.Splitter;
3737
import com.google.common.collect.ImmutableList;
3838
import com.google.common.collect.ImmutableMap;
39-
import com.google.common.collect.Lists;
4039
import com.google.common.collect.Maps;
4140
import net.sf.oval.constraint.NotEmpty;
4241
import net.sf.oval.constraint.NotNull;
4342

43+
import java.util.Collections;
4444
import java.util.List;
4545
import java.util.Map;
4646
import java.util.regex.Matcher;
@@ -90,26 +90,39 @@ private TransformingSource(final Builder builder) {
9090
super(builder);
9191
_source = builder._source;
9292

93-
_findAndReplace = Maps.newHashMapWithExpectedSize(builder._findAndReplace.size());
93+
final ImmutableMap.Builder<Pattern, List<String>> findReplaceBuilder =
94+
ImmutableMap.builderWithExpectedSize(builder._findAndReplace.size());
9495
for (final Map.Entry<String, ? extends List<String>> entry : builder._findAndReplace.entrySet()) {
95-
_findAndReplace.put(Pattern.compile(entry.getKey()), ImmutableList.copyOf(entry.getValue()));
96+
findReplaceBuilder.put(Pattern.compile(entry.getKey()), ImmutableList.copyOf(entry.getValue()));
9697
}
98+
_findAndReplace = findReplaceBuilder.build();
9799

98-
_source.attach(new TransformingObserver(this, _findAndReplace));
100+
_inject = builder._inject;
101+
_remove = builder._remove;
102+
103+
_source.attach(new TransformingObserver(this, _findAndReplace, _inject, _remove));
99104
}
100105

101106
private final Source _source;
102-
private final Map<Pattern, List<String>> _findAndReplace;
107+
private final ImmutableMap<Pattern, List<String>> _findAndReplace;
108+
private final ImmutableMap<String, DimensionInjection> _inject;
109+
private final ImmutableList<String> _remove;
103110

104111
private static final Logger LOGGER = LoggerFactory.getLogger(TransformingSource.class);
105112
private static final Splitter.MapSplitter TAG_SPLITTER = Splitter.on(';').omitEmptyStrings().trimResults().withKeyValueSeparator('=');
106113

107114
// NOTE: Package private for testing
108115
/* package private */ static final class TransformingObserver implements Observer {
109116

110-
/* package private */ TransformingObserver(final TransformingSource source, final Map<Pattern, List<String>> findAndReplace) {
117+
/* package private */ TransformingObserver(
118+
final TransformingSource source,
119+
final Map<Pattern, List<String>> findAndReplace,
120+
final ImmutableMap<String, DimensionInjection> inject,
121+
final ImmutableList<String> remove) {
111122
_source = source;
112123
_findAndReplace = findAndReplace;
124+
_inject = inject;
125+
_remove = remove;
113126
}
114127

115128
@Override
@@ -141,25 +154,31 @@ public void notify(final Observable observable, final Object event) {
141154
final int tagsStart = replacedString.indexOf(';');
142155
if (tagsStart == -1) {
143156
// We just have a metric name. Optimize for this common case
144-
merge(metric.getValue(), replacedString, mergedMetrics, record.getDimensions());
157+
merge(
158+
metric.getValue(),
159+
replacedString,
160+
mergedMetrics,
161+
getModifiedDimensions(record.getDimensions(), Collections.emptyMap(), ImmutableList.of()));
145162
} else {
146163
final String newMetricName = replacedString.substring(0, tagsStart);
147164
final Map<String, String> parsedTags = TAG_SPLITTER.split(replacedString.substring(tagsStart + 1));
148-
final Map<String, String> finalTags = Maps.newHashMap();
149-
finalTags.putAll(record.getDimensions());
150-
// Remove the dimensions that we consumed in the replacement
151-
consumedDimensions.forEach(finalTags::remove);
152-
finalTags.putAll(parsedTags);
153-
154-
merge(metric.getValue(), newMetricName, mergedMetrics, ImmutableMap.copyOf(finalTags));
165+
merge(
166+
metric.getValue(),
167+
newMetricName,
168+
mergedMetrics,
169+
getModifiedDimensions(record.getDimensions(), parsedTags, consumedDimensions));
155170
}
156171
}
157172
//Having "found" set here means that mapping a metric to an empty list suppresses that metric
158173
found = true;
159174
}
160175
}
161176
if (!found) {
162-
merge(metric.getValue(), metricName, mergedMetrics, record.getDimensions());
177+
merge(
178+
metric.getValue(),
179+
metricName,
180+
mergedMetrics,
181+
getModifiedDimensions(record.getDimensions(), Collections.emptyMap(), ImmutableList.of()));
163182
}
164183
}
165184

@@ -183,6 +202,24 @@ public void notify(final Observable observable, final Object event) {
183202
}
184203
}
185204

205+
private ImmutableMap<String, String> getModifiedDimensions(
206+
final ImmutableMap<String, String> inputDimensions,
207+
final Map<String, String> add,
208+
final ImmutableList<String> remove) {
209+
final Map<String, String> finalTags = Maps.newHashMap();
210+
finalTags.putAll(inputDimensions);
211+
// Remove the dimensions that we consumed in the replacement
212+
remove.forEach(finalTags::remove);
213+
_remove.forEach(finalTags::remove);
214+
_inject.forEach(
215+
(key, inject) ->
216+
finalTags.compute(key, (k, oldValue) ->
217+
inject.isReplaceExisting() || oldValue == null ? inject.getValue() : oldValue));
218+
finalTags.putAll(add);
219+
220+
return ImmutableMap.copyOf(finalTags);
221+
}
222+
186223
private void merge(final Metric metric, final String key,
187224
final Map<ImmutableMap<String, String>, Map<String, MergingMetric>> mergedMetrics,
188225
final ImmutableMap<String, String> dimensions) {
@@ -208,6 +245,8 @@ private void merge(final Metric metric, final String key,
208245

209246
private final TransformingSource _source;
210247
private final Map<Pattern, List<String>> _findAndReplace;
248+
private final ImmutableMap<String, DimensionInjection> _inject;
249+
private final ImmutableList<String> _remove;
211250
}
212251

213252
// NOTE: Package private for testing
@@ -256,10 +295,6 @@ public String toString() {
256295
* Represents a dimension to inject and whether or not it should overwrite the existing value (if any).
257296
*/
258297
public static final class DimensionInjection {
259-
public String getDimension() {
260-
return _dimension;
261-
}
262-
263298
public String getValue() {
264299
return _value;
265300
}
@@ -269,12 +304,10 @@ public boolean isReplaceExisting() {
269304
}
270305

271306
private DimensionInjection(final Builder builder) {
272-
_dimension = builder._dimension;
273307
_value = builder._value;
274308
_replaceExisting = builder._replaceExisting;
275309
}
276310

277-
private final String _dimension;
278311
private final String _value;
279312
private final boolean _replaceExisting;
280313

@@ -291,17 +324,6 @@ public Builder() {
291324
super(DimensionInjection::new);
292325
}
293326

294-
/**
295-
* Sets the dimension. Required. Cannot be null. Cannot be empty.
296-
*
297-
* @param value The dimension to inject.
298-
* @return This instance of {@link Builder}.
299-
*/
300-
public Builder setDimension(final String value) {
301-
_dimension = value;
302-
return this;
303-
}
304-
305327
/**
306328
* Sets the value. Required. Cannot be null. Cannot be empty.
307329
*
@@ -324,9 +346,6 @@ public Builder setReplaceExisting(final Boolean value) {
324346
return this;
325347
}
326348

327-
@NotNull
328-
@NotEmpty
329-
private String _dimension;
330349
@NotNull
331350
@NotEmpty
332351
private String _value;
@@ -366,7 +385,7 @@ public Builder setSource(final Source value) {
366385
* @param value The find and replace expression map.
367386
* @return This instance of <code>Builder</code>.
368387
*/
369-
public Builder setFindAndReplace(final Map<String, ? extends List<String>> value) {
388+
public Builder setFindAndReplace(final ImmutableMap<String, ? extends List<String>> value) {
370389
_findAndReplace = value;
371390
return this;
372391
}
@@ -377,11 +396,22 @@ public Builder setFindAndReplace(final Map<String, ? extends List<String>> value
377396
* @param value List of dimensions to inject.
378397
* @return This instance of <code>Builder</code>.
379398
*/
380-
public Builder setInject(final List<DimensionInjection> value) {
399+
public Builder setInject(final ImmutableMap<String, DimensionInjection> value) {
381400
_inject = value;
382401
return this;
383402
}
384403

404+
/**
405+
* Sets dimensions to remove. Optional. Cannot be null. Defaults to empty.
406+
*
407+
* @param value List of dimensions to inject.
408+
* @return This instance of <code>Builder</code>.
409+
*/
410+
public Builder setRemove(final ImmutableList<String> value) {
411+
_remove = value;
412+
return this;
413+
}
414+
385415
@Override
386416
protected Builder self() {
387417
return this;
@@ -390,8 +420,10 @@ protected Builder self() {
390420
@NotNull
391421
private Source _source;
392422
@NotNull
393-
private Map<String, ? extends List<String>> _findAndReplace = Maps.newHashMap();
423+
private ImmutableMap<String, ? extends List<String>> _findAndReplace = ImmutableMap.of();
424+
@NotNull
425+
private ImmutableMap<String, DimensionInjection> _inject = ImmutableMap.of();
394426
@NotNull
395-
private List<DimensionInjection> _inject = Lists.newArrayList();
427+
private ImmutableList<String> _remove = ImmutableList.of();
396428
}
397429
}

0 commit comments

Comments
 (0)