Skip to content

Commit ea78893

Browse files
authored
[DBMON-5640] SqlServer - compile and reuse connection error regex (DataDog#21319)
* compile and reuse connection error regex * Add changelog
1 parent 8ecd87c commit ea78893

2 files changed

Lines changed: 25 additions & 12 deletions

File tree

sqlserver/changelog.d/21319.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Compile and reuse connection error regex patterns

sqlserver/datadog_checks/sqlserver/connection_errors.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,21 +40,33 @@ class ConnectionErrorCode(Enum):
4040
# Connection error messages, which we expect to get from an ADO provider or
4141
# ODBC. These drivers can have inconsistent error codes across versions, so regex on
4242
# the known error messages
43-
known_error_patterns = {
43+
KNOWN_ERROR_PATTERNS = {
4444
# typically results in an -2147467259 ADO error code, which is not very descriptive. Identifying this error
4545
# can help provide specific troubleshooting help to the customer
46-
"(certificate verify failed|"
47-
"certificate chain was issued by an authority that is not trusted)": ConnectionErrorCode.certificate_verify_failed,
46+
re.compile(
47+
"(certificate verify failed|certificate chain was issued by an authority that is not trusted)", re.IGNORECASE
48+
): ConnectionErrorCode.certificate_verify_failed,
4849
# DSN could be specified incorrectly in config
49-
"data source name not found.* and no default driver specified": ConnectionErrorCode.driver_not_found,
50+
re.compile(
51+
"data source name not found.* and no default driver specified", re.IGNORECASE
52+
): ConnectionErrorCode.driver_not_found,
5053
# driver not installed on host
51-
"(can't open lib .* file not found|Provider cannot be found)": ConnectionErrorCode.driver_not_found,
54+
re.compile(
55+
"(can't open lib .* file not found|Provider cannot be found)", re.IGNORECASE
56+
): ConnectionErrorCode.driver_not_found,
5257
# Connection & login issues
53-
"(cannot open database .* requested by the login|login timeout expired)": ConnectionErrorCode.tcp_connection_failed,
54-
"(login failed for user|The login is from an untrusted domain)": ConnectionErrorCode.login_failed_for_user,
55-
"ssl security error": ConnectionErrorCode.ssl_security_error,
58+
re.compile(
59+
"(cannot open database .* requested by the login|login timeout expired)", re.IGNORECASE
60+
): ConnectionErrorCode.tcp_connection_failed,
61+
re.compile(
62+
"(login failed for user|The login is from an untrusted domain)", re.IGNORECASE
63+
): ConnectionErrorCode.login_failed_for_user,
64+
re.compile("ssl security error", re.IGNORECASE): ConnectionErrorCode.ssl_security_error,
5665
}
5766

67+
# Pre-compiled regex pattern for password obfuscation
68+
PASSWORD_OBFUSCATION_PATTERN = re.compile(r"(?i)(Password=)([^;]+)")
69+
5870
# ADO provider connection errors yield a hresult code, which
5971
# can be mapped to helpful err messages
6072
known_hresult_codes = {
@@ -132,9 +144,9 @@ def _get_is_odbc_driver_installed(configured_driver):
132144

133145

134146
def _lookup_conn_error_and_msg(hresult, msg):
135-
for k in known_error_patterns.keys():
136-
if re.search(k, msg, re.IGNORECASE):
137-
return None, known_error_patterns[k]
147+
for compiled_pattern, error_code in KNOWN_ERROR_PATTERNS.items():
148+
if compiled_pattern.search(msg):
149+
return None, error_code
138150
# if we cannot determine the type or error based on the msg, try to look it up by its hresult
139151
# this will be true for error messages like 'Invalid connection string attribute'
140152
if hresult:
@@ -151,7 +163,7 @@ def obfuscate_error_msg(msg, password):
151163
# obfuscate the password in the error message
152164
# regex to match the `Password=<password>;` in the connection string
153165
# and replace it with `Password=***;` (case insensitive)
154-
obfuscated_error_msg = re.sub(r"(?i)(Password=)([^;]+)", r"\1******", msg)
166+
obfuscated_error_msg = PASSWORD_OBFUSCATION_PATTERN.sub(r"\1******", msg)
155167
if password:
156168
# this is a fallback in case the password is not in the connection string
157169
obfuscated_error_msg = obfuscated_error_msg.replace(password, "*" * 6)

0 commit comments

Comments
 (0)