Skip to content

Commit 5d442eb

Browse files
authored
Closes #587 - Using InspectITContext during metric recording and ignoring isTag (#594)
1 parent 2de3c61 commit 5d442eb

File tree

8 files changed

+149
-84
lines changed

8 files changed

+149
-84
lines changed

inspectit-ocelot-agent/src/system-test/resources/config/UserInstrumentationWithMetricsTest.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ inspectit:
3636
scopes:
3737
UserInstrumentationWithMetricsTest-invocationCount : true
3838
entry:
39-
method_name: {action: get_method_fqn}
39+
current_method_name: {action: get_method_fqn}
4040
metrics:
4141
'[my/invocation]':
4242
value: 42
43-
data-tags: {method_name: method_name, user_tag: user_tag}
43+
data-tags: {method_name: current_method_name, user_tag: user_tag}
4444

4545
record_method_duration:
4646
scopes:

inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/InspectitContextImpl.java

+5-12
Original file line numberDiff line numberDiff line change
@@ -347,29 +347,22 @@ private boolean isInDifferentThreadThanParentOrIsParentClosed() {
347347
public Scope enterFullTagScope() {
348348
TagContextBuilder builder = Tags.getTagger().emptyBuilder();
349349
dataTagsStream()
350-
.forEach(e -> builder.put(TagKey.create(e.getKey()), TagValue.create(e.getValue().toString())));
350+
.forEach(e -> builder.putLocal(TagKey.create(e.getKey()), TagValue.create(e.getValue().toString())));
351351
return builder.buildScoped();
352352
}
353353

354-
/**
355-
* Returns map of tags currently present in {@link #getData()}.
356-
*
357-
* @return the tag stream
358-
* @see #enterFullTagScope()
359-
*/
360-
public Map<String, Object> getFullTagMap() {
361-
return dataTagsStream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
362-
}
363-
364354
private Stream<Map.Entry<String, Object>> dataTagsStream() {
365355
return getDataAsStream()
366356
.filter(e -> propagation.isTag(e.getKey()))
367357
.filter(e -> ALLOWED_TAG_TYPES.contains(e.getValue().getClass()));
368358
}
369359

370360
/**
361+
* Returns the most recent value for data, which either was inherited form the parent context,
362+
* set via {@link #setData(String, Object)} or changed due to an up-propagation.
363+
*
371364
* @param key the name of the data to query
372-
* @return the most recent value for data, which either was inherited form the parent context, set via {@link #setData(String, Object)} or changed due to an up-propagation.
365+
* @return the data element which is related to the given key or `null` if it doesn't exist
373366
*/
374367
@Override
375368
public Object getData(String key) {

inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java

+22-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package rocks.inspectit.ocelot.core.instrumentation.hook;
22

3+
import com.google.common.annotations.VisibleForTesting;
34
import io.opencensus.stats.StatsRecorder;
45
import io.opencensus.trace.Sampler;
56
import io.opencensus.trace.samplers.Samplers;
@@ -173,27 +174,35 @@ private List<IHookAction> buildTracingExitActions(RuleTracingSettings tracing) {
173174
}
174175

175176
private Optional<IHookAction> buildMetricsRecorder(MethodHookConfiguration config) {
176-
Collection<MetricRecordingSettings> metrics = config.getMetrics();
177-
if (!metrics.isEmpty()) {
178-
List<MetricAccessor> metricAccessors = metrics.stream()
179-
.map(metricConfig -> {
180-
String value = metricConfig.getValue();
181-
VariableAccessor valueAccessor;
182-
try {
183-
valueAccessor = variableAccessorFactory.getConstantAccessor(Double.parseDouble(value));
184-
} catch (NumberFormatException e) {
185-
valueAccessor = variableAccessorFactory.getVariableAccessor(value);
186-
}
187-
return new MetricAccessor(metricConfig.getMetric(), valueAccessor, metricConfig.getConstantTags(), metricConfig.getDataTags());
188-
})
177+
Collection<MetricRecordingSettings> metricRecordingSettings = config.getMetrics();
178+
if (!metricRecordingSettings.isEmpty()) {
179+
List<MetricAccessor> metricAccessors = metricRecordingSettings.stream()
180+
.map(this::buildMetricAccessor)
189181
.collect(Collectors.toList());
182+
190183
MetricsRecorder recorder = new MetricsRecorder(metricAccessors, commonTagsManager, metricsManager, statsRecorder);
191184
return Optional.of(recorder);
192185
} else {
193186
return Optional.empty();
194187
}
195188
}
196189

190+
@VisibleForTesting
191+
MetricAccessor buildMetricAccessor(MetricRecordingSettings metricSettings) {
192+
String value = metricSettings.getValue();
193+
VariableAccessor valueAccessor;
194+
try {
195+
valueAccessor = variableAccessorFactory.getConstantAccessor(Double.parseDouble(value));
196+
} catch (NumberFormatException e) {
197+
valueAccessor = variableAccessorFactory.getVariableAccessor(value);
198+
}
199+
200+
Map<String, VariableAccessor> tagAccessors = metricSettings.getDataTags().entrySet().stream()
201+
.collect(Collectors.toMap(Map.Entry::getKey, entry -> variableAccessorFactory.getVariableAccessor(entry.getValue())));
202+
203+
return new MetricAccessor(metricSettings.getMetric(), valueAccessor, metricSettings.getConstantTags(), tagAccessors);
204+
}
205+
197206
private List<IHookAction> buildActionCalls(List<ActionCallConfig> calls, MethodReflectionInformation methodInfo) {
198207

199208
List<IHookAction> result = new ArrayList<>();

inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/MetricsRecorder.java

+10-18
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
import io.opencensus.tags.*;
66
import lombok.Value;
77
import lombok.extern.slf4j.Slf4j;
8+
import rocks.inspectit.ocelot.core.instrumentation.context.InspectitContextImpl;
89
import rocks.inspectit.ocelot.core.instrumentation.hook.actions.model.MetricAccessor;
910
import rocks.inspectit.ocelot.core.metrics.MeasuresAndViewsManager;
1011
import rocks.inspectit.ocelot.core.tags.CommonTagsManager;
1112

1213
import java.util.List;
13-
import java.util.Map;
1414
import java.util.Optional;
1515

1616
/**
@@ -42,50 +42,42 @@ public class MetricsRecorder implements IHookAction {
4242

4343
@Override
4444
public void execute(ExecutionContext context) {
45-
// get all available data from the context and collect in a map
46-
Map<String, Object> contextTags = null;
47-
4845
// then iterate all metrics and enter new scope for metric collection
4946
for (MetricAccessor metricAccessor : metrics) {
5047
Object value = metricAccessor.getVariableAccessor().get(context);
5148
if (value != null) {
5249
if (value instanceof Number) {
53-
// resolve context tags on the first need for it
54-
if (contextTags == null) {
55-
contextTags = context.getInspectitContext().getFullTagMap();
56-
}
57-
5850
// only record metrics where a value is present
5951
// this allows to disable the recording of a metric depending on the results of action executions
6052
MeasureMap measureMap = statsRecorder.newMeasureMap();
6153
metricsManager.tryRecordingMeasurement(metricAccessor.getName(), measureMap, (Number) value);
62-
TagContext tagContext = getTagContext(contextTags, metricAccessor);
54+
TagContext tagContext = getTagContext(context, metricAccessor);
6355
measureMap.record(tagContext);
6456
}
6557
}
6658
}
6759
}
6860

69-
private TagContext getTagContext(Map<String, Object> contextTags, MetricAccessor metricAccessor) {
61+
private TagContext getTagContext(ExecutionContext context, MetricAccessor metricAccessor) {
62+
InspectitContextImpl inspectitContext = context.getInspectitContext();
63+
7064
// create builder
7165
TagContextBuilder builder = Tags.getTagger().emptyBuilder();
7266

7367
// first common tags to allow overwrite by constant or data tags
7468
commonTagsManager.getCommonTagKeys()
75-
.forEach(commonTagKey -> Optional.ofNullable(contextTags.get(commonTagKey.getName()))
76-
.ifPresent(value -> builder.putLocal(commonTagKey, TagValue.create(value.toString())))
77-
//TODO if not present in the context do we pull the value from the common tag map
69+
.forEach(commonTagKey -> Optional.ofNullable(inspectitContext.getData(commonTagKey.getName()))
70+
.ifPresent(value -> builder.putLocal(commonTagKey, TagValue.create(value.toString())))
7871
);
7972

8073
// then constant tags to allow overwrite by data
8174
metricAccessor.getConstantTags()
8275
.forEach((key, value) -> builder.putLocal(TagKey.create(key), TagValue.create(value)));
8376

8477
// go over data tags and match the value to the key from the contextTags (if available)
85-
metricAccessor.getDataTags()
86-
.forEach((key, dataLink) -> Optional.ofNullable(contextTags.get(dataLink))
87-
.ifPresent(value -> builder.putLocal(TagKey.create(key), TagValue.create(value.toString())))
88-
);
78+
metricAccessor.getDataTagAccessors()
79+
.forEach((key, accessor) -> Optional.ofNullable(accessor.get(context))
80+
.ifPresent(tagValue -> builder.putLocal(TagKey.create(key), TagValue.create(tagValue.toString()))));
8981

9082
// build and return
9183
return builder.build();

inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/model/MetricAccessor.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ public class MetricAccessor {
3030
private final Map<String, String> constantTags;
3131

3232
/**
33-
* Data tags keys and values.
33+
* VariableAccessors for the data tags.
3434
*/
35-
private final Map<String, String> dataTags;
35+
private final Map<String, VariableAccessor> dataTagAccessors;
3636

3737
}

inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/context/InspectitContextImplTest.java

+11-15
Original file line numberDiff line numberDiff line change
@@ -850,12 +850,11 @@ void verifyLocalValuesPublishedCorrectly() {
850850
.containsEntry("global", "globalValue");
851851
}
852852

853-
Map<String, Object> fullTagMap = ctx.getFullTagMap();
854-
assertThat(fullTagMap).hasSize(2)
855-
.containsEntry("local", "localValue")
856-
.containsEntry("global", "globalValue");
853+
assertThat(ctx.getData("local")).isEqualTo("localValue");
854+
assertThat(ctx.getData("global")).isEqualTo("globalValue");
857855

858856
ctx.close();
857+
859858
assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull();
860859
}
861860

@@ -893,13 +892,12 @@ void verifyUpPropagatedValuesOnlyAvailableInFullTagScope() {
893892
.containsEntry("nestedKey2", "nestedValue2");
894893
}
895894

896-
Map<String, Object> fullTagMap = ctx.getFullTagMap();
897-
assertThat(fullTagMap).hasSize(3)
898-
.containsEntry("rootKey1", "nestedValue1")
899-
.containsEntry("rootKey2", "rootValue2")
900-
.containsEntry("nestedKey2", "nestedValue2");
895+
assertThat(ctx.getData("rootKey1")).isEqualTo("nestedValue1");
896+
assertThat(ctx.getData("rootKey2")).isEqualTo("rootValue2");
897+
assertThat(ctx.getData("nestedKey2")).isEqualTo("nestedValue2");
901898

902899
ctx.close();
900+
903901
assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull();
904902
}
905903

@@ -931,12 +929,10 @@ void verifyOnlyPrimitiveDataUsedAsTag() {
931929
.containsEntry("d4", "2.0");
932930
}
933931

934-
Map<String, Object> fullTagMap = ctx.getFullTagMap();
935-
assertThat(fullTagMap).hasSize(4)
936-
.containsEntry("d1", "string")
937-
.containsEntry("d2", 1)
938-
.containsEntry("d3", 2L)
939-
.containsEntry("d4", 2.0d);
932+
assertThat(ctx.getData("d1")).isEqualTo("string");
933+
assertThat(ctx.getData("d2")).isEqualTo(1);
934+
assertThat(ctx.getData("d3")).isEqualTo(2L);
935+
assertThat(ctx.getData("d4")).isEqualTo(2.0D);
940936

941937
ctx.close();
942938
}

inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGeneratorTest.java

+68
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,17 @@
99
import org.mockito.InjectMocks;
1010
import org.mockito.Mock;
1111
import org.mockito.junit.jupiter.MockitoExtension;
12+
import rocks.inspectit.ocelot.config.model.instrumentation.rules.MetricRecordingSettings;
1213
import rocks.inspectit.ocelot.core.instrumentation.config.model.MethodHookConfiguration;
1314
import rocks.inspectit.ocelot.core.instrumentation.context.ContextManager;
15+
import rocks.inspectit.ocelot.core.instrumentation.hook.actions.model.MetricAccessor;
1416
import rocks.inspectit.ocelot.core.testutils.Dummy;
1517

18+
import java.util.Collections;
19+
1620
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.assertj.core.api.Assertions.entry;
22+
import static org.mockito.Mockito.*;
1723

1824
@ExtendWith(MockitoExtension.class)
1925
public class MethodHookGeneratorTest {
@@ -24,6 +30,9 @@ public class MethodHookGeneratorTest {
2430
@InjectMocks
2531
MethodHookGenerator generator;
2632

33+
@Mock
34+
VariableAccessorFactory variableAccessorFactory;
35+
2736
@Nested
2837
class BuildHook {
2938

@@ -79,6 +88,65 @@ void verifyDeclaringClassCorrect() {
7988

8089
assertThat(result.getMethodInformation().getDeclaringClass()).isSameAs(Dummy.class);
8190
}
91+
}
92+
93+
@Nested
94+
class BuildMetricAccessor {
95+
96+
@Test
97+
public void valueOnly() {
98+
VariableAccessor mockAccessor = mock(VariableAccessor.class);
99+
when(variableAccessorFactory.getConstantAccessor(1D)).thenReturn(mockAccessor);
100+
MetricRecordingSettings settings = MetricRecordingSettings.builder().metric("name").value("1.0").build();
101+
102+
MetricAccessor accessor = generator.buildMetricAccessor(settings);
103+
104+
assertThat(accessor.getVariableAccessor()).isSameAs(mockAccessor);
105+
assertThat(accessor.getConstantTags()).isEmpty();
106+
assertThat(accessor.getDataTagAccessors()).isEmpty();
107+
}
82108

109+
@Test
110+
public void dataValueOnly() {
111+
VariableAccessor mockAccessor = mock(VariableAccessor.class);
112+
when(variableAccessorFactory.getVariableAccessor("data-key")).thenReturn(mockAccessor);
113+
MetricRecordingSettings settings = MetricRecordingSettings.builder().metric("name").value("data-key").build();
114+
115+
MetricAccessor accessor = generator.buildMetricAccessor(settings);
116+
117+
assertThat(accessor.getVariableAccessor()).isSameAs(mockAccessor);
118+
assertThat(accessor.getConstantTags()).isEmpty();
119+
assertThat(accessor.getDataTagAccessors()).isEmpty();
120+
}
121+
122+
@Test
123+
public void valueAndConstantTag() {
124+
VariableAccessor mockAccessor = mock(VariableAccessor.class);
125+
when(variableAccessorFactory.getConstantAccessor(1D)).thenReturn(mockAccessor);
126+
MetricRecordingSettings settings = MetricRecordingSettings.builder().metric("name").value("1.0")
127+
.constantTags(Collections.singletonMap("tag-key", "tag-key")).build();
128+
129+
MetricAccessor accessor = generator.buildMetricAccessor(settings);
130+
131+
assertThat(accessor.getVariableAccessor()).isSameAs(mockAccessor);
132+
assertThat(accessor.getConstantTags()).containsOnly(entry("tag-key", "tag-key"));
133+
assertThat(accessor.getDataTagAccessors()).isEmpty();
134+
}
135+
136+
@Test
137+
public void valueAndDataTag() {
138+
VariableAccessor mockAccessorA = mock(VariableAccessor.class);
139+
doReturn(mockAccessorA).when(variableAccessorFactory).getConstantAccessor(1D);
140+
VariableAccessor mockAccessorB = mock(VariableAccessor.class);
141+
doReturn(mockAccessorB).when(variableAccessorFactory).getVariableAccessor("tag-value");
142+
MetricRecordingSettings settings = MetricRecordingSettings.builder().metric("name").value("1.0")
143+
.dataTags(Collections.singletonMap("tag-key", "tag-value")).build();
144+
145+
MetricAccessor accessor = generator.buildMetricAccessor(settings);
146+
147+
assertThat(accessor.getVariableAccessor()).isSameAs(mockAccessorA);
148+
assertThat(accessor.getConstantTags()).isEmpty();
149+
assertThat(accessor.getDataTagAccessors()).containsOnly(entry("tag-key", mockAccessorB));
150+
}
83151
}
84152
}

0 commit comments

Comments
 (0)