36
36
import com .google .common .base .Splitter ;
37
37
import com .google .common .collect .ImmutableList ;
38
38
import com .google .common .collect .ImmutableMap ;
39
- import com .google .common .collect .Lists ;
40
39
import com .google .common .collect .Maps ;
41
40
import net .sf .oval .constraint .NotEmpty ;
42
41
import net .sf .oval .constraint .NotNull ;
43
42
43
+ import java .util .Collections ;
44
44
import java .util .List ;
45
45
import java .util .Map ;
46
46
import java .util .regex .Matcher ;
@@ -90,26 +90,39 @@ private TransformingSource(final Builder builder) {
90
90
super (builder );
91
91
_source = builder ._source ;
92
92
93
- _findAndReplace = Maps .newHashMapWithExpectedSize (builder ._findAndReplace .size ());
93
+ final ImmutableMap .Builder <Pattern , List <String >> findReplaceBuilder =
94
+ ImmutableMap .builderWithExpectedSize (builder ._findAndReplace .size ());
94
95
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 ()));
96
97
}
98
+ _findAndReplace = findReplaceBuilder .build ();
97
99
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 ));
99
104
}
100
105
101
106
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 ;
103
110
104
111
private static final Logger LOGGER = LoggerFactory .getLogger (TransformingSource .class );
105
112
private static final Splitter .MapSplitter TAG_SPLITTER = Splitter .on (';' ).omitEmptyStrings ().trimResults ().withKeyValueSeparator ('=' );
106
113
107
114
// NOTE: Package private for testing
108
115
/* package private */ static final class TransformingObserver implements Observer {
109
116
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 ) {
111
122
_source = source ;
112
123
_findAndReplace = findAndReplace ;
124
+ _inject = inject ;
125
+ _remove = remove ;
113
126
}
114
127
115
128
@ Override
@@ -141,25 +154,31 @@ public void notify(final Observable observable, final Object event) {
141
154
final int tagsStart = replacedString .indexOf (';' );
142
155
if (tagsStart == -1 ) {
143
156
// 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 ()));
145
162
} else {
146
163
final String newMetricName = replacedString .substring (0 , tagsStart );
147
164
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 ));
155
170
}
156
171
}
157
172
//Having "found" set here means that mapping a metric to an empty list suppresses that metric
158
173
found = true ;
159
174
}
160
175
}
161
176
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 ()));
163
182
}
164
183
}
165
184
@@ -183,6 +202,24 @@ public void notify(final Observable observable, final Object event) {
183
202
}
184
203
}
185
204
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
+
186
223
private void merge (final Metric metric , final String key ,
187
224
final Map <ImmutableMap <String , String >, Map <String , MergingMetric >> mergedMetrics ,
188
225
final ImmutableMap <String , String > dimensions ) {
@@ -208,6 +245,8 @@ private void merge(final Metric metric, final String key,
208
245
209
246
private final TransformingSource _source ;
210
247
private final Map <Pattern , List <String >> _findAndReplace ;
248
+ private final ImmutableMap <String , DimensionInjection > _inject ;
249
+ private final ImmutableList <String > _remove ;
211
250
}
212
251
213
252
// NOTE: Package private for testing
@@ -256,10 +295,6 @@ public String toString() {
256
295
* Represents a dimension to inject and whether or not it should overwrite the existing value (if any).
257
296
*/
258
297
public static final class DimensionInjection {
259
- public String getDimension () {
260
- return _dimension ;
261
- }
262
-
263
298
public String getValue () {
264
299
return _value ;
265
300
}
@@ -269,12 +304,10 @@ public boolean isReplaceExisting() {
269
304
}
270
305
271
306
private DimensionInjection (final Builder builder ) {
272
- _dimension = builder ._dimension ;
273
307
_value = builder ._value ;
274
308
_replaceExisting = builder ._replaceExisting ;
275
309
}
276
310
277
- private final String _dimension ;
278
311
private final String _value ;
279
312
private final boolean _replaceExisting ;
280
313
@@ -291,17 +324,6 @@ public Builder() {
291
324
super (DimensionInjection ::new );
292
325
}
293
326
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
-
305
327
/**
306
328
* Sets the value. Required. Cannot be null. Cannot be empty.
307
329
*
@@ -324,9 +346,6 @@ public Builder setReplaceExisting(final Boolean value) {
324
346
return this ;
325
347
}
326
348
327
- @ NotNull
328
- @ NotEmpty
329
- private String _dimension ;
330
349
@ NotNull
331
350
@ NotEmpty
332
351
private String _value ;
@@ -366,7 +385,7 @@ public Builder setSource(final Source value) {
366
385
* @param value The find and replace expression map.
367
386
* @return This instance of <code>Builder</code>.
368
387
*/
369
- public Builder setFindAndReplace (final Map <String , ? extends List <String >> value ) {
388
+ public Builder setFindAndReplace (final ImmutableMap <String , ? extends List <String >> value ) {
370
389
_findAndReplace = value ;
371
390
return this ;
372
391
}
@@ -377,11 +396,22 @@ public Builder setFindAndReplace(final Map<String, ? extends List<String>> value
377
396
* @param value List of dimensions to inject.
378
397
* @return This instance of <code>Builder</code>.
379
398
*/
380
- public Builder setInject (final List < DimensionInjection > value ) {
399
+ public Builder setInject (final ImmutableMap < String , DimensionInjection > value ) {
381
400
_inject = value ;
382
401
return this ;
383
402
}
384
403
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
+
385
415
@ Override
386
416
protected Builder self () {
387
417
return this ;
@@ -390,8 +420,10 @@ protected Builder self() {
390
420
@ NotNull
391
421
private Source _source ;
392
422
@ 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 ();
394
426
@ NotNull
395
- private List < DimensionInjection > _inject = Lists . newArrayList ();
427
+ private ImmutableList < String > _remove = ImmutableList . of ();
396
428
}
397
429
}
0 commit comments