feat(configuration): Mark 4xx calls as erroneous spans and adapt current instrumentations. - #900
feat(configuration): Mark 4xx calls as erroneous spans and adapt current instrumentations.#900CagriYonca wants to merge 1 commit into
Conversation
b4a4852 to
ef94674
Compare
a0dc787 to
067a804
Compare
…ent instrumentations. Signed-off-by: Cagri Yonca <cagri@ibm.com>
067a804 to
be9bfea
Compare
| env_classify_as_errors = os.environ.get( | ||
| "INSTANA_TRACING_HTTP_EXIT_CLASSIFY_AS_ERRORS", None | ||
| ) | ||
| if env_classify_as_errors is not None: | ||
| codes = [] | ||
| for part in env_classify_as_errors.split(","): | ||
| part = part.strip() | ||
| if part.isdigit(): | ||
| code = int(part) | ||
| if 400 <= code <= 499: | ||
| codes.append(code) | ||
| else: | ||
| logger.warning( | ||
| "Ignoring out-of-range value in" | ||
| " INSTANA_TRACING_HTTP_EXIT_CLASSIFY_AS_ERRORS:" | ||
| f" {code}, must be 400-499" | ||
| ) | ||
| elif part: | ||
| logger.warning( | ||
| "Ignoring non-integer value in" | ||
| f" INSTANA_TRACING_HTTP_EXIT_CLASSIFY_AS_ERRORS: {part}" | ||
| ) | ||
| if codes: | ||
| self.http_exit_classify_as_errors = codes | ||
| elif "INSTANA_TRACING_HTTP_EXIT_CLASSIFY_ALL_4XX_AS_ERRORS" in os.environ: | ||
| raw = os.environ["INSTANA_TRACING_HTTP_EXIT_CLASSIFY_ALL_4XX_AS_ERRORS"] | ||
| if raw.lower() in ("true", "false", "1"): | ||
| self.http_exit_classify_all_4xx_as_errors = is_truthy(raw) | ||
| else: | ||
| logger.warning( | ||
| "Ignoring unrecognised value for" | ||
| " INSTANA_TRACING_HTTP_EXIT_CLASSIFY_ALL_4XX_AS_ERRORS:" | ||
| f" '{raw}', expected true or false" | ||
| ) | ||
| elif "INSTANA_CONFIG_PATH" in os.environ: | ||
| classify_all, codes = get_http_exit_classification_from_yaml() | ||
| if codes: | ||
| self.http_exit_classify_as_errors = codes | ||
| elif classify_all: | ||
| self.http_exit_classify_all_4xx_as_errors = True |
There was a problem hiding this comment.
Move this to a specific method like the set_disable_trace_configurations() and others.
It's too long and hard to understand that this is related to setting up the list of 4xx status codes to use as error codes.
It was even difficult to select all those lines to provide this comment.
| raw = os.environ["INSTANA_TRACING_HTTP_EXIT_CLASSIFY_ALL_4XX_AS_ERRORS"] | ||
| if raw.lower() in ("true", "false", "1"): | ||
| self.http_exit_classify_all_4xx_as_errors = is_truthy(raw) | ||
| else: | ||
| logger.warning( | ||
| "Ignoring unrecognised value for" | ||
| " INSTANA_TRACING_HTTP_EXIT_CLASSIFY_ALL_4XX_AS_ERRORS:" | ||
| f" '{raw}', expected true or false" |
There was a problem hiding this comment.
No need to process all of this information. You have already set http_exit_classify_all_4xx_as_errors = False as the default value, so let's only accept a "truthy" value here.
| raw = os.environ["INSTANA_TRACING_HTTP_EXIT_CLASSIFY_ALL_4XX_AS_ERRORS"] | |
| if raw.lower() in ("true", "false", "1"): | |
| self.http_exit_classify_all_4xx_as_errors = is_truthy(raw) | |
| else: | |
| logger.warning( | |
| "Ignoring unrecognised value for" | |
| " INSTANA_TRACING_HTTP_EXIT_CLASSIFY_ALL_4XX_AS_ERRORS:" | |
| f" '{raw}', expected true or false" | |
| self.http_exit_classify_all_4xx_as_errors = is_truthy( | |
| os.environ["INSTANA_TRACING_HTTP_EXIT_CLASSIFY_ALL_4XX_AS_ERRORS"] | |
| ) | |
| logger.debug( | |
| "INSTANA_TRACING_HTTP_EXIT_CLASSIFY_ALL_4XX_AS_ERRORS = True" | |
| ) |
| elif "INSTANA_CONFIG_PATH" in os.environ: | ||
| classify_all, codes = get_http_exit_classification_from_yaml() | ||
| if codes: | ||
| self.http_exit_classify_as_errors = codes | ||
| elif classify_all: | ||
| self.http_exit_classify_all_4xx_as_errors = True |
There was a problem hiding this comment.
Let's ensure the self.http_exit_classify_as_errors and self.http_exit_classify_all_4xx_as_errors are correct:
| elif "INSTANA_CONFIG_PATH" in os.environ: | |
| classify_all, codes = get_http_exit_classification_from_yaml() | |
| if codes: | |
| self.http_exit_classify_as_errors = codes | |
| elif classify_all: | |
| self.http_exit_classify_all_4xx_as_errors = True | |
| elif "INSTANA_CONFIG_PATH" in os.environ: | |
| classify_all, codes = get_http_exit_classification_from_yaml() | |
| if codes: | |
| self.http_exit_classify_as_errors = codes | |
| self.http_exit_classify_all_4xx_as_errors = False | |
| elif classify_all: | |
| self.http_exit_classify_all_4xx_as_errors = True | |
| self.http_exit_classify_as_errors = [] |
Actually, if you really guarantee the return of get_http_exit_classification_from_yaml() will be always correct, you can make it simpler:
| elif "INSTANA_CONFIG_PATH" in os.environ: | |
| classify_all, codes = get_http_exit_classification_from_yaml() | |
| if codes: | |
| self.http_exit_classify_as_errors = codes | |
| elif classify_all: | |
| self.http_exit_classify_all_4xx_as_errors = True | |
| elif "INSTANA_CONFIG_PATH" in os.environ: | |
| self.http_exit_classify_all_4xx_as_errors, self.http_exit_classify_as_errors = get_http_exit_classification_from_yaml() |
| if params.response.status >= 500: | ||
| span.mark_as_errored({"http.error": params.response.reason}) | ||
| if should_mark_http_exit_as_error(params.response.status, agent.options): | ||
| span.mark_as_errored({"http.error": http_error_message(params.response.status)}) |
There was a problem hiding this comment.
I like the idea of having a method to set the expected value with the status code and reason. However, I don't think that's totally reusable since not all frameworks work with the same encoding or return pattern - you can see your Twisted change; it doesn't fit. In addition, users can customize the reason depending on the behavior of their systems, so not all messages will provide the correct reason.
I suggest you remove the http_error_message() method and use the f-string format for that.
| span.mark_as_errored({"http.error": http_error_message(params.response.status)}) | |
| error_msg = f"{params.response.status} {params.response.reason}" | |
| span.mark_as_errored({"http.error": error_msg) |
|
|
||
| extract_custom_headers(span, response.headers) | ||
| if should_mark_http_exit_as_error(response.code, agent.options): | ||
| span.mark_as_errored({"http.error": http_error_message(response.code)}) |
There was a problem hiding this comment.
Like here:
| span.mark_as_errored({"http.error": http_error_message(response.code)}) | |
| error_msg = f"{response.code} {response.body}" | |
| span.mark_as_errored({"http.error": error_msg) |
| span = instance.request._instana | ||
| tracer = get_tracer() | ||
| tracer.inject(span.context, Format.HTTP_HEADERS, instance._headers) | ||
| return wrapped(*argv, **kwargs) |
feat: HTTP 4xx exit span error classification
Implements opt-in 4xx error classification for HTTP exit spans per the
tracing specification. Entry spans are never affected.
What changed
Configuration
tracing.http.exit:classify-all-4xx-as-errors(boolean) — marks all 4xx responses as errors on exit spansclassify-as-errors(list of integers, 400–499) — marks only the listed codes as errorsclassify-as-errorstakes full precedence overclassify-all-4xx-as-errorswhen both are setINSTANA_TRACING_HTTP_EXIT_CLASSIFY_AS_ERRORS=401,403INSTANA_TRACING_HTTP_EXIT_CLASSIFY_ALL_4XX_AS_ERRORS=trueINSTANA_CONFIG_PATHYAMLCLASSIFY_ALLkeep the default (false)http.errorformathttp.errorattribute now uses"<code> <reason>"format (e.g."404 Not Found") via a newhttp_error_message()helper, consistent across all four clients:aiohttp,httpx,tornado,urllib3Tests
tests/clients/test_4xx_classification.py— end-to-end classification teststests/test_options.py— configuration precedence, invalid value handling, YAML fallbacktests/util/test_http_utils.py—http_error_message()andshould_mark_http_exit_as_error()unit tests