-
Notifications
You must be signed in to change notification settings - Fork 829
Integrating sql commenter into otel_django_instrumentation #896
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ocelotl
merged 39 commits into
open-telemetry:main
from
Thiyagu55:sqlcommenter-django-integration-solution-a
Jun 29, 2022
Merged
Changes from 38 commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
0e11241
Integrating sql commenter into otel_django_instrumentation
Thiyagu55 d6013da
Merge branch 'main' of https://github.com/Thiyagu55/opentelemetry-pyt…
Thiyagu55 c4b13fa
Added test cases for django
Thiyagu55 f57b0ba
Merge branch 'main' of https://github.com/Thiyagu55/opentelemetry-pyt…
Thiyagu55 b6d3ff1
- Linting changes
Thiyagu55 835cb7a
- Linting changes
Thiyagu55 a20e131
- Linting changes
Thiyagu55 f55843e
- Linting changes
Thiyagu55 08b5210
- Linting changes
Thiyagu55 09554b8
- Linting changes
Thiyagu55 885f94e
- Linting changes
Thiyagu55 fe3e330
- Linting changes
Thiyagu55 8f9004d
Merge branch 'main' into sqlcommenter-django-integration-solution-a
Thiyagu55 c807402
PR changes
Thiyagu55 53f16e1
Merge branch 'sqlcommenter-django-integration-solution-a' of https://…
Thiyagu55 2fc90df
Merge branch 'main' into sqlcommenter-django-integration-solution-a
srikanthccv b337eda
Merge branch 'main' into sqlcommenter-django-integration-solution-a
ocelotl 93dc5e8
Merge branch 'main' into sqlcommenter-django-integration-solution-a
srikanthccv 1538979
PR changes
Thiyagu55 d63eb09
Linting changes
Thiyagu55 7738153
Linting changes
Thiyagu55 ce923a1
Linting changes
Thiyagu55 3be1d15
Linting changes
Thiyagu55 397317d
PR changes
Thiyagu55 5270310
PR changes
Thiyagu55 2e58d9e
Merge branch 'main' into sqlcommenter-django-integration-solution-a
ocelotl 21be39b
merge main
Thiyagu55 a532b15
Merge branch 'sqlcommenter-django-integration-solution-a' of https://…
Thiyagu55 febbd1f
PR changes
Thiyagu55 96857ba
linting changes
Thiyagu55 ee38934
PR changes
Thiyagu55 a02c138
linting changes
Thiyagu55 fe32a37
PR changes
Thiyagu55 a358a2a
PR changes
Thiyagu55 3f6423c
Merge branch 'main' into sqlcommenter-django-integration-solution-a
Thiyagu55 1d9e0b7
PR changes
Thiyagu55 9c34550
Merge branch 'sqlcommenter-django-integration-solution-a' of https://…
Thiyagu55 083dce1
PR changes
Thiyagu55 c03192e
PR changes
Thiyagu55 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
File renamed without changes.
153 changes: 153 additions & 0 deletions
153
...ion-django/src/opentelemetry/instrumentation/django/middleware/sqlcommenter_middleware.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| #!/usr/bin/python | ||
| # | ||
| # Copyright The OpenTelemetry Authors | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| from logging import getLogger | ||
| from typing import Any, Type, TypeVar | ||
| from urllib.parse import quote as urllib_quote | ||
|
|
||
| # pylint: disable=no-name-in-module | ||
| from django import conf, get_version | ||
| from django.db import connection | ||
| from django.db.backends.utils import CursorDebugWrapper | ||
|
|
||
| from opentelemetry.trace.propagation.tracecontext import ( | ||
| TraceContextTextMapPropagator, | ||
| ) | ||
|
|
||
| _propagator = TraceContextTextMapPropagator() | ||
|
|
||
| _django_version = get_version() | ||
| _logger = getLogger(__name__) | ||
|
|
||
| T = TypeVar("T") # pylint: disable-msg=invalid-name | ||
|
|
||
|
|
||
| class SqlCommenter: | ||
| """ | ||
| Middleware to append a comment to each database query with details about | ||
| the framework and the execution context. | ||
| """ | ||
|
|
||
| def __init__(self, get_response) -> None: | ||
| self.get_response = get_response | ||
|
|
||
| def __call__(self, request) -> Any: | ||
| with connection.execute_wrapper(_QueryWrapper(request)): | ||
| return self.get_response(request) | ||
|
|
||
|
|
||
| class _QueryWrapper: | ||
| def __init__(self, request) -> None: | ||
| self.request = request | ||
|
|
||
| def __call__(self, execute: Type[T], sql, params, many, context) -> T: | ||
| # pylint: disable-msg=too-many-locals | ||
| with_framework = getattr( | ||
| conf.settings, "SQLCOMMENTER_WITH_FRAMEWORK", True | ||
| ) | ||
| with_controller = getattr( | ||
| conf.settings, "SQLCOMMENTER_WITH_CONTROLLER", True | ||
| ) | ||
| with_route = getattr(conf.settings, "SQLCOMMENTER_WITH_ROUTE", True) | ||
| with_app_name = getattr( | ||
| conf.settings, "SQLCOMMENTER_WITH_APP_NAME", True | ||
| ) | ||
| with_opentelemetry = getattr( | ||
| conf.settings, "SQLCOMMENTER_WITH_OPENTELEMETRY", True | ||
| ) | ||
| with_db_driver = getattr( | ||
| conf.settings, "SQLCOMMENTER_WITH_DB_DRIVER", True | ||
| ) | ||
|
|
||
| db_driver = context["connection"].settings_dict.get("ENGINE", "") | ||
| resolver_match = self.request.resolver_match | ||
|
|
||
| sql_comment = _generate_sql_comment( | ||
| # Information about the controller. | ||
| controller=resolver_match.view_name | ||
| if resolver_match and with_controller | ||
| else None, | ||
| # route is the pattern that matched a request with a controller i.e. the regex | ||
| # See https://docs.djangoproject.com/en/stable/ref/urlresolvers/#django.urls.ResolverMatch.route | ||
| # getattr() because the attribute doesn't exist in Django < 2.2. | ||
| route=getattr(resolver_match, "route", None) | ||
| if resolver_match and with_route | ||
| else None, | ||
| # app_name is the application namespace for the URL pattern that matches the URL. | ||
| # See https://docs.djangoproject.com/en/stable/ref/urlresolvers/#django.urls.ResolverMatch.app_name | ||
| app_name=(resolver_match.app_name or None) | ||
| if resolver_match and with_app_name | ||
| else None, | ||
| # Framework centric information. | ||
| framework=f"django:{_django_version}" if with_framework else None, | ||
| # Information about the database and driver. | ||
| db_driver=db_driver if with_db_driver else None, | ||
| **_get_opentelemetry_values() if with_opentelemetry else {}, | ||
| ) | ||
|
|
||
| # TODO: MySQL truncates logs > 1024B so prepend comments | ||
| # instead of statements, if the engine is MySQL. | ||
| # See: | ||
| # * https://github.com/basecamp/marginalia/issues/61 | ||
| # * https://github.com/basecamp/marginalia/pull/80 | ||
| sql += sql_comment | ||
|
|
||
| # Add the query to the query log if debugging. | ||
| if context["cursor"].__class__ is CursorDebugWrapper: | ||
| context["connection"].queries_log.append(sql) | ||
|
|
||
| return execute(sql, params, many, context) | ||
|
|
||
|
|
||
| def _generate_sql_comment(**meta) -> str: | ||
| """ | ||
| Return a SQL comment with comma delimited key=value pairs created from | ||
| **meta kwargs. | ||
| """ | ||
| key_value_delimiter = "," | ||
|
|
||
| if not meta: # No entries added. | ||
| return "" | ||
|
|
||
| # Sort the keywords to ensure that caching works and that testing is | ||
| # deterministic. It eases visual inspection as well. | ||
| return ( | ||
| " /*" | ||
| + key_value_delimiter.join( | ||
| f"{_url_quote(key)}={_url_quote(value)!r}" | ||
| for key, value in sorted(meta.items()) | ||
| if value is not None | ||
| ) | ||
| + "*/" | ||
| ) | ||
|
|
||
|
|
||
| def _url_quote(value) -> str: | ||
| if not isinstance(value, (str, bytes)): | ||
| return value | ||
| _quoted = urllib_quote(value) | ||
| # Since SQL uses '%' as a keyword, '%' is a by-product of url quoting | ||
| # e.g. foo,bar --> foo%2Cbar | ||
| # thus in our quoting, we need to escape it too to finally give | ||
| # foo,bar --> foo%%2Cbar | ||
| return _quoted.replace("%", "%%") | ||
|
|
||
|
|
||
| def _get_opentelemetry_values() -> dict or None: | ||
| """ | ||
| Return the OpenTelemetry Trace and Span IDs if Span ID is set in the | ||
| OpenTelemetry execution context. | ||
| """ | ||
| return _propagator.inject({}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.