Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/python-ec2-adot-sigv4-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ env:
E2E_TEST_ROLE_NAME: ${{ secrets.APPLICATION_SIGNALS_E2E_TEST_ROLE_NAME }}
METRIC_NAMESPACE: ApplicationSignals
LOG_GROUP_NAME: aws/spans
TEST_LOG_GROUP_NAME: otlp_sigv4_logs
TEST_RESOURCES_FOLDER: ${GITHUB_WORKSPACE}

jobs:
Expand Down Expand Up @@ -218,6 +219,23 @@ jobs:
--instance-ami ${{ env.EC2_INSTANCE_AMI }}
--instance-id ${{ env.MAIN_SERVICE_INSTANCE_ID }}
--rollup'

- name: Validate generated SigV4 logs
id: log-validation-1
run: ./gradlew validator:run --args='-c python/ec2/adot-sigv4/logs/log-validation.yml
--testing-id ${{ env.TESTING_ID }}
--endpoint http://localhost:8000
--remote-service-deployment-name ${{ env.REMOTE_SERVICE_IP }}:8001
--region ${{ env.E2E_TEST_AWS_REGION }}
--account-id ${{ env.E2E_TEST_ACCOUNT_ID }}
--metric-namespace ${{ env.METRIC_NAMESPACE }}
--log-group ${{ env.TEST_LOG_GROUP_NAME }}
--service-name python-sample-application-${{ env.TESTING_ID }}
--remote-service-name python-sample-remote-application-${{ env.TESTING_ID }}
--query-string ip=${{ env.REMOTE_SERVICE_IP }}&testingId=${{ env.TESTING_ID }}
--instance-ami ${{ env.EC2_INSTANCE_AMI }}
--instance-id ${{ env.MAIN_SERVICE_INSTANCE_ID }}
--rollup'

- name: Refresh AWS Credentials
if: always()
Expand Down
16 changes: 12 additions & 4 deletions terraform/python/ec2/adot-sigv4/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,14 @@ resource "null_resource" "main_service_setup" {
export OTEL_PYTHON_CONFIGURATOR="aws_configurator"
export OTEL_AWS_APPLICATION_SIGNALS_ENABLED=false
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
export OTEL_LOGS_EXPORT=none \
export OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf \
export OTEL_LOGS_EXPORTER=otlp \
export OTEL_METRICS_EXPORTER=none \
export OTEL_TRACES_EXPORTER=otlp
export OTEL_TRACES_EXPORTER=otlp \
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://xray.${var.aws_region}.amazonaws.com/v1/traces \
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://logs.${var.aws_region}.amazonaws.com/v1/logs \
export OTEL_EXPORTER_OTLP_LOGS_HEADERS=x-aws-log-group=otlp_sigv4_logs,x-aws-log-stream=default \
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true \
export OTEL_SERVICE_NAME=python-sample-application-${var.test_id}
export OTEL_TRACES_SAMPLER=always_on
python${var.language_version} manage.py migrate
Expand Down Expand Up @@ -284,10 +288,14 @@ resource "null_resource" "remote_service_setup" {
export OTEL_PYTHON_CONFIGURATOR="aws_configurator"
export OTEL_AWS_APPLICATION_SIGNALS_ENABLED=false
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
export OTEL_LOGS_EXPORT=none \
export OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf \
export OTEL_LOGS_EXPORTER=otlp \
export OTEL_METRICS_EXPORTER=none \
export OTEL_TRACES_EXPORTER=otlp
export OTEL_TRACES_EXPORTER=otlp \
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://xray.${var.aws_region}.amazonaws.com/v1/traces \
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://logs.${var.aws_region}.amazonaws.com/v1/logs \
export OTEL_EXPORTER_OTLP_LOGS_HEADERS=x-aws-log-group=otlp_sigv4_logs,x-aws-log-stream=default \
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true \
export OTEL_SERVICE_NAME=python-sample-remote-application-${var.test_id}
export OTEL_TRACES_SAMPLER=always_on
python${var.language_version} manage.py migrate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,23 +222,26 @@ public enum PredefinedExpectedTemplate implements FileConfig {

/** Python EC2 ADOT SigV4 (Stand Alone ADOT) Test Case Validations */
PYTHON_EC2_ADOT_SIGV4_OUTGOING_HTTP_CALL_LOG(
"/expected-data-template/python/ec2/adot-sigv4/outgoing-http-call-log.mustache"),
"/expected-data-template/python/ec2/adot-sigv4/traces/outgoing-http-call-log.mustache"),
PYTHON_EC2_ADOT_SIGV4_OUTGOING_HTTP_CALL_METRIC(
"/expected-data-template/python/ec2/adot-sigv4/outgoing-http-call-metric.mustache"),
"/expected-data-template/python/ec2/adot-sigv4/traces/outgoing-http-call-metric.mustache"),
PYTHON_EC2_ADOT_SIGV4_OUTGOING_HTTP_CALL_TRACE(
"/expected-data-template/python/ec2/adot-sigv4/outgoing-http-call-trace.mustache"),
"/expected-data-template/python/ec2/adot-sigv4/traces/outgoing-http-call-trace.mustache"),

PYTHON_EC2_ADOT_SIGV4_AWS_SDK_CALL_LOG("/expected-data-template/python/ec2/adot-sigv4/aws-sdk-call-log.mustache"),
PYTHON_EC2_ADOT_SIGV4_AWS_SDK_CALL_METRIC("/expected-data-template/python/ec2/adot-sigv4/aws-sdk-call-metric.mustache"),
PYTHON_EC2_ADOT_SIGV4_AWS_SDK_CALL_TRACE("/expected-data-template/python/ec2/adot-sigv4/aws-sdk-call-trace.mustache"),
PYTHON_EC2_ADOT_SIGV4_AWS_SDK_CALL_LOG("/expected-data-template/python/ec2/adot-sigv4/traces/aws-sdk-call-log.mustache"),
PYTHON_EC2_ADOT_SIGV4_AWS_SDK_CALL_METRIC("/expected-data-template/python/ec2/adot-sigv4/traces/aws-sdk-call-metric.mustache"),
PYTHON_EC2_ADOT_SIGV4_AWS_SDK_CALL_TRACE("/expected-data-template/python/ec2/adot-sigv4/traces/aws-sdk-call-trace.mustache"),

PYTHON_EC2_ADOT_SIGV4_REMOTE_SERVICE_LOG("/expected-data-template/python/ec2/adot-sigv4/remote-service-log.mustache"),
PYTHON_EC2_ADOT_SIGV4_REMOTE_SERVICE_METRIC("/expected-data-template/python/ec2/adot-sigv4/remote-service-metric.mustache"),
PYTHON_EC2_ADOT_SIGV4_REMOTE_SERVICE_TRACE("/expected-data-template/python/ec2/adot-sigv4/remote-service-trace.mustache"),
PYTHON_EC2_ADOT_SIGV4_REMOTE_SERVICE_LOG("/expected-data-template/python/ec2/adot-sigv4/traces/remote-service-log.mustache"),
PYTHON_EC2_ADOT_SIGV4_REMOTE_SERVICE_METRIC("/expected-data-template/python/ec2/adot-sigv4/traces/remote-service-metric.mustache"),
PYTHON_EC2_ADOT_SIGV4_REMOTE_SERVICE_TRACE("/expected-data-template/python/ec2/adot-sigv4/traces/remote-service-trace.mustache"),

PYTHON_EC2_ADOT_SIGV4_CLIENT_CALL_LOG("/expected-data-template/python/ec2/adot-sigv4/client-call-log.mustache"),
PYTHON_EC2_ADOT_SIGV4_CLIENT_CALL_METRIC("/expected-data-template/python/ec2/adot-sigv4/client-call-metric.mustache"),
PYTHON_EC2_ADOT_SIGV4_CLIENT_CALL_TRACE("/expected-data-template/python/ec2/adot-sigv4/client-call-trace.mustache"),
PYTHON_EC2_ADOT_SIGV4_CLIENT_CALL_LOG("/expected-data-template/python/ec2/adot-sigv4/traces/client-call-log.mustache"),
PYTHON_EC2_ADOT_SIGV4_CLIENT_CALL_METRIC("/expected-data-template/python/ec2/adot-sigv4/traces/client-call-metric.mustache"),
PYTHON_EC2_ADOT_SIGV4_CLIENT_CALL_TRACE("/expected-data-template/python/ec2/adot-sigv4/traces/client-call-trace.mustache"),

/** Python EC2 ADOT SigV4 Log Exporter Test Case Validation */
PYTHON_EC2_ADOT_SIGV4_LOG("/expected-data-template/python/ec2/adot-sigv4/logs/log.mustache"),

/** Python K8S Test Case Validations */
PYTHON_K8S_OUTGOING_HTTP_CALL_LOG("/expected-data-template/python/k8s/outgoing-http-call-log.mustache"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,32 +65,36 @@ public void validate() throws Exception {
// which are in normal text as they are needed for
// the filter expressions for retrieving the actual logs.
log.info("Searching for expected log: {}", expectedAttributes);
String operation = (String) expectedAttributes.get("Operation");
String remoteService = (String) expectedAttributes.get("RemoteService");
String remoteOperation = (String) expectedAttributes.get("RemoteOperation");
String remoteResourceType = (String) expectedAttributes.get("RemoteResourceType");
String remoteResourceIdentifier = (String) expectedAttributes.get("RemoteResourceIdentifier");

Map<String, Object> actualLog;

// Parsing unique identifiers in OTLP spans
if (operation == null) {
operation = (String) expectedAttributes.get("attributes[\\\"aws.local.operation\\\"]");
remoteService = (String) expectedAttributes.get("attributes[\\\"aws.remote.service\\\"]");
remoteOperation = (String) expectedAttributes.get("attributes[\\\"aws.remote.operation\\\"]");
// Runtime metrics have no operation at all, we must ensure we are in the proper use case
if (operation != null) {
actualLog = this.getActualOtelSpanLog(operation, remoteService, remoteOperation);
} else {
// No operation at all -> Runtime metric
if (isOtlpSigV4Log(expectedAttributes)) {
actualLog = this.getActualOtlpSigV4Log();
} else {
String operation = (String) expectedAttributes.get("Operation");
String remoteService = (String) expectedAttributes.get("RemoteService");
String remoteOperation = (String) expectedAttributes.get("RemoteOperation");
String remoteResourceType = (String) expectedAttributes.get("RemoteResourceType");
String remoteResourceIdentifier = (String) expectedAttributes.get("RemoteResourceIdentifier");

// Parsing unique identifiers in OTLP spans
if (operation == null) {
operation = (String) expectedAttributes.get("attributes[\\\"aws.local.operation\\\"]");
remoteService = (String) expectedAttributes.get("attributes[\\\"aws.remote.service\\\"]");
remoteOperation = (String) expectedAttributes.get("attributes[\\\"aws.remote.operation\\\"]");
// Runtime metrics have no operation at all, we must ensure we are in the proper use case
if (operation != null) {
actualLog = this.getActualOtelSpanLog(operation, remoteService, remoteOperation);
} else {
// No operation at all -> Runtime metric
actualLog =
this.getActualLog(operation, remoteService, remoteOperation, remoteResourceType, remoteResourceIdentifier);
}
}
else {
actualLog =
this.getActualLog(operation, remoteService, remoteOperation, remoteResourceType, remoteResourceIdentifier);
}
}
else {
actualLog =
this.getActualLog(operation, remoteService, remoteOperation, remoteResourceType, remoteResourceIdentifier);
}
log.info("Value of an actual log: {}", actualLog);

if (actualLog == null) throw new BaseException(ExceptionCode.EXPECTED_LOG_NOT_FOUND);
Expand Down Expand Up @@ -147,6 +151,13 @@ private JsonifyArrayList<Map<String, Object>> getExpectedAttributes() throws Exc
return flattenedJsonMapForExpectedLogArray;
}

private boolean isOtlpSigV4Log(Map<String, Object> expectedAttributes) {
// OTLP SigV4 logs have 'body' as a top-level attribute
return expectedAttributes.containsKey("body") &&
expectedAttributes.containsKey("severityNumber") &&
expectedAttributes.containsKey("severityText");
}

private Map<String, Object> getActualLog(
String operation, String remoteService, String remoteOperation, String remoteResourceType, String remoteResourceIdentifier) throws Exception {
String dependencyFilter = null;
Expand Down Expand Up @@ -223,6 +234,27 @@ private Map<String, Object> getActualOtelSpanLog(String operation, String remote
return JsonFlattener.flattenAsMap(retrievedLogs.get(0).getMessage());
}

private Map<String, Object> getActualOtlpSigV4Log() throws Exception {
String filterPattern= String.format(
"{ ($.attributes.otelServiceName = \"%s\") && ($.body = \"This is a custom log for validation testing\") }",
context.getServiceName()
);
log.info("Filter Pattern for OTLP SigV4 Log Search: " + filterPattern);

List<FilteredLogEvent> retrievedLogs =
this.cloudWatchService.filterLogs(
context.getLogGroup(),
filterPattern,
System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5),
10);

if (retrievedLogs == null || retrievedLogs.isEmpty()) {
throw new BaseException(ExceptionCode.EMPTY_LIST);
}

return JsonFlattener.flattenAsMap(retrievedLogs.get(0).getMessage());
}

@Override
public void init(
Context context,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[{
"resource": {
"attributes": {
"aws.local.service": "{{serviceName}}",
"service.name": "{{serviceName}}",
"cloud.provider": "aws",
"cloud.region": "{{region}}",
"cloud.account.id": "{{accountId}}",
"cloud.platform": "aws_ec2"
}
},
"severityNumber": "^[0-9]+$",
"severityText": "{{severityText}}",
"body": "This is a custom log for validation testing",
"traceId": "{{traceId}}",
"spanId": "{{spanId}}",
"attributes": {
"otelServiceName": "{{serviceName}}"
}
}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-
validationType: "cw-log"
expectedLogStructureTemplate: "PYTHON_EC2_ADOT_SIGV4_LOG"
Loading