Skip to content

Commit

Permalink
Support non-string type telemetry processor attributes (#3388)
Browse files Browse the repository at this point in the history
  • Loading branch information
heyams authored Dec 5, 2023
1 parent f0f8c8e commit 7498f01
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,13 @@ public void validate(ProcessorType processorType, IncludeExclude includeExclude)
+ " processors here: https://go.microsoft.com/fwlink/?linkid=2151557");
}
if (matchType == MatchType.REGEXP && attribute.value != null) {
validateRegex(attribute.value, processorType);
validateRegex(String.valueOf(attribute.value), processorType);
}

// regex matches always convert the value to string first, so no need to validate the regex
// type.
if (matchType == MatchType.STRICT) {
attribute.validate();
}
}

Expand Down Expand Up @@ -1094,7 +1100,96 @@ private static void validateRegex(String value, ProcessorType processorType) {

public static class ProcessorAttribute {
public String key;
public String value;
public Object value;
public AttributeType type =
AttributeType.STRING; // default to string for backward compatibility

// TODO (heya) remove this and reuse the standalone exporter Mappings.convertToString, need to
// make it public
public AttributeKey<?> getAttributeKey() {
switch (type) {
case STRING:
return AttributeKey.stringKey(key);
case BOOLEAN:
return AttributeKey.booleanKey(key);
case LONG:
return AttributeKey.longKey(key);
case DOUBLE:
return AttributeKey.doubleKey(key);
case STRING_ARRAY:
return AttributeKey.stringArrayKey(key);
case BOOLEAN_ARRAY:
return AttributeKey.booleanArrayKey(key);
case LONG_ARRAY:
return AttributeKey.longArrayKey(key);
case DOUBLE_ARRAY:
return AttributeKey.doubleArrayKey(key);
}
throw new IllegalStateException("Unexpected attribute key type: " + type);
}

public Object getAttributeValue() {
if (value instanceof Integer) {
if (type == AttributeType.LONG) {
return ((Integer) value).longValue();
} else if (type == AttributeType.DOUBLE) {
return ((Integer) value).doubleValue();
}
}
return value;
}

public void validate() {
if (type == AttributeType.STRING && value != null && !(value instanceof String)) {
throw new FriendlyException(
"Telemetry processor attribute '"
+ key
+ "' has type of String, but the value is not a String.",
"Please provide a json value which matches the attribute type.");
} else if (type == AttributeType.LONG && !(getAttributeValue() instanceof Long)) {
throw new FriendlyException(
"Telemetry processor attribute '"
+ key
+ "' has type of Long or Integer, but the value is not a Long nor an Integer.",
"Please provide a json value which matches the attribute type.");
} else if (type == AttributeType.DOUBLE && !(getAttributeValue() instanceof Double)) {
throw new FriendlyException(
"Telemetry processor attribute '"
+ key
+ "' has type of Double or Integer, but the value is not a Double nor an Integer.",
"Please provide a json value which matches the attribute type.");
} else if (type == AttributeType.BOOLEAN && !(value instanceof Boolean)) {
throw new FriendlyException(
"Telemetry processor attribute '"
+ key
+ "' has type of Boolean, but the value is not a Boolean.",
"Please provide a json value which matches the attribute type.");
} else if (type == AttributeType.STRING_ARRAY && !(value instanceof List)) {
throw new FriendlyException(
"Telemetry processor attribute '"
+ key
+ "' has type of an array of String, but the value is not an array of String.",
"Please provide a json value which matches the attribute type.");
} else if (type == AttributeType.LONG_ARRAY && !(value instanceof List)) {
throw new FriendlyException(
"Telemetry processor attribute '"
+ key
+ "' has type of an array of Long or Integer, but the value is not an array of Long nor Integer.",
"Please provide a json value which matches the attribute type.");
} else if (type == AttributeType.DOUBLE_ARRAY && !(value instanceof List)) {
throw new FriendlyException(
"Telemetry processor attribute '"
+ key
+ "' has type of an array of Double or Integer, but the value is not an array of Double nor Integer.",
"Please provide a json value which matches the attribute type.");
} else if (type == AttributeType.BOOLEAN_ARRAY && !(value instanceof List)) {
throw new FriendlyException(
"Telemetry processor attribute '"
+ key
+ "' has type of an array of Boolean, but the value is not an array of Boolean.",
"Please provide a json value which matches the attribute type.");
}
}
}

public static class ExtractAttribute {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,12 @@ public boolean isMatch(Attributes attributes, String name) {
private boolean checkAttributes(Attributes attributes) {
for (ProcessorAttribute attribute : processorAttributes) {
// All of these attributes must match exactly for a match to occur.
Object existingAttributeValue = attributes.get(AttributeKey.stringKey(attribute.key));
// to get the string value
// existingAttributeValue.toString()
// String.valueOf(existingAttributeValue);
if (!(existingAttributeValue instanceof String)) {
Object valueObject = attributes.get(attribute.getAttributeKey());
if (valueObject == null) {
// user specified key not found
return false;
}
if (attribute.value != null && !existingAttributeValue.equals(attribute.value)) {
if (attribute.value != null && !valueObject.equals(attribute.getAttributeValue())) {
// user specified value doesn't match
return false;
}
Expand Down Expand Up @@ -123,7 +120,7 @@ public static RegexpIncludeExclude create(
for (ProcessorAttribute attribute : attributes) {
if (attribute.value != null) {
attributeKeyValuePatterns.put(
AttributeKey.stringKey(attribute.key), Pattern.compile(attribute.value));
attribute.getAttributeKey(), Pattern.compile(String.valueOf(attribute.value)));
}
}
}
Expand Down Expand Up @@ -176,13 +173,11 @@ public boolean isMatch(Attributes attributes, String name) {
private boolean checkAttributes(Attributes attributes) {
for (Entry<AttributeKey<?>, Pattern> attributeEntry : attributeValuePatterns.entrySet()) {
// All of these attributes must match exactly for a match to occur.

Object existingAttributeValue = attributes.get(attributeEntry.getKey());
if (!(existingAttributeValue instanceof String)) {
// user specified key not found
return false;
}
if (attributeEntry.getValue() != null
&& !isAttributeValueMatch((String) existingAttributeValue, attributeEntry.getValue())) {
&& !isAttributeValueMatch(
String.valueOf(existingAttributeValue), attributeEntry.getValue())) {
// user specified value doesn't match
return false;
}
Expand Down
1 change: 1 addition & 0 deletions smoke-tests/apps/TelemetryProcessors/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ dependencies {
}
// this dependency is needed to make wildfly happy
implementation("org.reactivestreams:reactive-streams:1.0.3")
implementation("io.opentelemetry:opentelemetry-api:1.31.0")
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

package com.microsoft.applicationinsights.smoketestapp;

import static io.opentelemetry.api.common.AttributeKey.doubleArrayKey;

import io.opentelemetry.api.trace.Span;
import java.util.Arrays;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -23,7 +27,6 @@ public String test() {
return "OK!";
}

// span name: GET /sensitivedata
@GetMapping("/sensitivedata")
public String sensitiveData() {
return "some sensitive data!";
Expand All @@ -34,6 +37,22 @@ public String sensitiveDataInUrl() {
return "Test sensitive data in URL";
}

@GetMapping("/test-non-string-strict-span-attributes")
public String testNonStringStrictSpanAttributes() {
Span.current()
.setAttribute("myLongAttributeKey", 1234)
.setAttribute("myBooleanAttributeKey", true)
.setAttribute(
doubleArrayKey("myDoubleArrayAttributeKey"), Arrays.asList(1.0, 2.0, 3.0, 4.0));
return "Test non string strict type span attributes";
}

@GetMapping("/test-non-string-regex-span-attributes")
public String testNonStringRegexSpanAttributes() {
Span.current().setAttribute("myLongRegexAttributeKey", 428);
return "Test non string regex type span attributes";
}

@GetMapping("/mask-user-id-in-log-body")
public String maskUserIdInLogBody() {
logger.info("User account with userId 123456xx failed to login");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.applicationinsights.smoketest;

import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_11;
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_11_OPENJ9;
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_17;
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_19;
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_20;
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_8;
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_8_OPENJ9;
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.WILDFLY_13_JAVA_8;
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.WILDFLY_13_JAVA_8_OPENJ9;
import static org.assertj.core.api.Assertions.assertThat;

import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

@UseAgent("applicationinsights-non-string-span-attributes.json")
abstract class TelemetryProcessorsNonStringAttributesTest {

@RegisterExtension static final SmokeTestExtension testing = SmokeTestExtension.create();

@Test
@TargetUri("/test-non-string-strict-span-attributes")
void testNonStringStrictSpanAttributes() throws Exception {
Telemetry telemetry = testing.getTelemetry(0);
Map<String, String> properties = telemetry.rd.getProperties();

assertThat(properties.get("myLongAttributeKey")).isEqualTo("1234");
assertThat(properties.get("myBooleanAttributeKey")).isEqualTo("true");
assertThat(properties.get("myDoubleArrayAttributeKey")).contains("1.0", "2.0", "3.0", "4.0");
assertThat(properties.get("myNewAttributeKeyStrict")).isEqualTo("myNewAttributeValueStrict");
}

@Test
@TargetUri("/test-non-string-regex-span-attributes")
void testNonStringRegexSpanAttributes() throws Exception {
Telemetry telemetry = testing.getTelemetry(0);
Map<String, String> properties = telemetry.rd.getProperties();

assertThat(properties.get("myLongRegexAttributeKey")).isEqualTo("428");
assertThat(properties.get("myNewAttributeKeyRegex")).isEqualTo("myNewAttributeValueRegex");
}

@Environment(TOMCAT_8_JAVA_8)
static class Tomcat8Java8Test extends TelemetryProcessorsNonStringAttributesTest {}

@Environment(TOMCAT_8_JAVA_8_OPENJ9)
static class Tomcat8Java8OpenJ9Test extends TelemetryProcessorsNonStringAttributesTest {}

@Environment(TOMCAT_8_JAVA_11)
static class Tomcat8Java11Test extends TelemetryProcessorsNonStringAttributesTest {}

@Environment(TOMCAT_8_JAVA_11_OPENJ9)
static class Tomcat8Java11OpenJ9Test extends TelemetryProcessorsNonStringAttributesTest {}

@Environment(TOMCAT_8_JAVA_17)
static class Tomcat8Java17Test extends TelemetryProcessorsNonStringAttributesTest {}

@Environment(TOMCAT_8_JAVA_19)
static class Tomcat8Java19Test extends TelemetryProcessorsNonStringAttributesTest {}

@Environment(TOMCAT_8_JAVA_20)
static class Tomcat8Java20Test extends TelemetryProcessorsNonStringAttributesTest {}

@Environment(WILDFLY_13_JAVA_8)
static class Wildfly13Java8Test extends TelemetryProcessorsNonStringAttributesTest {}

@Environment(WILDFLY_13_JAVA_8_OPENJ9)
static class Wildfly13Java8OpenJ9Test extends TelemetryProcessorsNonStringAttributesTest {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"role": {
"name": "testrolename",
"instance": "testroleinstance"
},
"sampling": {
"percentage": 100
},
"preview": {
"processors": [
{
"type": "attribute",
"include": {
"matchType": "strict",
"attributes": [
{
"key": "myLongAttributeKey",
"value": 1234,
"type": "long"
},
{
"key": "myBooleanAttributeKey",
"value": true,
"type": "boolean"
},
{
"key": "myDoubleArrayAttributeKey",
"value": [1.0, 2.0, 3.0, 4.0],
"type": "double-array"
}
]
},
"actions": [
{
"key": "myNewAttributeKeyStrict",
"value": "myNewAttributeValueStrict",
"action": "insert"
}
],
"id": "attributes/insertMyNewAttributeKeyStrict"
},
{
"type": "attribute",
"include": {
"matchType": "regexp",
"attributes": [
{
"key": "myLongRegexAttributeKey",
"value": "4[0-9][0-9]",
"type": "long"
}
]
},
"actions": [
{
"key": "myNewAttributeKeyRegex",
"value": "myNewAttributeValueRegex",
"action": "insert"
}
],
"id": "attributes/insertMyNewAttributeKeyRegex"
}
]
}
}

0 comments on commit 7498f01

Please sign in to comment.