diff --git a/aws/logs_monitoring/settings.py b/aws/logs_monitoring/settings.py index 85b67d188..de7722b6f 100644 --- a/aws/logs_monitoring/settings.py +++ b/aws/logs_monitoring/settings.py @@ -166,11 +166,11 @@ def __init__(self, name, pattern, placeholder): # Option to redact all pattern that looks like an ip address / email address / custom pattern SCRUBBING_RULE_CONFIGS = [ ScrubbingRuleConfig( - "REDACT_IP", "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", "xxx.xxx.xxx.xxx" + "REDACT_IP", r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", "xxx.xxx.xxx.xxx" ), ScrubbingRuleConfig( "REDACT_EMAIL", - "[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+", + r"[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+", "xxxxx@xxxxx.com", ), ScrubbingRuleConfig( diff --git a/aws/logs_monitoring/steps/common.py b/aws/logs_monitoring/steps/common.py index 90aed9145..d2551246b 100644 --- a/aws/logs_monitoring/steps/common.py +++ b/aws/logs_monitoring/steps/common.py @@ -6,10 +6,23 @@ AwsCwEventSourcePrefix, AwsS3EventSourceKeyword, ) -from settings import DD_CUSTOM_TAGS, DD_SERVICE, DD_SOURCE +from settings import ( + AWS_STRING, + DD_CUSTOM_TAGS, + DD_FORWARDER_VERSION, + DD_SERVICE, + DD_SOURCE, + DD_TAGS, + FUNCTIONVERSION_STRING, + FORWARDERNAME_STRING, + FORWARDERMEMSIZE_STRING, + FORWARDERVERSION_STRING, + INVOKEDFUNCTIONARN_STRING, + SOURCECATEGORY_STRING, +) CLOUDTRAIL_REGEX = re.compile( - "\d+_CloudTrail(|-Digest|-Insight)_\w{2}(|-gov|-cn)-\w{4,9}-\d_(|.+)\d{8}T\d{4,6}Z(|.+).json.gz$", + r"\d+_CloudTrail(|-Digest|-Insight)_\w{2}(|-gov|-cn)-\w{4,9}-\d_(|.+)\d{8}T\d{4,6}Z(|.+).json.gz$", re.I, ) @@ -101,3 +114,38 @@ def merge_dicts(a, b, path=None): else: a[key] = b[key] return a + + +def generate_metadata(context): + metadata = { + SOURCECATEGORY_STRING: AWS_STRING, + AWS_STRING: { + FUNCTIONVERSION_STRING: context.function_version, + INVOKEDFUNCTIONARN_STRING: context.invoked_function_arn, + }, + } + # Add custom tags here by adding new value with the following format "key1:value1, key2:value2" - might be subject to modifications + dd_custom_tags_data = generate_custom_tags(context) + metadata[DD_CUSTOM_TAGS] = ",".join( + filter( + None, + [ + DD_TAGS, + ",".join( + ["{}:{}".format(k, v) for k, v in dd_custom_tags_data.items()] + ), + ], + ) + ) + + return metadata + + +def generate_custom_tags(context): + dd_custom_tags_data = { + FORWARDERNAME_STRING: context.function_name.lower(), + FORWARDERMEMSIZE_STRING: context.memory_limit_in_mb, + FORWARDERVERSION_STRING: DD_FORWARDER_VERSION, + } + + return dd_custom_tags_data diff --git a/aws/logs_monitoring/steps/enrichment.py b/aws/logs_monitoring/steps/enrichment.py index 7dc312621..c1bc82650 100644 --- a/aws/logs_monitoring/steps/enrichment.py +++ b/aws/logs_monitoring/steps/enrichment.py @@ -54,11 +54,16 @@ def add_metadata_to_lambda_log(event, cache_layer): if not lambda_log_arn: return + # Function name is the seventh piece of the ARN + try: + function_name = lambda_log_arn.split(":")[6] + except IndexError: + logger.error(f"Failed to extract function name from ARN: {lambda_log_arn}") + return + # Set Lambda ARN to "host" event[DD_HOST] = lambda_log_arn - # Function name is the seventh piece of the ARN - function_name = lambda_log_arn.split(":")[6] tags = [f"functionname:{function_name}"] # Get custom tags of the Lambda function @@ -67,7 +72,7 @@ def add_metadata_to_lambda_log(event, cache_layer): # If not set during parsing or has a default value # then set the service tag from lambda tags cache or using the function name # otherwise, remove the service tag from the custom lambda tags if exists to avoid duplication - if not event[DD_SERVICE] or event[DD_SERVICE] == event[DD_SOURCE]: + if not event.get(DD_SERVICE) or event.get(DD_SERVICE) == event.get(DD_SOURCE): service_tag = next( (tag for tag in custom_lambda_tags if tag.startswith("service:")), f"service:{function_name}", @@ -85,16 +90,21 @@ def add_metadata_to_lambda_log(event, cache_layer): custom_env_tag = next( (tag for tag in custom_lambda_tags if tag.startswith("env:")), None ) - if custom_env_tag is not None: - event[DD_CUSTOM_TAGS] = event[DD_CUSTOM_TAGS].replace("env:none", "") + if custom_env_tag: + event[DD_CUSTOM_TAGS] = ",".join( + [t for t in event.get(DD_CUSTOM_TAGS, "").split(",") if t != "env:none"] + ) - tags += custom_lambda_tags + tags.extend(custom_lambda_tags) # Dedup tags, so we don't end up with functionname twice tags = list(set(tags)) tags.sort() # Keep order deterministic - event[DD_CUSTOM_TAGS] = ",".join([event[DD_CUSTOM_TAGS]] + tags) + if custom_tags := event.get(DD_CUSTOM_TAGS): + event[DD_CUSTOM_TAGS] = ",".join([custom_tags] + tags) + else: + event[DD_CUSTOM_TAGS] = ",".join(tags) def get_enriched_lambda_log_tags(log_event, cache_layer): @@ -164,7 +174,7 @@ def extract_ddtags_from_message(event): [ tag for tag in event[DD_CUSTOM_TAGS].split(",") - if not tag.startswith("service") + if not tag.startswith("service:") ] ) diff --git a/aws/logs_monitoring/steps/handlers/awslogs_handler.py b/aws/logs_monitoring/steps/handlers/awslogs_handler.py index 4ab2e24df..8bdaabd78 100644 --- a/aws/logs_monitoring/steps/handlers/awslogs_handler.py +++ b/aws/logs_monitoring/steps/handlers/awslogs_handler.py @@ -8,6 +8,7 @@ from steps.common import ( add_service_tag, + generate_metadata, merge_dicts, parse_event_source, ) @@ -33,7 +34,9 @@ def __init__(self, context, cache_layer): self.context = context self.cache_layer = cache_layer - def handle(self, event, metadata): + def handle(self, event): + # Generate metadata + metadata = generate_metadata(self.context) # Get logs logs = self.extract_logs(event) # Build aws attributes @@ -65,7 +68,8 @@ def handle(self, event, metadata): self.process_eks_logs(metadata, aws_attributes) # Create and send structured logs to Datadog for log in logs["logEvents"]: - yield merge_dicts(log, aws_attributes.to_dict()) + merged = merge_dicts(log, aws_attributes.to_dict()) + yield merge_dicts(merged, metadata) @staticmethod def extract_logs(event): diff --git a/aws/logs_monitoring/steps/parsing.py b/aws/logs_monitoring/steps/parsing.py index fe58ff551..be8b84d0c 100644 --- a/aws/logs_monitoring/steps/parsing.py +++ b/aws/logs_monitoring/steps/parsing.py @@ -11,23 +11,14 @@ from steps.handlers.awslogs_handler import AwsLogsHandler from steps.handlers.s3_handler import S3EventHandler from steps.common import ( - merge_dicts, + generate_metadata, get_service_from_tags_and_remove_duplicates, + merge_dicts, ) from steps.enums import AwsEventType, AwsEventTypeKeyword, AwsEventSource from settings import ( - AWS_STRING, - FUNCTIONVERSION_STRING, - INVOKEDFUNCTIONARN_STRING, - SOURCECATEGORY_STRING, - FORWARDERNAME_STRING, - FORWARDERMEMSIZE_STRING, - FORWARDERVERSION_STRING, - DD_TAGS, DD_SOURCE, - DD_CUSTOM_TAGS, DD_SERVICE, - DD_FORWARDER_VERSION, ) logger = logging.getLogger() @@ -37,29 +28,26 @@ def parse(event, context, cache_layer): """Parse Lambda input to normalized events""" metadata = generate_metadata(context) - event_type = AwsEventType.UNKNOWN try: - # Route to the corresponding parser event_type = parse_event_type(event) if logger.isEnabledFor(logging.DEBUG): logger.debug(f"Parsed event type: {event_type}") + set_forwarder_telemetry_tags(context, event_type) match event_type: + case AwsEventType.AWSLOGS: + aws_handler = AwsLogsHandler(context, cache_layer) + events = aws_handler.handle(event) + return collect_and_count(events) case AwsEventType.S3: s3_handler = S3EventHandler(context, metadata, cache_layer) events = s3_handler.handle(event) - case AwsEventType.AWSLOGS: - aws_handler = AwsLogsHandler(context, cache_layer) - # regenerate a metadata object for each event - metadata = generate_metadata(context) - events = aws_handler.handle(event, metadata) case AwsEventType.EVENTS: events = cwevent_handler(event, metadata) case AwsEventType.SNS: events = sns_handler(event, metadata) case AwsEventType.KINESIS: events = kinesis_awslogs_handler(event, context, cache_layer) - case _: - events = ["Parsing: Unsupported event type"] + return collect_and_count(events) except Exception as e: # Logs through the socket the error err_message = "Error parsing the object. Exception: {} for event {}".format( @@ -67,21 +55,9 @@ def parse(event, context, cache_layer): ) events = [err_message] - set_forwarder_telemetry_tags(context, event_type) - return normalize_events(events, metadata) -def generate_custom_tags(context): - dd_custom_tags_data = { - FORWARDERNAME_STRING: context.function_name.lower(), - FORWARDERMEMSIZE_STRING: context.memory_limit_in_mb, - FORWARDERVERSION_STRING: DD_FORWARDER_VERSION, - } - - return dd_custom_tags_data - - def parse_event_type(event): if records := event.get(str(AwsEventTypeKeyword.RECORDS), None): record = records[0] @@ -138,36 +114,10 @@ def reformat_record(record): awslogs_handler = AwsLogsHandler(context, cache_layer) return itertools.chain.from_iterable( - awslogs_handler.handle(reformat_record(r), generate_metadata(context)) - for r in event["Records"] + awslogs_handler.handle(reformat_record(r)) for r in event["Records"] ) -def generate_metadata(context): - metadata = { - SOURCECATEGORY_STRING: AWS_STRING, - AWS_STRING: { - FUNCTIONVERSION_STRING: context.function_version, - INVOKEDFUNCTIONARN_STRING: context.invoked_function_arn, - }, - } - # Add custom tags here by adding new value with the following format "key1:value1, key2:value2" - might be subject to modifications - dd_custom_tags_data = generate_custom_tags(context) - metadata[DD_CUSTOM_TAGS] = ",".join( - filter( - None, - [ - DD_TAGS, - ",".join( - ["{}:{}".format(k, v) for k, v in dd_custom_tags_data.items()] - ), - ], - ) - ) - - return metadata - - def normalize_events(events, metadata): normalized = [] events_counter = 0 @@ -186,3 +136,15 @@ def normalize_events(events, metadata): send_event_metric("incoming_events", events_counter) return normalized + + +def collect_and_count(events): + collected = [] + counter = 0 + for event in events: + counter += 1 + collected.append(event) + + send_event_metric("incoming_events", counter) + + return collected diff --git a/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_lambda_log.approved.json b/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_lambda_log.approved.json new file mode 100644 index 000000000..364f68d20 --- /dev/null +++ b/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_lambda_log.approved.json @@ -0,0 +1,24 @@ +[ + { + "aws": { + "awslogs": { + "logGroup": "/aws/lambda/test-lambda-default-log-group", + "logStream": "2023/11/06/[$LATEST]b25b1f977b3e416faa45a00f427e7acb", + "owner": "123456789012" + }, + "function_version": 0, + "invoked_function_arn": "invoked_function_arn" + }, + "ddsource": "lambda", + "ddsourcecategory": "aws", + "ddtags": "forwardername:function_name,forwarder_memorysize:10,forwarder_version:4.0.1,env:none", + "host": "/aws/lambda/test-lambda-default-log-group", + "id": "37199773595581154154810589279545129148442535997644275712", + "lambda": { + "arn": "invoked_function_arnfunction:test-lambda-default-log-group" + }, + "message": "2021-01-02 03:04:05 UTC::@:[5306]:LOG: database system is ready to accept connections", + "service": "lambda", + "timestamp": 1668095539607 + } +] diff --git a/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_rds_postgresql.approved.json b/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_rds_postgresql.approved.json index 9e90266f0..048364859 100644 --- a/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_rds_postgresql.approved.json +++ b/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_rds_postgresql.approved.json @@ -5,10 +5,17 @@ "logGroup": "/aws/rds/instance/datadog/postgresql", "logStream": "datadog.0", "owner": "123456789012" - } + }, + "function_version": 0, + "invoked_function_arn": "invoked_function_arn" }, + "ddsource": "cloudwatch", + "ddsourcecategory": "aws", + "ddtags": "forwardername:function_name,forwarder_memorysize:10,forwarder_version:4.0.1,test_tag_key:test_tag_value", + "host": "/aws/rds/instance/datadog/postgresql", "id": "31953106606966983378809025079804211143289615424298221568", "message": "2021-01-02 03:04:05 UTC::@:[5306]:LOG: database system is ready to accept connections", + "service": "cloudwatch", "timestamp": 1609556645000 } ] diff --git a/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_step_functions_customized_log_group.approved.json b/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_step_functions_customized_log_group.approved.json index f99ff1453..ca8537ef8 100644 --- a/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_step_functions_customized_log_group.approved.json +++ b/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_step_functions_customized_log_group.approved.json @@ -5,10 +5,17 @@ "logGroup": "test/logs", "logStream": "states/logs-to-traces-sequential/2022-11-10-15-50/7851b2d9", "owner": "425362996713" - } + }, + "function_version": 0, + "invoked_function_arn": "invoked_function_arn" }, + "ddsource": "stepfunction", + "ddsourcecategory": "aws", + "ddtags": "forwardername:function_name,forwarder_memorysize:10,forwarder_version:4.0.1,test_tag_key:test_tag_value,dd_step_functions_trace_enabled:true", + "host": "arn:aws:states:us-east-1:12345678910:stateMachine:StepFunction2", "id": "37199773595581154154810589279545129148442535997644275712", "message": "{\"id\": \"1\",\"type\": \"ExecutionStarted\",\"details\": {\"input\": \"{}\",\"inputDetails\": {\"truncated\": \"false\"},\"roleArn\": \"arn:aws:iam::12345678910:role/service-role/StepFunctions-test-role-a0iurr4pt\"},\"previous_event_id\": \"0\",\"event_timestamp\": \"1716992192441\",\"execution_arn\": \"arn:aws:states:us-east-1:12345678910:execution:StepFunction2:ccccccc-d1da-4c38-b32c-2b6b07d713fa\",\"redrive_count\": \"0\"}", + "service": "stepfunction", "timestamp": 1668095539607 } ] diff --git a/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_step_functions_tags_added_properly.approved.json b/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_step_functions_tags_added_properly.approved.json index 2dd05043b..614faddb8 100644 --- a/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_step_functions_tags_added_properly.approved.json +++ b/aws/logs_monitoring/tests/approved_files/TestAWSLogsHandler.test_awslogs_handler_step_functions_tags_added_properly.approved.json @@ -5,10 +5,17 @@ "logGroup": "/aws/vendedlogs/states/logs-to-traces-sequential-Logs", "logStream": "states/logs-to-traces-sequential/2022-11-10-15-50/7851b2d9", "owner": "425362996713" - } + }, + "function_version": 0, + "invoked_function_arn": "invoked_function_arn" }, + "ddsource": "stepfunction", + "ddsourcecategory": "aws", + "ddtags": "forwardername:function_name,forwarder_memorysize:10,forwarder_version:4.0.1,test_tag_key:test_tag_value,dd_step_functions_trace_enabled:true", + "host": "arn:aws:states:us-east-1:12345678910:stateMachine:StepFunction1", "id": "37199773595581154154810589279545129148442535997644275712", "message": "{\"id\": \"1\",\"type\": \"ExecutionStarted\",\"details\": {\"input\": \"{}\",\"inputDetails\": {\"truncated\": \"false\"},\"roleArn\": \"arn:aws:iam::12345678910:role/service-role/StepFunctions-test-role-a0iurr4pt\"},\"previous_event_id\": \"0\",\"event_timestamp\": \"1716992192441\",\"execution_arn\": \"arn:aws:states:us-east-1:12345678910:execution:StepFunction1:ccccccc-d1da-4c38-b32c-2b6b07d713fa\",\"redrive_count\": \"0\"}", + "service": "stepfunction", "timestamp": 1668095539607 } ] diff --git a/aws/logs_monitoring/tests/approved_files/TestLambdaFunctionEndToEnd.test_datadog_forwarder.approved.json b/aws/logs_monitoring/tests/approved_files/TestLambdaFunctionEndToEnd.test_datadog_forwarder.approved.json index 1f510b19b..f7a1f5fd5 100644 --- a/aws/logs_monitoring/tests/approved_files/TestLambdaFunctionEndToEnd.test_datadog_forwarder.approved.json +++ b/aws/logs_monitoring/tests/approved_files/TestLambdaFunctionEndToEnd.test_datadog_forwarder.approved.json @@ -11,7 +11,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576111948622874033876462979853992919938886093242368", "lambda": { @@ -33,7 +33,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576111948622874033876462979853992919938886093242369", "lambda": { @@ -55,7 +55,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576111948622874033876462979853992919938886093242370", "lambda": { @@ -77,7 +77,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576111948622874033876462979853992919938886093242371", "lambda": { @@ -99,7 +99,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576111948622874033876462979853992919938886093242372", "lambda": { @@ -121,7 +121,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576112305434797210366433244425485282312670188929029", "lambda": { @@ -143,7 +143,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576113197464605151591358905854216188247130428145670", "lambda": { @@ -165,7 +165,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576113197464605151591358905854216188247130428145671", "lambda": { @@ -187,7 +187,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576113197464605151591358905854216188247130428145672", "lambda": { @@ -209,7 +209,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576113197464605151591358905854216188247130428145673", "lambda": { @@ -231,7 +231,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576113197464605151591358905854216188247130428145674", "lambda": { @@ -253,7 +253,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576113643479509122203821736568581641214360547753995", "lambda": { @@ -275,7 +275,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576113643479509122203821736568581641214360547753996", "lambda": { @@ -297,7 +297,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576113643479509122203821736568581641214360547753997", "lambda": { @@ -319,7 +319,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576123455807396475678004012284621606493423179137038", "lambda": { @@ -341,7 +341,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576123478108141674208627153820339879141784685117455", "lambda": { @@ -363,7 +363,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576125685881916328740318165856448871329573777178640", "lambda": { @@ -385,7 +385,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576126131896820299352780996570814324296803896786961", "lambda": { @@ -407,7 +407,7 @@ }, "ddsource": "lambda", "ddsourcecategory": "aws", - "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,account_id:601427279990,aws_account:601427279990,creator:swf,env:prod,functionname:hello-dog-node-dev-hello12x,monitor:datadog,region:sa-east-1,service:hello,team:metrics", "host": "arn:aws:lambda:sa-east-1:601427279990:function:hello-dog-node-dev-hello12x", "id": "35311576126131896820299352780996570814324296803896786962", "lambda": { diff --git a/aws/logs_monitoring/tests/approved_files/TestLambdaFunctionEndToEnd.test_kinesis_awslogs_handler.approved.json b/aws/logs_monitoring/tests/approved_files/TestLambdaFunctionEndToEnd.test_kinesis_awslogs_handler.approved.json new file mode 100644 index 000000000..57717354b --- /dev/null +++ b/aws/logs_monitoring/tests/approved_files/TestLambdaFunctionEndToEnd.test_kinesis_awslogs_handler.approved.json @@ -0,0 +1,43 @@ +[ + { + "aws": { + "awslogs": { + "logGroup": "/aws/lambda/testing-datadog", + "logStream": "2024/10/10/[$LATEST]20bddfd5a2dc4c6b97ac02800eae90d0", + "owner": "601427279990" + }, + "function_version": 0, + "invoked_function_arn": "arn:aws:lambda:sa-east-1:601427279990:function:inferred-spans-python-dev-initsender" + }, + "ddsource": "lambda", + "ddsourcecategory": "aws", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,env:none,functionname:testing-datadog,env:test,service:test-inner-message", + "host": "arn:aws:lambda:sa-east-1:601427279990:function:testing-datadog", + "id": "35311576111948622874033876462979853992919938886093242368", + "lambda": { + "arn": "arn:aws:lambda:sa-east-1:601427279990:function:testing-datadog" + }, + "message": "{\"status\": \"debug\", \"message\": \"datadog:Patched console output with trace context\"}", + "service": "test-inner-message", + "timestamp": 1583425836114 + }, + { + "aws": { + "awslogs": { + "logGroup": "/aws/rds/cluster", + "logStream": "2025/01/01/[$LATEST]3b1b2b3b4b5b6b7b8b9b0", + "owner": "123456789012" + }, + "function_version": 0, + "invoked_function_arn": "arn:aws:lambda:sa-east-1:601427279990:function:inferred-spans-python-dev-initsender" + }, + "ddsource": "cloudwatch", + "ddsourcecategory": "aws", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,my:tag", + "host": "/aws/rds/cluster", + "id": "45311576111948622874033876462979853992919938886093242368", + "message": "{\"status\": \"info\", \"message\": \"datadog:This is a custom message\"}", + "service": "cloudwatch", + "timestamp": 1737735732 + } +] diff --git a/aws/logs_monitoring/tests/approved_files/TestLambdaFunctionEndToEnd.test_kinesis_awslogs_handler.received.json b/aws/logs_monitoring/tests/approved_files/TestLambdaFunctionEndToEnd.test_kinesis_awslogs_handler.received.json new file mode 100644 index 000000000..57717354b --- /dev/null +++ b/aws/logs_monitoring/tests/approved_files/TestLambdaFunctionEndToEnd.test_kinesis_awslogs_handler.received.json @@ -0,0 +1,43 @@ +[ + { + "aws": { + "awslogs": { + "logGroup": "/aws/lambda/testing-datadog", + "logStream": "2024/10/10/[$LATEST]20bddfd5a2dc4c6b97ac02800eae90d0", + "owner": "601427279990" + }, + "function_version": 0, + "invoked_function_arn": "arn:aws:lambda:sa-east-1:601427279990:function:inferred-spans-python-dev-initsender" + }, + "ddsource": "lambda", + "ddsourcecategory": "aws", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,env:none,functionname:testing-datadog,env:test,service:test-inner-message", + "host": "arn:aws:lambda:sa-east-1:601427279990:function:testing-datadog", + "id": "35311576111948622874033876462979853992919938886093242368", + "lambda": { + "arn": "arn:aws:lambda:sa-east-1:601427279990:function:testing-datadog" + }, + "message": "{\"status\": \"debug\", \"message\": \"datadog:Patched console output with trace context\"}", + "service": "test-inner-message", + "timestamp": 1583425836114 + }, + { + "aws": { + "awslogs": { + "logGroup": "/aws/rds/cluster", + "logStream": "2025/01/01/[$LATEST]3b1b2b3b4b5b6b7b8b9b0", + "owner": "123456789012" + }, + "function_version": 0, + "invoked_function_arn": "arn:aws:lambda:sa-east-1:601427279990:function:inferred-spans-python-dev-initsender" + }, + "ddsource": "cloudwatch", + "ddsourcecategory": "aws", + "ddtags": "forwardername:inferred-spans-python-dev-initsender,forwarder_memorysize:10,forwarder_version:,my:tag", + "host": "/aws/rds/cluster", + "id": "45311576111948622874033876462979853992919938886093242368", + "message": "{\"status\": \"info\", \"message\": \"datadog:This is a custom message\"}", + "service": "cloudwatch", + "timestamp": 1737735732 + } +] diff --git a/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_bad_arn.approved.json b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_bad_arn.approved.json new file mode 100644 index 000000000..da111f3d9 --- /dev/null +++ b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_bad_arn.approved.json @@ -0,0 +1,5 @@ +{ + "lambda": { + "arn": "bad_arn" + } +} diff --git a/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_custom_tags_env.approved.json b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_custom_tags_env.approved.json new file mode 100644 index 000000000..9f25b1cc2 --- /dev/null +++ b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_custom_tags_env.approved.json @@ -0,0 +1,8 @@ +{ + "ddtags": "account_id:123456789012,aws_account:123456789012,env:customtags_env,functionname:my-function,region:us-east-1,service:my-function", + "host": "arn:aws:lambda:us-east-1:123456789012:function:my-function", + "lambda": { + "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function" + }, + "service": "my-function" +} diff --git a/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_custom_tags_w_service.approved.json b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_custom_tags_w_service.approved.json new file mode 100644 index 000000000..0945d9a37 --- /dev/null +++ b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_custom_tags_w_service.approved.json @@ -0,0 +1,8 @@ +{ + "ddtags": "account_id:123456789012,aws_account:123456789012,functionname:my-function,region:us-east-1", + "host": "arn:aws:lambda:us-east-1:123456789012:function:my-function", + "lambda": { + "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function" + }, + "service": "my_service" +} diff --git a/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_custom_tags_wo_service.approved.json b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_custom_tags_wo_service.approved.json new file mode 100644 index 000000000..0f2b09f2b --- /dev/null +++ b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_custom_tags_wo_service.approved.json @@ -0,0 +1,8 @@ +{ + "ddtags": "account_id:123456789012,aws_account:123456789012,functionname:my-function,region:us-east-1,service:customtags_service", + "host": "arn:aws:lambda:us-east-1:123456789012:function:my-function", + "lambda": { + "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function" + }, + "service": "customtags_service" +} diff --git a/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_service.approved.json b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_service.approved.json new file mode 100644 index 000000000..0945d9a37 --- /dev/null +++ b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_service.approved.json @@ -0,0 +1,8 @@ +{ + "ddtags": "account_id:123456789012,aws_account:123456789012,functionname:my-function,region:us-east-1", + "host": "arn:aws:lambda:us-east-1:123456789012:function:my-function", + "lambda": { + "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function" + }, + "service": "my_service" +} diff --git a/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_service_and_ddtags.approved.json b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_service_and_ddtags.approved.json new file mode 100644 index 000000000..9c7923475 --- /dev/null +++ b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_w_service_and_ddtags.approved.json @@ -0,0 +1,8 @@ +{ + "ddtags": "service:ddtags_service,account_id:123456789012,aws_account:123456789012,functionname:my-function,region:us-east-1", + "host": "arn:aws:lambda:us-east-1:123456789012:function:my-function", + "lambda": { + "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function" + }, + "service": "my_service" +} diff --git a/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_wo_service.approved.json b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_wo_service.approved.json new file mode 100644 index 000000000..0ae1d7e9e --- /dev/null +++ b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_lambda_event_wo_service.approved.json @@ -0,0 +1,8 @@ +{ + "ddtags": "account_id:123456789012,aws_account:123456789012,functionname:my-function,region:us-east-1,service:my-function", + "host": "arn:aws:lambda:us-east-1:123456789012:function:my-function", + "lambda": { + "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function" + }, + "service": "my-function" +} diff --git a/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_non_lambda_event.approved.json b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_non_lambda_event.approved.json new file mode 100644 index 000000000..76d47699f --- /dev/null +++ b/aws/logs_monitoring/tests/approved_files/TestLambdaMetadataEnrichment.test_non_lambda_event.approved.json @@ -0,0 +1,3 @@ +{ + "lambda": {} +} diff --git a/aws/logs_monitoring/tests/events/cloudwatch_logs_2.json b/aws/logs_monitoring/tests/events/cloudwatch_logs_2.json new file mode 100644 index 000000000..46d74a169 --- /dev/null +++ b/aws/logs_monitoring/tests/events/cloudwatch_logs_2.json @@ -0,0 +1,16 @@ +{ + "messageType": "DATA_MESSAGE", + "owner": "123456789012", + "logGroup": "/aws/rds/cluster", + "logStream": "2025/01/01/[$LATEST]3b1b2b3b4b5b6b7b8b9b0", + "subscriptionFilters": [ + "testing-datadog" + ], + "logEvents": [ + { + "id": "45311576111948622874033876462979853992919938886093242368", + "timestamp": 1737735732, + "message": "{\"status\":\"info\",\"message\":\"datadog:This is a custom message\",\"ddtags\":\"my:tag\"}\n" + } + ] +} diff --git a/aws/logs_monitoring/tests/test_awslogs_handler.py b/aws/logs_monitoring/tests/test_awslogs_handler.py index c1c77c425..ea0bb2c1f 100644 --- a/aws/logs_monitoring/tests/test_awslogs_handler.py +++ b/aws/logs_monitoring/tests/test_awslogs_handler.py @@ -6,9 +6,6 @@ import sys from unittest.mock import patch, MagicMock from approvaltests.approvals import verify_as_json -from approvaltests.namer import NamerFactory - -from steps.enums import AwsEventSource sys.modules["trace_forwarder.connection"] = MagicMock() sys.modules["datadog_lambda.wrapper"] = MagicMock() @@ -24,7 +21,6 @@ }, ) env_patch.start() -from settings import DD_HOST, DD_SOURCE from steps.handlers.awslogs_handler import AwsLogsHandler from steps.handlers.aws_attributes import AwsAttributes from caching.cache_layer import CacheLayer @@ -32,6 +28,13 @@ env_patch.stop() +class Context: + function_version = 0 + invoked_function_arn = "invoked_function_arn" + function_name = "function_name" + memory_limit_in_mb = "10" + + class TestAWSLogsHandler(unittest.TestCase): @patch("caching.cloudwatch_log_group_cache.CloudwatchLogGroupTagsCache.__init__") def test_awslogs_handler_rds_postgresql(self, mock_cache_init): @@ -60,8 +63,7 @@ def test_awslogs_handler_rds_postgresql(self, mock_cache_init): ) } } - context = None - metadata = {"ddsource": "cloudwatch", "ddtags": "env:dev"} + context = Context() mock_cache_init.return_value = None cache_layer = CacheLayer("") cache_layer._cloudwatch_log_group_cache.get = MagicMock( @@ -69,8 +71,7 @@ def test_awslogs_handler_rds_postgresql(self, mock_cache_init): ) awslogs_handler = AwsLogsHandler(context, cache_layer) - verify_as_json(list(awslogs_handler.handle(event, metadata))) - verify_as_json(metadata, options=NamerFactory.with_parameters("metadata")) + verify_as_json(list(awslogs_handler.handle(event))) @patch("caching.cloudwatch_log_group_cache.CloudwatchLogGroupTagsCache.__init__") @patch("caching.cloudwatch_log_group_cache.send_forwarder_internal_metrics") @@ -107,8 +108,7 @@ def test_awslogs_handler_step_functions_tags_added_properly( ) } } - context = None - metadata = {"ddsource": "postgresql", "ddtags": "env:dev"} + context = Context() mock_forward_metrics.side_effect = MagicMock() mock_cache_init.return_value = None cache_layer = CacheLayer("") @@ -118,14 +118,7 @@ def test_awslogs_handler_step_functions_tags_added_properly( cache_layer._cloudwatch_log_group_cache.get = MagicMock() awslogs_handler = AwsLogsHandler(context, cache_layer) - verify_as_json(list(awslogs_handler.handle(event, metadata))) - verify_as_json(metadata, options=NamerFactory.with_parameters("metadata")) - # verify that the handling can properly handle SF logs with the default log group naming - self.assertEqual(metadata[DD_SOURCE], AwsEventSource.STEPFUNCTION.value) - self.assertEqual( - metadata[DD_HOST], - "arn:aws:states:us-east-1:12345678910:stateMachine:StepFunction1", - ) + verify_as_json(list(awslogs_handler.handle(event))) @patch("caching.cloudwatch_log_group_cache.CloudwatchLogGroupTagsCache.__init__") @patch("caching.cloudwatch_log_group_cache.send_forwarder_internal_metrics") @@ -163,8 +156,7 @@ def test_awslogs_handler_step_functions_customized_log_group( ) } } - context = None - metadata = {"ddtags": "env:dev"} + context = Context() mock_forward_metrics.side_effect = MagicMock() mock_cache_init.return_value = None cache_layer = CacheLayer("") @@ -175,16 +167,46 @@ def test_awslogs_handler_step_functions_customized_log_group( awslogs_handler = AwsLogsHandler(context, cache_layer) # for some reasons, the below two are needed to update the context of the handler - verify_as_json( - list(awslogs_handler.handle(eventFromCustomizedLogGroup, metadata)) - ) - verify_as_json(metadata, options=NamerFactory.with_parameters("metadata")) - self.assertEqual(metadata[DD_SOURCE], AwsEventSource.STEPFUNCTION.value) - self.assertEqual( - metadata[DD_HOST], - "arn:aws:states:us-east-1:12345678910:stateMachine:StepFunction2", + verify_as_json(list(awslogs_handler.handle(eventFromCustomizedLogGroup))) + + def test_awslogs_handler_lambda_log(self): + event = { + "awslogs": { + "data": base64.b64encode( + gzip.compress( + bytes( + json.dumps( + { + "messageType": "DATA_MESSAGE", + "owner": "123456789012", + "logGroup": "/aws/lambda/test-lambda-default-log-group", + "logStream": "2023/11/06/[$LATEST]b25b1f977b3e416faa45a00f427e7acb", + "subscriptionFilters": ["testFilter"], + "logEvents": [ + { + "id": "37199773595581154154810589279545129148442535997644275712", + "timestamp": 1668095539607, + "message": "2021-01-02 03:04:05 UTC::@:[5306]:LOG: database system is ready to accept connections", + } + ], + } + ), + "utf-8", + ) + ) + ) + } + } + context = Context() + cache_layer = CacheLayer("") + cache_layer._cloudwatch_log_group_cache.get = MagicMock() + cache_layer._lambda_cache.get = MagicMock( + return_value=["service:customtags_service"] ) + awslogs_handler = AwsLogsHandler(context, cache_layer) + verify_as_json(list(awslogs_handler.handle(event))) + def test_process_lambda_logs(self): # Non Lambda log stepfunction_loggroup = { @@ -199,7 +221,7 @@ def test_process_lambda_logs(self): stepfunction_loggroup.get("logStream"), stepfunction_loggroup.get("owner"), ) - context = None + context = Context() aws_handler = AwsLogsHandler(context, CacheLayer("")) aws_handler.process_lambda_logs(metadata, aws_attributes) @@ -218,8 +240,7 @@ def test_process_lambda_logs(self): lambda_default_loggroup.get("logStream"), lambda_default_loggroup.get("owner"), ) - context = MagicMock() - context.invoked_function_arn = "arn:aws:lambda:sa-east-1:601427279990:function:inferred-spans-python-dev-initsender" + context = Context() aws_handler = AwsLogsHandler(context, CacheLayer("")) aws_handler.process_lambda_logs(metadata, aws_attributes) @@ -232,7 +253,7 @@ def test_process_lambda_logs(self): ) self.assertEqual( aws_attributes.to_dict().get("lambda", None).get("arn", None), - "arn:aws:lambda:sa-east-1:601427279990:function:test-lambda-default-log-group", + "invoked_function_arnfunction:test-lambda-default-log-group", ) # env not set diff --git a/aws/logs_monitoring/tests/test_enrichment.py b/aws/logs_monitoring/tests/test_enrichment.py index 79506862e..9544e257a 100644 --- a/aws/logs_monitoring/tests/test_enrichment.py +++ b/aws/logs_monitoring/tests/test_enrichment.py @@ -1,6 +1,12 @@ -import unittest import json + +import unittest +from unittest.mock import MagicMock + +from caching.cache_layer import CacheLayer +from approvaltests.approvals import verify_as_json from steps.enrichment import ( + add_metadata_to_lambda_log, extract_host_from_cloudtrails, extract_host_from_guardduty, extract_host_from_route53, @@ -153,5 +159,99 @@ def test_parse_source_route53(self): self.assertEqual(event["host"], "i-99999999") +class TestLambdaMetadataEnrichment(unittest.TestCase): + def test_empty_event(self): + cache_layer = CacheLayer("") + event = {} + add_metadata_to_lambda_log(event, cache_layer) + + self.assertEqual(event, {}) + + def test_non_lambda_event(self): + cache_layer = CacheLayer("") + event = {"lambda": {}} + add_metadata_to_lambda_log(event, cache_layer) + + verify_as_json(event) + + def test_lambda_event_bad_arn(self): + cache_layer = CacheLayer("") + event = {"lambda": {"arn": "bad_arn"}} + add_metadata_to_lambda_log(event, cache_layer) + verify_as_json(event) + + def test_lambda_event_wo_service(self): + cache_layer = CacheLayer("") + event = { + "lambda": { + "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function" + } + } + add_metadata_to_lambda_log(event, cache_layer) + verify_as_json(event) + + def test_lambda_event_w_custom_tags_wo_service(self): + cache_layer = CacheLayer("") + cache_layer._lambda_cache.get = MagicMock( + return_value=["service:customtags_service"] + ) + event = { + "lambda": { + "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function" + } + } + add_metadata_to_lambda_log(event, cache_layer) + verify_as_json(event) + + def test_lambda_event_w_custom_tags_w_service(self): + cache_layer = CacheLayer("") + cache_layer._lambda_cache.get = MagicMock( + return_value=["service:customtags_service"] + ) + event = { + "lambda": { + "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function" + }, + "service": "my_service", + } + add_metadata_to_lambda_log(event, cache_layer) + verify_as_json(event) + + def test_lambda_event_w_service(self): + cache_layer = CacheLayer("") + event = { + "lambda": { + "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function" + }, + "service": "my_service", + } + add_metadata_to_lambda_log(event, cache_layer) + verify_as_json(event) + + def test_lambda_event_w_service_and_ddtags(self): + cache_layer = CacheLayer("") + event = { + "lambda": { + "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function" + }, + "service": "my_service", + "ddtags": "service:ddtags_service", + } + add_metadata_to_lambda_log(event, cache_layer) + verify_as_json(event) + + def test_lambda_event_w_custom_tags_env(self): + cache_layer = CacheLayer("") + cache_layer._lambda_cache.get = MagicMock(return_value=["env:customtags_env"]) + event = { + "lambda": { + "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function" + }, + "ddtags": "env:none", + } + add_metadata_to_lambda_log(event, cache_layer) + verify_as_json(event) + + if __name__ == "__main__": unittest.main() diff --git a/aws/logs_monitoring/tests/test_lambda_function.py b/aws/logs_monitoring/tests/test_lambda_function.py index 5d88c4b40..1b11d6a9c 100644 --- a/aws/logs_monitoring/tests/test_lambda_function.py +++ b/aws/logs_monitoring/tests/test_lambda_function.py @@ -88,9 +88,7 @@ def test_datadog_forwarder(self, mock_cache_init): context = Context() input_data = self._get_input_data() - event = { - "awslogs": {"data": self._create_cloudwatch_log_event_from_data(input_data)} - } + event = {"awslogs": {"data": self._create_log_event_from_data(input_data)}} event_type = parse_event_type(event) self.assertEqual(event_type, AwsEventType.AWSLOGS) @@ -100,7 +98,7 @@ def test_datadog_forwarder(self, mock_cache_init): transformed_events = transform(enriched_events) scrubber = create_regex_scrubber( - "forwarder_version:\d+\.\d+\.\d+", + r"forwarder_version:\d+\.\d+\.\d+", "forwarder_version:", ) verify_as_json(transformed_events, options=Options().with_scrubber(scrubber)) @@ -141,6 +139,7 @@ def test_datadog_forwarder(self, mock_cache_init): def test_setting_service_tag_from_log_group_cache(self, mock_cache_init): reload(sys.modules["settings"]) reload(sys.modules["steps.parsing"]) + reload(sys.modules["steps.common"]) mock_cache_init.return_value = None cache_layer = CacheLayer("") cache_layer._cloudwatch_log_group_cache.get = MagicMock( @@ -148,9 +147,7 @@ def test_setting_service_tag_from_log_group_cache(self, mock_cache_init): ) context = Context() input_data = self._get_input_data() - event = { - "awslogs": {"data": self._create_cloudwatch_log_event_from_data(input_data)} - } + event = {"awslogs": {"data": self._create_log_event_from_data(input_data)}} normalized_events = parse(event, context, cache_layer) enriched_events = enrich(normalized_events, cache_layer) @@ -166,6 +163,7 @@ def test_setting_service_tag_from_log_group_cache(self, mock_cache_init): def test_service_override_from_dd_tags(self, mock_cache_init): reload(sys.modules["settings"]) reload(sys.modules["steps.parsing"]) + reload(sys.modules["steps.common"]) mock_cache_init.return_value = None cache_layer = CacheLayer("") cache_layer._cloudwatch_log_group_cache.get = MagicMock( @@ -173,9 +171,7 @@ def test_service_override_from_dd_tags(self, mock_cache_init): ) context = Context() input_data = self._get_input_data() - event = { - "awslogs": {"data": self._create_cloudwatch_log_event_from_data(input_data)} - } + event = {"awslogs": {"data": self._create_log_event_from_data(input_data)}} normalized_events = parse(event, context, cache_layer) enriched_events = enrich(normalized_events, cache_layer) @@ -205,9 +201,7 @@ def test_overrding_service_tag_from_lambda_cache( cache_layer._cloudwatch_log_group_cache = MagicMock() context = Context() input_data = self._get_input_data() - event = { - "awslogs": {"data": self._create_cloudwatch_log_event_from_data(input_data)} - } + event = {"awslogs": {"data": self._create_log_event_from_data(input_data)}} normalized_events = parse(event, context, cache_layer) enriched_events = enrich(normalized_events, cache_layer) @@ -230,9 +224,7 @@ def test_overrding_service_tag_from_lambda_cache_when_dd_tags_is_set( cache_layer._cloudwatch_log_group_cache = MagicMock() context = Context() input_data = self._get_input_data() - event = { - "awslogs": {"data": self._create_cloudwatch_log_event_from_data(input_data)} - } + event = {"awslogs": {"data": self._create_log_event_from_data(input_data)}} normalized_events = parse(event, context, cache_layer) enriched_events = enrich(normalized_events, cache_layer) transformed_events = transform(enriched_events) @@ -250,9 +242,7 @@ def test_overrding_service_tag_from_message_ddtags(self, mock_cache_init): cache_layer._cloudwatch_log_group_cache = MagicMock() context = Context() input_data = self._get_input_data(path="events/cloudwatch_logs_ddtags.json") - event = { - "awslogs": {"data": self._create_cloudwatch_log_event_from_data(input_data)} - } + event = {"awslogs": {"data": self._create_log_event_from_data(input_data)}} normalized_events = parse(event, context, cache_layer) enriched_events = enrich(normalized_events, cache_layer) transformed_events = transform(enriched_events) @@ -263,10 +253,35 @@ def test_overrding_service_tag_from_message_ddtags(self, mock_cache_init): self.assertEqual(log["service"], "test-inner-message") self.assertTrue(isinstance(log["message"], str)) + @patch("caching.cloudwatch_log_group_cache.CloudwatchLogGroupTagsCache.__init__") + def test_kinesis_awslogs_handler(self, mock_cache_init): + mock_cache_init.return_value = None + cache_layer = CacheLayer("") + cache_layer._lambda_cache = MagicMock() + cache_layer._cloudwatch_log_group_cache = MagicMock() + context = Context() + input_data1 = self._get_input_data(path="events/cloudwatch_logs_ddtags.json") + input_data2 = self._get_input_data(path="events/cloudwatch_logs_2.json") + event = { + "Records": [ + {"kinesis": {"data": self._create_log_event_from_data(input_data1)}}, + {"kinesis": {"data": self._create_log_event_from_data(input_data2)}}, + ] + } + + normalized_events = parse(event, context, cache_layer) + enriched_events = enrich(normalized_events, cache_layer) + transformed_events = transform(enriched_events) + + scrubber = create_regex_scrubber( + r"forwarder_version:\d+\.\d+\.\d+", + "forwarder_version:", + ) + verify_as_json(transformed_events, options=Options().with_scrubber(scrubber)) + def _get_input_data(self, path="events/cloudwatch_logs.json"): my_path = os.path.abspath(os.path.dirname(__file__)) path = os.path.join(my_path, path) - with open( path, "r", @@ -275,7 +290,7 @@ def _get_input_data(self, path="events/cloudwatch_logs.json"): return input_data - def _create_cloudwatch_log_event_from_data(self, data): + def _create_log_event_from_data(self, data): # CloudWatch log event data is a base64-encoded ZIP archive # see https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchlogs.html gzipped_data = gzip.compress(bytes(data, encoding="utf-8")) diff --git a/aws/logs_monitoring/tests/test_s3_handler.py b/aws/logs_monitoring/tests/test_s3_handler.py index 9ef615e1c..e2bd0f7cf 100644 --- a/aws/logs_monitoring/tests/test_s3_handler.py +++ b/aws/logs_monitoring/tests/test_s3_handler.py @@ -122,9 +122,11 @@ def test_s3_handler_with_multiline_regex(self): } data = "2022-02-08aaa\nbbbccc\n2022-02-09bbb\n2022-02-10ccc\n" self.s3_handler.data_store.data = data.encode("utf-8") - self.s3_handler.multiline_regex_start_pattern = re.compile("^\d{4}-\d{2}-\d{2}") + self.s3_handler.multiline_regex_start_pattern = re.compile( + r"^\d{4}-\d{2}-\d{2}" + ) self.s3_handler.multiline_regex_pattern = re.compile( - "[\n\r\f]+(?=\d{4}-\d{2}-\d{2})" + r"[\n\r\f]+(?=\d{4}-\d{2}-\d{2})" ) self.s3_handler._extract_data = MagicMock() structured_lines = list(self.s3_handler.handle(event))