Skip to content
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

NH-26617 add Python framework versions to init msg #94

Merged
merged 11 commits into from
Jan 5, 2023
92 changes: 92 additions & 0 deletions solarwinds_apm/configurator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Module to initialize OpenTelemetry SDK components and liboboe to work with SolarWinds backend"""

import importlib
import logging
import os
import sys
Expand All @@ -10,6 +11,12 @@
OTEL_PROPAGATORS,
OTEL_TRACES_EXPORTER,
)
from opentelemetry.instrumentation.dependencies import (
get_dist_dependency_conflicts,
)
from opentelemetry.instrumentation.environment_variables import (
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS,
)
from opentelemetry.instrumentation.propagators import (
set_global_response_propagator,
)
Expand Down Expand Up @@ -266,6 +273,87 @@ def _initialize_solarwinds_reporter(

return NoopReporter(**reporter_kwargs)

def _add_all_instrumented_python_framework_versions(
self,
version_keys,
) -> dict:
"""Updates version_keys with versions of Python frameworks that have been
instrumented with installed (bootstrapped) OTel instrumentation libraries.
Borrowed from opentelemetry-instrumentation sitecustomize.

Example output:
{
"Python.Urllib.Version": "3.9",
"Python.Requests.Version": "2.28.1",
"Python.Django.Version": "4.1.4",
"Python.Psycopg2.Version": "2.9.5 (dt dec pq3 ext lo64)",
"Python.Sqlite3.Version": "3.34.1",
"Python.Logging.Version": "0.5.1.2",
}
"""
package_to_exclude = os.environ.get(
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS, []
)
if isinstance(package_to_exclude, str):
package_to_exclude = package_to_exclude.split(",")
package_to_exclude = [x.strip() for x in package_to_exclude]

for entry_point in iter_entry_points("opentelemetry_instrumentor"):
if entry_point.name in package_to_exclude:
logger.debug(
"Skipping version lookup for library %s because excluded",
entry_point.name,
)
continue

try:
conflict = get_dist_dependency_conflicts(entry_point.dist)
if conflict:
logger.warning(
"Version lookup for library %s skipped due to conflict: %s",
entry_point.name,
conflict,
)
continue
except Exception as ex: # pylint: disable=broad-except
logger.warning(
"Version conflict check of %s failed, so skipping: %s",
entry_point.name,
ex,
)
continue

instr_key = f"Python.{entry_point.name.capitalize()}.Version"
try:
# urllib has a rich complex history
if entry_point.name == "urllib":
importlib.import_module(f"{entry_point.name}.request")
else:
importlib.import_module(entry_point.name)

# some Python frameworks just don't have __version__
if entry_point.name == "urllib":
version_keys[instr_key] = sys.modules[
f"{entry_point.name}.request"
].__version__
elif entry_point.name == "sqlite3":
version_keys[instr_key] = sys.modules[
entry_point.name
].sqlite_version
else:
version_keys[instr_key] = sys.modules[
entry_point.name
].__version__
except (AttributeError, ImportError) as ex:
# could not import package for whatever reason
logger.warning(
"Version lookup of %s failed, so skipping: %s",
entry_point.name,
ex,
)

return version_keys

# pylint: disable=too-many-locals
def _report_init_event(
self,
Expand Down Expand Up @@ -324,6 +412,10 @@ def _report_init_event(
] = Config.getVersionString()
version_keys["APM.Extension.Version"] = Config.getVersionString()

version_keys = self._add_all_instrumented_python_framework_versions(
version_keys
)

if keys:
version_keys.update(keys)

Expand Down