Skip to content

Commit

Permalink
fix(aws): limit api params as span tags [backport DataDog#4781 to 1.6] (
Browse files Browse the repository at this point in the history
  • Loading branch information
majorgreys committed Dec 19, 2022
1 parent c5c5bec commit 0cad73a
Show file tree
Hide file tree
Showing 12 changed files with 1,052 additions and 622 deletions.
44 changes: 33 additions & 11 deletions ddtrace/contrib/aiobotocore/__init__.py
@@ -1,22 +1,44 @@
"""
The aiobotocore integration will trace all AWS calls made with the ``aiobotocore``
library. This integration isn't enabled when applying the default patching.
To enable it, you must run ``patch_all(aiobotocore=True)``
library. This integration is not enabled by default.
::
Enabling
~~~~~~~~
import aiobotocore.session
from ddtrace import patch
The aiobotocore integration is not enabled by default. Use
:func:`patch()<ddtrace.patch>` to enable the integration::
# If not patched yet, you can patch botocore specifically
from ddtrace import patch
patch(aiobotocore=True)
# This will report spans with the default instrumentation
aiobotocore.session.get_session()
lambda_client = session.create_client('lambda', region_name='us-east-1')
Configuration
~~~~~~~~~~~~~
.. py:data:: ddtrace.config.aiobotocore['tag_no_params']
This opts out of the default behavior of adding span tags for a narrow set of API parameters.
To not collect any API parameters, ``ddtrace.config.aiobotocore.tag_no_params = True`` or by setting the environment
variable ``DD_AWS_TAG_NO_PARAMS=true``.
Default: ``False``
.. py:data:: ddtrace.config.aiobotocore['tag_all_params']
**Deprecated**: This retains the deprecated behavior of adding span tags for
all API parameters that are not explicitly excluded by the integration.
These deprecated span tags will be added along with the API parameters
enabled by default.
This configuration is ignored if ``tag_no_parms`` (``DD_AWS_TAG_NO_PARAMS``)
is set to ``True``.
To collect all API parameters, ``ddtrace.config.botocore.tag_all_params =
True`` or by setting the environment variable ``DD_AWS_TAG_ALL_PARAMS=true``.
# This query generates a trace
lambda_client.list_functions()
Default: ``False``
"""
from ...internal.utils.importlib import require_modules

Expand Down
29 changes: 28 additions & 1 deletion ddtrace/contrib/aiobotocore/patch.py
@@ -1,7 +1,10 @@
import os

import aiobotocore.client

from ddtrace import config
from ddtrace.internal.utils.version import parse_version
from ddtrace.vendor import debtcollector
from ddtrace.vendor import wrapt

from ...constants import ANALYTICS_SAMPLE_RATE_KEY
Expand All @@ -12,6 +15,7 @@
from ...internal.compat import PYTHON_VERSION_INFO
from ...internal.utils import ArgumentError
from ...internal.utils import get_argument_value
from ...internal.utils.formats import asbool
from ...internal.utils.formats import deep_getattr
from ...pin import Pin
from ..trace_utils import unwrap
Expand All @@ -31,6 +35,22 @@
TRACED_ARGS = {"params", "path", "verb"}


if os.getenv("DD_AWS_TAG_ALL_PARAMS") is not None:
debtcollector.deprecate(
"Using environment variable 'DD_AWS_TAG_ALL_PARAMS' is deprecated",
message="The aiobotocore integration no longer includes all API parameters by default.",
removal_version="2.0.0",
)

config._add(
"aiobotocore",
{
"tag_no_params": asbool(os.getenv("DD_AWS_TAG_NO_PARAMS", default=False)),
"tag_all_params": asbool(os.getenv("DD_AWS_TAG_ALL_PARAMS", default=False)),
},
)


def patch():
if getattr(aiobotocore.client, "_datadog_patch", False):
return
Expand Down Expand Up @@ -94,13 +114,20 @@ async def _wrapped_api_call(original_func, instance, args, kwargs):
span.set_tag(SPAN_MEASURED_KEY)

try:

operation = get_argument_value(args, kwargs, 0, "operation_name")
params = get_argument_value(args, kwargs, 1, "params")

span.resource = "{}.{}".format(endpoint_name, operation.lower())

if params and not config.aiobotocore["tag_no_params"]:
aws._add_api_param_span_tags(span, endpoint_name, params)
except ArgumentError:
operation = None
span.resource = endpoint_name

aws.add_span_arg_tags(span, endpoint_name, args, ARGS_NAME, TRACED_ARGS)
if not config.aiobotocore["tag_no_params"] and config.aiobotocore["tag_all_params"]:
aws.add_span_arg_tags(span, endpoint_name, args, ARGS_NAME, TRACED_ARGS)

region_name = deep_getattr(instance, "meta.region_name")

Expand Down
46 changes: 38 additions & 8 deletions ddtrace/contrib/boto/__init__.py
@@ -1,17 +1,47 @@
"""
Boto integration will trace all AWS calls made via boto2.
This integration is automatically patched when using ``patch_all()``::
import boto.ec2
from ddtrace import patch
Enabling
~~~~~~~~
The boto integration is enabled automatically when using
:ref:`ddtrace-run<ddtracerun>` or :func:`patch_all()<ddtrace.patch_all>`.
# If not patched yet, you can patch boto specifically
Or use :func:`patch()<ddtrace.patch>` to manually enable the integration::
from ddtrace import patch
patch(boto=True)
# This will report spans with the default instrumentation
ec2 = boto.ec2.connect_to_region("us-west-2")
# Example of instrumented query
ec2.get_all_instances()
Configuration
~~~~~~~~~~~~~
.. py:data:: ddtrace.config.boto['tag_no_params']
This opts out of the default behavior of collecting a narrow set of API
parameters as span tags.
To not collect any API parameters, ``ddtrace.config.boto.tag_no_params =
True`` or by setting the environment variable ``DD_AWS_TAG_NO_PARAMS=true``.
Default: ``False``
.. py:data:: ddtrace.config.boto['tag_all_params']
**Deprecated**: This retains the deprecated behavior of adding span tags for
all API parameters that are not explicitly excluded by the integration.
These deprecated span tags will be added along with the API parameters
enabled by default.
This configuration is ignored if ``tag_no_parms`` (``DD_AWS_TAG_NO_PARAMS``)
is set to ``True``.
To collect all API parameters, ``ddtrace.config.botocore.tag_all_params =
True`` or by setting the environment variable ``DD_AWS_TAG_ALL_PARAMS=true``.
Default: ``False``
"""

from ...internal.utils.importlib import require_modules
Expand Down
30 changes: 28 additions & 2 deletions ddtrace/contrib/boto/patch.py
@@ -1,4 +1,5 @@
import inspect
import os

import boto.connection

Expand All @@ -10,9 +11,11 @@
from ddtrace.ext import http
from ddtrace.internal.utils.wrappers import unwrap
from ddtrace.pin import Pin
from ddtrace.vendor import debtcollector
from ddtrace.vendor import wrapt

from ...internal.utils import get_argument_value
from ...internal.utils.formats import asbool


# Original boto client class
Expand All @@ -32,6 +35,22 @@
AWS_AUTH_TRACED_ARGS = {"path", "data", "host"}


if os.getenv("DD_AWS_TAG_ALL_PARAMS") is not None:
debtcollector.deprecate(
"Using environment variable 'DD_AWS_TAG_ALL_PARAMS' is deprecated",
message="The boto integration no longer includes all API parameters by default.",
removal_version="2.0.0",
)

config._add(
"boto",
{
"tag_no_params": asbool(os.getenv("DD_AWS_TAG_NO_PARAMS", default=False)),
"tag_all_params": asbool(os.getenv("DD_AWS_TAG_ALL_PARAMS", default=False)),
},
)


def patch():
if getattr(boto.connection, "_datadog_patch", False):
return
Expand Down Expand Up @@ -72,11 +91,17 @@ def patched_query_request(original_func, instance, args, kwargs):
operation_name = None
if args:
operation_name = get_argument_value(args, kwargs, 0, "action")
params = get_argument_value(args, kwargs, 1, "params")

span.resource = "%s.%s" % (endpoint_name, operation_name.lower())

if params and not config.boto["tag_no_params"]:
aws._add_api_param_span_tags(span, endpoint_name, params)
else:
span.resource = endpoint_name

aws.add_span_arg_tags(span, endpoint_name, args, AWS_QUERY_ARGS_NAME, AWS_QUERY_TRACED_ARGS)
if not config.boto["tag_no_params"] and config.boto["tag_all_params"]:
aws.add_span_arg_tags(span, endpoint_name, args, AWS_QUERY_ARGS_NAME, AWS_QUERY_TRACED_ARGS)

# Obtaining region name
region_name = _get_instance_region_name(instance)
Expand Down Expand Up @@ -140,7 +165,8 @@ def patched_auth_request(original_func, instance, args, kwargs):
else:
span.resource = endpoint_name

aws.add_span_arg_tags(span, endpoint_name, args, AWS_AUTH_ARGS_NAME, AWS_AUTH_TRACED_ARGS)
if not config.boto["tag_no_params"] and config.boto["tag_all_params"]:
aws.add_span_arg_tags(span, endpoint_name, args, AWS_AUTH_ARGS_NAME, AWS_AUTH_TRACED_ARGS)

# Obtaining region name
region_name = _get_instance_region_name(instance)
Expand Down
26 changes: 26 additions & 0 deletions ddtrace/contrib/botocore/__init__.py
Expand Up @@ -52,6 +52,32 @@
See :ref:`HTTP - Custom Error Codes<http-custom-error>` documentation for more examples.
.. py:data:: ddtrace.config.botocore['tag_no_params']
This opts out of the default behavior of collecting a narrow set of API parameters as span tags.
To not collect any API parameters, ``ddtrace.config.botocore.tag_no_params = True`` or by setting the environment
variable ``DD_AWS_TAG_NO_PARAMS=true``.
Default: ``False``
.. py:data:: ddtrace.config.botocore['tag_all_params']
**Deprecated**: This retains the deprecated behavior of adding span tags for
all API parameters that are not explicitly excluded by the integration.
These deprecated span tags will be added along with the API parameters
enabled by default.
This configuration is ignored if ``tag_no_parms`` (``DD_AWS_TAG_NO_PARAMS``)
is set to ``True``.
To collect all API parameters, ``ddtrace.config.botocore.tag_all_params =
True`` or by setting the environment variable ``DD_AWS_TAG_ALL_PARAMS=true``.
Default: ``False``
Example::
Expand Down
17 changes: 16 additions & 1 deletion ddtrace/contrib/botocore/patch.py
Expand Up @@ -16,6 +16,7 @@

from ddtrace import config
from ddtrace.settings.config import Config
from ddtrace.vendor import debtcollector
from ddtrace.vendor import wrapt

from ...constants import ANALYTICS_SAMPLE_RATE_KEY
Expand Down Expand Up @@ -47,13 +48,23 @@
log = get_logger(__name__)


if os.getenv("DD_AWS_TAG_ALL_PARAMS") is not None:
debtcollector.deprecate(
"Using environment variable 'DD_AWS_TAG_ALL_PARAMS' is deprecated",
message="The botocore integration no longer includes all API parameters by default.",
removal_version="2.0.0",
)


# Botocore default settings
config._add(
"botocore",
{
"distributed_tracing": asbool(os.getenv("DD_BOTOCORE_DISTRIBUTED_TRACING", default=True)),
"invoke_with_legacy_context": asbool(os.getenv("DD_BOTOCORE_INVOKE_WITH_LEGACY_CONTEXT", default=False)),
"operations": collections.defaultdict(Config._HTTPServerConfig),
"tag_no_params": asbool(os.getenv("DD_AWS_TAG_NO_PARAMS", default=False)),
"tag_all_params": asbool(os.getenv("DD_AWS_TAG_ALL_PARAMS", default=False)),
},
)

Expand Down Expand Up @@ -325,10 +336,14 @@ def patched_api_call(original_func, instance, args, kwargs):
except Exception:
log.warning("Unable to inject trace context", exc_info=True)

if params and not config.botocore["tag_no_params"]:
aws._add_api_param_span_tags(span, endpoint_name, params)

else:
span.resource = endpoint_name

aws.add_span_arg_tags(span, endpoint_name, args, ARGS_NAME, TRACED_ARGS)
if not config.botocore["tag_no_params"] and config.botocore["tag_all_params"]:
aws.add_span_arg_tags(span, endpoint_name, args, ARGS_NAME, TRACED_ARGS)

region_name = deep_getattr(instance, "meta.region_name")

Expand Down
33 changes: 33 additions & 0 deletions ddtrace/ext/aws.py
@@ -1,4 +1,5 @@
from typing import Any
from typing import Dict
from typing import FrozenSet
from typing import Set
from typing import TYPE_CHECKING
Expand Down Expand Up @@ -47,6 +48,38 @@ def add_span_arg_tags(
)


def _add_api_param_span_tags(span, endpoint_name, params):
# type: (Span, str, Dict[str, Any]) -> None
if endpoint_name == "cloudwatch":
log_group_name = params.get("logGroupName")
if log_group_name:
span.set_tag_str("aws.cloudwatch.logs.log_group_name", log_group_name)
elif endpoint_name == "dynamodb":
table_name = params.get("TableName")
if table_name:
span.set_tag_str("aws.dynamodb.table_name", table_name)
elif endpoint_name == "kinesis":
stream_name = params.get("StreamName")
if stream_name:
span.set_tag_str("aws.kinesis.stream_name", stream_name)
elif endpoint_name == "redshift":
cluster_identifier = params.get("ClusterIdentifier")
if cluster_identifier:
span.set_tag_str("aws.redshift.cluster_identifier", cluster_identifier)
elif endpoint_name == "s3":
bucket_name = params.get("Bucket")
if bucket_name:
span.set_tag_str("aws.s3.bucket_name", bucket_name)
elif endpoint_name == "sns":
topic_arn = params.get("TopicArn")
if topic_arn:
span.set_tag_str("aws.sns.topic_arn", topic_arn)
elif endpoint_name == "sqs":
queue_name = params.get("QueueName") or params.get("QueueUrl")
if queue_name:
span.set_tag_str("aws.sqs.queue_name", queue_name)


REGION = "aws.region"
AGENT = "aws.agent"
OPERATION = "aws.operation"
1 change: 1 addition & 0 deletions docs/spelling_wordlist.txt
Expand Up @@ -32,6 +32,7 @@ asyncpg
attrs
autodetected
autopatching
aws
backend
backends
backport
Expand Down
7 changes: 7 additions & 0 deletions releasenotes/notes/fix-aws-tag-params-0e1488513a0ae5c7.yaml
@@ -0,0 +1,7 @@
---
deprecations:
- |
aws: The boto, botocore and aiobotocore integrations no longer include all API parameters by default. To retain the deprecated behavior, set the environment variable ``DD_AWS_TAG_ALL_PARAMS=1``. The deprecated behavior and environment variable will be removed in v2.0.0.
fixes:
- |
aws: We are reducing the number of API parameters that the boto, botocore and aiobotocore integrations collect as span tags by default. This change limits span tags to a narrow set of parameters for specific AWS APIs using standard tag names. To opt out of the new default behavior and collect no API parameters, set the environment variable ``DD_AWS_TAG_NO_PARAMS=1``. To retain the deprecated behavior and collect all API parameters, set the environment variable ``DD_AWS_TAG_ALL_PARAMS=1``.

0 comments on commit 0cad73a

Please sign in to comment.