Esempio n. 1
0
 def test_to_native_string(self):
     # type: () -> None
     text = u'éâû'
     binary = text.encode('utf-8')
     if PY3:
         assert to_native_string(binary) == text
     else:
         assert to_native_string(binary) == binary
Esempio n. 2
0
 def __init__(self, check, config, connection_args):
     # (MySql, MySQLConfig) -> None
     collection_interval = float(
         config.statement_metrics_config.get('collection_interval', 10))
     if collection_interval <= 0:
         collection_interval = 10
     super(MySQLStatementMetrics, self).__init__(
         check,
         rate_limit=1 / float(collection_interval),
         run_sync=is_affirmative(
             config.statement_metrics_config.get('run_sync', False)),
         enabled=is_affirmative(
             config.statement_metrics_config.get('enabled', True)),
         expected_db_exceptions=(pymysql.err.DatabaseError, ),
         min_collection_interval=config.min_collection_interval,
         dbms="mysql",
         job_name="statement-metrics",
         shutdown_callback=self._close_db_conn,
     )
     self._metric_collection_interval = collection_interval
     self._connection_args = connection_args
     self._db = None
     self._config = config
     self.log = get_check_logger()
     self._state = StatementMetrics()
     self._obfuscate_options = to_native_string(
         json.dumps(self._config.obfuscator_options))
     # full_statement_text_cache: limit the ingestion rate of full statement text events per query_signature
     self._full_statement_text_cache = TTLCache(
         maxsize=self._config.full_statement_text_cache_max_size,
         ttl=60 * 60 /
         self._config.full_statement_text_samples_per_hour_per_query,
     )  # type: TTLCache
Esempio n. 3
0
 def __init__(self, check, config, shutdown_callback):
     collection_interval = float(
         config.statement_metrics_config.get('collection_interval', DEFAULT_COLLECTION_INTERVAL)
     )
     if collection_interval <= 0:
         collection_interval = DEFAULT_COLLECTION_INTERVAL
     super(PostgresStatementMetrics, self).__init__(
         check,
         run_sync=is_affirmative(config.statement_metrics_config.get('run_sync', False)),
         enabled=is_affirmative(config.statement_metrics_config.get('enabled', True)),
         expected_db_exceptions=(psycopg2.errors.DatabaseError,),
         min_collection_interval=config.min_collection_interval,
         dbms="postgres",
         rate_limit=1 / float(collection_interval),
         job_name="query-metrics",
         shutdown_callback=shutdown_callback,
     )
     self._metrics_collection_interval = collection_interval
     self._config = config
     self._state = StatementMetrics()
     self._stat_column_cache = []
     self._obfuscate_options = to_native_string(json.dumps(self._config.obfuscator_options))
     # full_statement_text_cache: limit the ingestion rate of full statement text events per query_signature
     self._full_statement_text_cache = TTLCache(
         maxsize=config.full_statement_text_cache_max_size,
         ttl=60 * 60 / config.full_statement_text_samples_per_hour_per_query,
     )
Esempio n. 4
0
def _strip_whitespace(raw_plan):
    tree = ET.fromstring(raw_plan)
    for e in tree.iter():
        if e.text:
            e.text = e.text.strip()
        if e.tail:
            e.tail = e.tail.strip()
    return to_native_string(ET.tostring(tree))
Esempio n. 5
0
def obfuscate_xml_plan(raw_plan, obfuscator_options=None):
    """
    Obfuscates SQL text & Parameters from the provided SQL Server XML Plan
    Also strips unnecessary whitespace
    """
    tree = ET.fromstring(raw_plan)
    for e in tree.iter():
        if e.text:
            e.text = e.text.strip()
        if e.tail:
            e.tail = e.tail.strip()
        for k in XML_PLAN_OBFUSCATION_ATTRS:
            val = e.attrib.get(k, None)
            if val:
                statement = obfuscate_sql_with_metadata(val, obfuscator_options)
                e.attrib[k] = ensure_unicode(statement['query'])
    return to_native_string(ET.tostring(tree, encoding="UTF-8"))
Esempio n. 6
0
def generate_container_profile_config_with_ad(profile):
    host = socket.gethostbyname(get_container_ip(SNMP_CONTAINER_NAME))
    network = ipaddress.ip_network(u'{}/29'.format(host),
                                   strict=False).with_prefixlen
    conf = {
        # Make sure the check handles bytes
        'network_address': to_native_string(network),
        'port': PORT,
        'community_string': 'apc_ups',
    }

    init_config = {}

    instance = generate_instance_config([], template=conf)
    instance['community_string'] = profile
    instance['enforce_mib_constraints'] = False
    return {
        'init_config': init_config,
        'instances': [instance],
    }
Esempio n. 7
0
def _hash_to_hex(hash):
    return to_native_string(binascii.hexlify(hash))
Esempio n. 8
0
 def __init__(self, check, config, connection_args):
     collection_interval = float(
         config.statement_metrics_config.get('collection_interval', 1))
     if collection_interval <= 0:
         collection_interval = 1
     super(MySQLStatementSamples, self).__init__(
         check,
         rate_limit=1 / collection_interval,
         run_sync=is_affirmative(
             config.statement_samples_config.get('run_sync', False)),
         enabled=is_affirmative(
             config.statement_samples_config.get('enabled', True)),
         min_collection_interval=config.min_collection_interval,
         dbms="mysql",
         expected_db_exceptions=(pymysql.err.DatabaseError, ),
         job_name="statement-samples",
         shutdown_callback=self._close_db_conn,
     )
     self._config = config
     self._version_processed = False
     self._connection_args = connection_args
     # checkpoint at zero so we pull the whole history table on the first run
     self._checkpoint = 0
     self._last_check_run = 0
     self._db = None
     self._configured_collection_interval = self._config.statement_samples_config.get(
         'collection_interval', -1)
     self._events_statements_row_limit = self._config.statement_samples_config.get(
         'events_statements_row_limit', 5000)
     self._explain_procedure = self._config.statement_samples_config.get(
         'explain_procedure', 'explain_statement')
     self._fully_qualified_explain_procedure = self._config.statement_samples_config.get(
         'fully_qualified_explain_procedure', 'datadog.explain_statement')
     self._events_statements_temp_table = self._config.statement_samples_config.get(
         'events_statements_temp_table_name', 'datadog.temp_events')
     self._events_statements_enable_procedure = self._config.statement_samples_config.get(
         'events_statements_enable_procedure',
         'datadog.enable_events_statements_consumers')
     self._preferred_events_statements_tables = EVENTS_STATEMENTS_PREFERRED_TABLES
     self._has_window_functions = False
     events_statements_table = self._config.statement_samples_config.get(
         'events_statements_table', None)
     if events_statements_table:
         if events_statements_table in DEFAULT_EVENTS_STATEMENTS_COLLECTION_INTERVAL:
             self._log.debug(
                 "Configured preferred events_statements_table: %s",
                 events_statements_table)
             self._preferred_events_statements_tables = [
                 events_statements_table
             ]
         else:
             self._log.warning(
                 "Invalid events_statements_table: %s. Must be one of %s. Falling back to trying all tables.",
                 events_statements_table,
                 ', '.join(
                     DEFAULT_EVENTS_STATEMENTS_COLLECTION_INTERVAL.keys()),
             )
     self._explain_strategies = {
         'PROCEDURE': self._run_explain_procedure,
         'FQ_PROCEDURE': self._run_fully_qualified_explain_procedure,
         'STATEMENT': self._run_explain,
     }
     self._preferred_explain_strategies = [
         'PROCEDURE', 'FQ_PROCEDURE', 'STATEMENT'
     ]
     self._obfuscate_options = to_native_string(
         json.dumps(self._config.obfuscator_options))
     self._init_caches()
Esempio n. 9
0
    def __init__(self, name, init_config, instances):
        super(SQLServer, self).__init__(name, init_config, instances)

        self._resolved_hostname = None
        self._agent_hostname = None
        self.connection = None
        self.failed_connections = {}
        self.instance_metrics = []
        self.instance_per_type_metrics = defaultdict(set)
        self.do_check = True

        self.tags = self.instance.get("tags", [])
        self.reported_hostname = self.instance.get('reported_hostname')
        self.autodiscovery = is_affirmative(self.instance.get('database_autodiscovery'))
        self.autodiscovery_include = self.instance.get('autodiscovery_include', ['.*'])
        self.autodiscovery_exclude = self.instance.get('autodiscovery_exclude', [])
        self.autodiscovery_db_service_check = is_affirmative(self.instance.get('autodiscovery_db_service_check', True))
        self.min_collection_interval = self.instance.get('min_collection_interval', 15)
        self._compile_patterns()
        self.autodiscovery_interval = self.instance.get('autodiscovery_interval', DEFAULT_AUTODISCOVERY_INTERVAL)
        self.databases = set()
        self.ad_last_check = 0

        self.proc = self.instance.get('stored_procedure')
        self.proc_type_mapping = {'gauge': self.gauge, 'rate': self.rate, 'histogram': self.histogram}
        self.custom_metrics = init_config.get('custom_metrics', [])

        # DBM
        self.dbm_enabled = self.instance.get('dbm', False)
        self.statement_metrics_config = self.instance.get('query_metrics', {}) or {}
        self.statement_metrics = SqlserverStatementMetrics(self)
        self.activity_config = self.instance.get('query_activity', {}) or {}
        self.activity = SqlserverActivity(self)
        self.cloud_metadata = {}
        aws = self.instance.get('aws', {})
        gcp = self.instance.get('gcp', {})
        azure = self.instance.get('azure', {})
        if aws:
            self.cloud_metadata.update({'aws': aws})
        if gcp:
            self.cloud_metadata.update({'gcp': gcp})
        if azure:
            self.cloud_metadata.update({'azure': azure})
        obfuscator_options_config = self.instance.get('obfuscator_options', {}) or {}
        self.obfuscator_options = to_native_string(
            json.dumps(
                {
                    # Valid values for this can be found at
                    # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md#connection-level-attributes
                    'dbms': 'mssql',
                    'replace_digits': is_affirmative(
                        obfuscator_options_config.get(
                            'replace_digits',
                            obfuscator_options_config.get('quantize_sql_tables', False),
                        )
                    ),
                    'keep_sql_alias': is_affirmative(obfuscator_options_config.get('keep_sql_alias', True)),
                    'return_json_metadata': is_affirmative(obfuscator_options_config.get('collect_metadata', True)),
                    'table_names': is_affirmative(obfuscator_options_config.get('collect_tables', True)),
                    'collect_commands': is_affirmative(obfuscator_options_config.get('collect_commands', True)),
                    'collect_comments': is_affirmative(obfuscator_options_config.get('collect_comments', True)),
                }
            )
        )

        self.static_info_cache = TTLCache(
            maxsize=100,
            # cache these for a full day
            ttl=60 * 60 * 24,
        )

        # Query declarations
        check_queries = []
        if is_affirmative(self.instance.get('include_ao_metrics', False)):
            check_queries.extend(
                [
                    QUERY_AO_AVAILABILITY_GROUPS,
                    QUERY_AO_FAILOVER_CLUSTER,
                    QUERY_AO_FAILOVER_CLUSTER_MEMBER,
                ]
            )
        if is_affirmative(self.instance.get('include_fci_metrics', False)):
            check_queries.extend([QUERY_FAILOVER_CLUSTER_INSTANCE])
        self._check_queries = self._new_query_executor(check_queries)
        self.check_initializations.append(self._check_queries.compile_queries)

        self.server_state_queries = self._new_query_executor([QUERY_SERVER_STATIC_INFO])
        self.check_initializations.append(self.server_state_queries.compile_queries)

        # use QueryManager to process custom queries
        self._query_manager = QueryManager(
            self, self.execute_query_raw, tags=self.tags, hostname=self.resolved_hostname
        )

        self._dynamic_queries = None

        self.check_initializations.append(self.config_checks)
        self.check_initializations.append(self._query_manager.compile_queries)
        self.check_initializations.append(self.initialize_connection)
    def __init__(self, check, config, shutdown_callback):
        collection_interval = float(
            config.statement_samples_config.get('collection_interval',
                                                DEFAULT_COLLECTION_INTERVAL))
        if collection_interval <= 0:
            collection_interval = DEFAULT_COLLECTION_INTERVAL
        super(PostgresStatementSamples, self).__init__(
            check,
            rate_limit=1 / collection_interval,
            run_sync=is_affirmative(
                config.statement_samples_config.get('run_sync', False)),
            enabled=is_affirmative(
                config.statement_samples_config.get('enabled', True)),
            dbms="postgres",
            min_collection_interval=config.min_collection_interval,
            expected_db_exceptions=(psycopg2.errors.DatabaseError, ),
            job_name="query-samples",
            shutdown_callback=shutdown_callback,
        )
        self._check = check
        self._config = config
        self._tags_no_db = None
        self._activity_last_query_start = None
        # The value is loaded when connecting to the main database
        self._explain_function = config.statement_samples_config.get(
            'explain_function', 'datadog.explain_statement')
        self._obfuscate_options = to_native_string(
            json.dumps(self._config.obfuscator_options))

        self._collection_strategy_cache = TTLCache(
            maxsize=config.statement_samples_config.get(
                'collection_strategy_cache_maxsize', 1000),
            ttl=config.statement_samples_config.get(
                'collection_strategy_cache_ttl', 300),
        )

        self._explain_errors_cache = TTLCache(
            maxsize=config.statement_samples_config.get(
                'explain_errors_cache_maxsize', 5000),
            # only try to re-explain invalid statements once per day
            ttl=config.statement_samples_config.get('explain_errors_cache_ttl',
                                                    24 * 60 * 60),
        )

        # explained_statements_ratelimiter: limit how often we try to re-explain the same query
        self._explained_statements_ratelimiter = RateLimitingTTLCache(
            maxsize=int(
                config.statement_samples_config.get(
                    'explained_queries_cache_maxsize', 5000)),
            ttl=60 * 60 / int(
                config.statement_samples_config.get(
                    'explained_queries_per_hour_per_query', 60)),
        )

        # seen_samples_ratelimiter: limit the ingestion rate per (query_signature, plan_signature)
        self._seen_samples_ratelimiter = RateLimitingTTLCache(
            # assuming ~100 bytes per entry (query & plan signature, key hash, 4 pointers (ordered dict), expiry time)
            # total size: 10k * 100 = 1 Mb
            maxsize=int(
                config.statement_samples_config.get(
                    'seen_samples_cache_maxsize', 10000)),
            ttl=60 * 60 / int(
                config.statement_samples_config.get(
                    'samples_per_hour_per_query', 15)),
        )

        self._activity_coll_enabled = is_affirmative(
            self._config.statement_activity_config.get('enabled', True))
        # activity events cannot be reported more often than regular samples
        self._activity_coll_interval = max(
            self._config.statement_activity_config.get(
                'collection_interval', DEFAULT_ACTIVITY_COLLECTION_INTERVAL),
            collection_interval,
        )
        self._activity_max_rows = self._config.statement_activity_config.get(
            'payload_row_limit', 3500)
        # Keep track of last time we sent an activity event
        self._time_since_last_activity_event = 0
        self._pg_stat_activity_cols = None