Skip to content

Commit

Permalink
Merge branch 'main' into openai-update-semconv-to-latest
Browse files Browse the repository at this point in the history
  • Loading branch information
lzchen authored Oct 30, 2024
2 parents 8ec2482 + 54c7ee8 commit cfe8c3a
Show file tree
Hide file tree
Showing 5 changed files with 316 additions and 20 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#2922](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2922))
- `opentelemetry-instrumentation-celery` Don't detach context without a None token
([#2927](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2927))
- `opentelemetry-exporter-prometheus-remote-write`: sort labels before exporting
([#2940](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2940))
- `opentelemetry-instrumentation-dbapi` sqlcommenter key values created from PostgreSQL, MySQL systems
([#2897](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2897))

### Breaking changes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import re
from collections import defaultdict
from itertools import chain
from typing import Dict, Sequence
from typing import Dict, Mapping, Sequence

import requests
import snappy
Expand Down Expand Up @@ -253,12 +253,14 @@ def _parse_metric(
return self._convert_to_timeseries(sample_sets, resource_labels)

def _convert_to_timeseries(
self, sample_sets: Sequence[tuple], resource_labels: Sequence
self, sample_sets: Mapping[tuple, Sequence], resource_labels: Sequence
) -> Sequence[TimeSeries]:
timeseries = []
for labels, samples in sample_sets.items():
ts = TimeSeries()
for label_name, label_value in chain(resource_labels, labels):
for label_name, label_value in sorted(
chain(resource_labels, labels)
):
# Previous implementation did not str() the names...
ts.labels.append(self._label(label_name, str(label_value)))
for value, timestamp in samples:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
PrometheusRemoteWriteMetricsExporter,
)
from opentelemetry.exporter.prometheus_remote_write.gen.types_pb2 import ( # pylint: disable=E0611
Label,
Sample,
TimeSeries,
)
from opentelemetry.sdk.metrics.export import (
Expand Down Expand Up @@ -155,6 +157,38 @@ def test_parse_metric(metric, prom_rw):
assert sample.value in values


def test_convert_to_timeseries(prom_rw):
resource_labels = (("service_name", "foo"), ("bool_value", True))
sample_sets = {
(("foo", "bar"), ("baz", 42), ("__name__", "test_histogram_tu")): [
(1, 1641946016139)
],
(("baz", "42"), ("foo", "bar")): [(4, 1641946016139)],
}
timeseries = prom_rw._convert_to_timeseries(sample_sets, resource_labels)
assert timeseries == [
TimeSeries(
labels=[
Label(name="__name__", value="test_histogram_tu"),
Label(name="baz", value="42"),
Label(name="bool_value", value="True"),
Label(name="foo", value="bar"),
Label(name="service_name", value="foo"),
],
samples=[Sample(value=1, timestamp=1641946016139)],
),
TimeSeries(
labels=[
Label(name="baz", value="42"),
Label(name="bool_value", value="True"),
Label(name="foo", value="bar"),
Label(name="service_name", value="foo"),
],
samples=[Sample(value=4, timestamp=1641946016139)],
),
]


class TestValidation(unittest.TestCase):
# Test cases to ensure exporter parameter validation works as intended
def test_valid_standard_param(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@
)
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace import SpanKind, TracerProvider, get_tracer
from opentelemetry.util._importlib_metadata import version as util_version

_DB_DRIVER_ALIASES = {
"MySQLdb": "mysqlclient",
}

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -275,6 +280,70 @@ def __init__(
self.name = ""
self.database = ""
self.connect_module = connect_module
self.commenter_data = self.calculate_commenter_data()

def _get_db_version(
self,
db_driver,
):
if db_driver in _DB_DRIVER_ALIASES:
return util_version(_DB_DRIVER_ALIASES[db_driver])
db_version = ""
try:
db_version = self.connect_module.__version__
except AttributeError:
db_version = "unknown"
return db_version

def calculate_commenter_data(
self,
):
commenter_data = {}
if not self.enable_commenter:
return commenter_data

db_driver = getattr(self.connect_module, "__name__", "unknown")
db_version = self._get_db_version(db_driver)

commenter_data = {
"db_driver": f"{db_driver}:{db_version.split(' ')[0]}",
# PEP 249-compliant drivers should have the following attributes.
# We can assume apilevel "1.0" if not given.
# We use "unknown" for others to prevent uncaught AttributeError.
# https://peps.python.org/pep-0249/#globals
"dbapi_threadsafety": getattr(
self.connect_module, "threadsafety", "unknown"
),
"dbapi_level": getattr(self.connect_module, "apilevel", "1.0"),
"driver_paramstyle": getattr(
self.connect_module, "paramstyle", "unknown"
),
}

if self.database_system == "postgresql":
if hasattr(self.connect_module, "__libpq_version__"):
libpq_version = self.connect_module.__libpq_version__
else:
libpq_version = self.connect_module.pq.__build_version__
commenter_data.update(
{
"libpq_version": libpq_version,
}
)
elif self.database_system == "mysql":
mysqlc_version = ""
if db_driver == "MySQLdb":
mysqlc_version = self.connect_module._mysql.get_client_info()
elif db_driver == "pymysql":
mysqlc_version = self.connect_module.get_client_info()

commenter_data.update(
{
"mysql_client_version": mysqlc_version,
}
)

return commenter_data

def wrapped_connection(
self,
Expand Down Expand Up @@ -427,21 +496,23 @@ def traced_execution(
if args and self._commenter_enabled:
try:
args_list = list(args)
if hasattr(self._connect_module, "__libpq_version__"):
libpq_version = self._connect_module.__libpq_version__
else:
libpq_version = (
self._connect_module.pq.__build_version__
)

commenter_data = {
# Psycopg2/framework information
"db_driver": f"psycopg2:{self._connect_module.__version__.split(' ')[0]}",
"dbapi_threadsafety": self._connect_module.threadsafety,
"dbapi_level": self._connect_module.apilevel,
"libpq_version": libpq_version,
"driver_paramstyle": self._connect_module.paramstyle,
}
# lazy capture of mysql-connector client version using cursor
if (
self._db_api_integration.database_system == "mysql"
and self._db_api_integration.connect_module.__name__
== "mysql.connector"
and not self._db_api_integration.commenter_data[
"mysql_client_version"
]
):
self._db_api_integration.commenter_data[
"mysql_client_version"
] = cursor._cnx._cmysql.get_client_info()

commenter_data = dict(
self._db_api_integration.commenter_data
)
if self._commenter_options.get(
"opentelemetry_values", True
):
Expand Down
Loading

0 comments on commit cfe8c3a

Please sign in to comment.