Files
Steve Flanders ce66c8d458 Remove non-inclusive language (#43)
Addresses #42

TODO: address pylint
2020-09-08 17:06:47 -07:00

91 lines
3.6 KiB
Python

import re
from ..internal.logger import get_logger
from ..utils.http import normalize_header_name
log = get_logger(__name__)
REQUEST = 'request'
RESPONSE = 'response'
# Tag normalization based on: https://docs.datadoghq.com/tagging/#defining-tags
# With the exception of '.' in header names which are replaced with '_' to avoid
# starting a "new object" on the UI.
NORMALIZE_PATTERN = re.compile(r'([^a-z0-9_\-:/]){1}')
def store_request_headers(headers, span, integration_config):
"""
Store request headers as a span's tags
:param headers: All the request's http headers, will be filtered through the allowlist
:type headers: dict or list
:param span: The Span instance where tags will be stored
:type span: ddtrace.Span
:param integration_config: An integration specific config object.
:type integration_config: ddtrace.settings.IntegrationConfig
"""
_store_headers(headers, span, integration_config, REQUEST)
def store_response_headers(headers, span, integration_config):
"""
Store response headers as a span's tags
:param headers: All the response's http headers, will be filtered through the allowlist
:type headers: dict or list
:param span: The Span instance where tags will be stored
:type span: ddtrace.Span
:param integration_config: An integration specific config object.
:type integration_config: ddtrace.settings.IntegrationConfig
"""
_store_headers(headers, span, integration_config, RESPONSE)
def _store_headers(headers, span, integration_config, request_or_response):
"""
:param headers: A dict of http headers to be stored in the span
:type headers: dict or list
:param span: The Span instance where tags will be stored
:type span: ddtrace.span.Span
:param integration_config: An integration specific config object.
:type integration_config: ddtrace.settings.IntegrationConfig
"""
if not isinstance(headers, dict):
try:
headers = dict(headers)
except Exception:
return
if integration_config is None:
log.debug('Skipping headers tracing as no integration config was provided')
return
for header_name, header_value in headers.items():
if not integration_config.header_is_traced(header_name):
continue
tag_name = _normalize_tag_name(request_or_response, header_name)
span.set_tag(tag_name, header_value)
def _normalize_tag_name(request_or_response, header_name):
"""
Given a tag name, e.g. 'Content-Type', returns a corresponding normalized tag name, i.e
'http.request.headers.content_type'. Rules applied actual header name are:
- any letter is converted to lowercase
- any digit is left unchanged
- any block of any length of different ASCII chars is converted to a single underscore '_'
:param request_or_response: The context of the headers: request|response
:param header_name: The header's name
:type header_name: str
:rtype: str
"""
# Looking at:
# - http://www.iana.org/assignments/message-headers/message-headers.xhtml
# - https://tools.ietf.org/html/rfc6648
# and for consistency with other language integrations seems safe to assume the following algorithm for header
# names normalization:
# - any letter is converted to lowercase
# - any digit is left unchanged
# - any block of any length of different ASCII chars is converted to a single underscore '_'
normalized_name = NORMALIZE_PATTERN.sub('_', normalize_header_name(header_name))
return 'http.{}.headers.{}'.format(request_or_response, normalized_name)