Retire Jaeger driver

... because its core dependency, the jaeger-client-python library, was
retired 3 years ago[1].

The driver interface and its options are kept to make users aware of
the retirement.

[1] https://github.com/jaegertracing/jaeger-client-python

Change-Id: I323ea859d643d35b26ad1b3fc847bc802f21ca9b
This commit is contained in:
Takashi Kajinami
2025-06-11 23:05:57 +09:00
parent c242129442
commit e03de733da
6 changed files with 14 additions and 244 deletions

View File

@@ -13,144 +13,18 @@
# License for the specific language governing permissions and limitations
# under the License.
import collections
import datetime
import time
from urllib import parse as parser
from oslo_config import cfg
from oslo_serialization import jsonutils
from osprofiler import _utils as utils
from osprofiler.drivers import base
from osprofiler import exc
# TODO(tkajinam): Remove this and the deprecated options after G-release
class Jaeger(base.Driver):
def __init__(self, connection_str, project=None, service=None, host=None,
conf=cfg.CONF, **kwargs):
conf=None, **kwargs):
"""Jaeger driver for OSProfiler."""
super().__init__(connection_str, project=project,
service=service, host=host,
conf=conf, **kwargs)
try:
import jaeger_client
self.jaeger_client = jaeger_client
except ImportError:
raise exc.CommandError(
"To use OSProfiler with Uber Jaeger tracer, "
"please install `jaeger-client` library. "
"To install with pip:\n `pip install jaeger-client`."
)
parsed_url = parser.urlparse(connection_str)
cfg = {
"local_agent": {
"reporting_host": parsed_url.hostname,
"reporting_port": parsed_url.port,
},
"tags": conf.profiler_jaeger.process_tags
}
# Initialize tracer for each profiler
service_name = self._get_service_name(conf, project, service)
config = jaeger_client.Config(cfg, service_name=service_name)
self.tracer = config.initialize_tracer()
self.spans = collections.deque()
def _get_service_name(self, conf, project, service):
prefix = conf.profiler_jaeger.service_name_prefix
if prefix:
return "{}-{}-{}".format(prefix, project, service)
return "{}-{}".format(project, service)
raise exc.CommandError('Jaeger driver is no longer supported')
@classmethod
def get_name(cls):
return "jaeger"
def notify(self, payload):
if payload["name"].endswith("start"):
timestamp = datetime.datetime.strptime(payload["timestamp"],
"%Y-%m-%dT%H:%M:%S.%f")
epoch = datetime.datetime.utcfromtimestamp(0)
start_time = (timestamp - epoch).total_seconds()
# Create parent span
child_of = self.jaeger_client.SpanContext(
trace_id=utils.shorten_id(payload["base_id"]),
span_id=utils.shorten_id(payload["parent_id"]),
parent_id=None,
flags=self.jaeger_client.span.SAMPLED_FLAG
)
# Create Jaeger Tracing span
span = self.tracer.start_span(
operation_name=payload["name"].rstrip("-start"),
child_of=child_of,
tags=self.create_span_tags(payload),
start_time=start_time
)
# Replace Jaeger Tracing span_id (random id) to OSProfiler span_id
span.context.span_id = utils.shorten_id(payload["trace_id"])
self.spans.append(span)
else:
span = self.spans.pop()
# Store result of db call and function call
for call in ("db", "function"):
if payload.get("info", {}).get(call) is not None:
span.set_tag("result", payload["info"][call]["result"])
# Span error tag and log
if payload["info"].get("etype") is not None:
span.set_tag("error", True)
span.log_kv({"error.kind": payload["info"]["etype"]})
span.log_kv({"message": payload["info"]["message"]})
span.finish(finish_time=time.time())
def get_report(self, base_id):
"""Please use Jaeger Tracing UI for this task."""
return self._parse_results()
def list_traces(self, fields=None):
"""Please use Jaeger Tracing UI for this task."""
return []
def list_error_traces(self):
"""Please use Jaeger Tracing UI for this task."""
return []
def create_span_tags(self, payload):
"""Create tags for OpenTracing span.
:param info: Information from OSProfiler trace.
:returns tags: A dictionary contains standard tags
from OpenTracing sematic conventions,
and some other custom tags related to http, db calls.
"""
tags = {}
info = payload["info"]
if info.get("db"):
# DB calls
tags["db.statement"] = info["db"]["statement"]
tags["db.params"] = jsonutils.dumps(info["db"]["params"])
elif info.get("request"):
# WSGI call
tags["http.path"] = info["request"]["path"]
tags["http.query"] = info["request"]["query"]
tags["http.method"] = info["request"]["method"]
tags["http.scheme"] = info["request"]["scheme"]
elif info.get("function"):
# RPC, function calls
if "args" in info["function"]:
tags["args"] = info["function"]["args"]
if "kwargs" in info["function"]:
tags["kwargs"] = info["function"]["kwargs"]
tags["name"] = info["function"]["name"]
return tags

View File

@@ -194,6 +194,8 @@ _jaegerprofiler_opt_group = cfg.OptGroup(
_service_name_prefix = cfg.StrOpt(
"service_name_prefix",
deprecated_for_removal=True,
deprecated_reason="Jager driver is no longer supported",
help="""
Set service name prefix to Jaeger service name.
""")
@@ -201,6 +203,8 @@ Set service name prefix to Jaeger service name.
_process_tags = cfg.DictOpt(
"process_tags",
default={},
deprecated_for_removal=True,
deprecated_reason="Jager driver is no longer supported",
help="""
Set process tracer tags.
""")

View File

@@ -1,110 +0,0 @@
# Copyright 2018 Fujitsu Ltd.
# All Rights Reserved.
#
# 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 unittest import mock
from oslo_config import cfg
from osprofiler.drivers import jaeger
from osprofiler import opts
from osprofiler.tests import test
from jaeger_client import Config
class JaegerTestCase(test.TestCase):
def setUp(self):
super().setUp()
opts.set_defaults(cfg.CONF)
cfg.CONF.set_default(
"process_tags", "k1:v1,k2:v2", "profiler_jaeger")
self.payload_start = {
"name": "api-start",
"base_id": "4e3e0ec6-2938-40b1-8504-09eb1d4b0dee",
"trace_id": "1c089ea8-28fe-4f3d-8c00-f6daa2bc32f1",
"parent_id": "e2715537-3d1c-4f0c-b3af-87355dc5fc5b",
"timestamp": "2018-05-03T04:31:51.781381",
"info": {
"host": "test"
}
}
self.payload_stop = {
"name": "api-stop",
"base_id": "4e3e0ec6-2938-40b1-8504-09eb1d4b0dee",
"trace_id": "1c089ea8-28fe-4f3d-8c00-f6daa2bc32f1",
"parent_id": "e2715537-3d1c-4f0c-b3af-87355dc5fc5b",
"timestamp": "2018-05-03T04:31:51.781381",
"info": {
"host": "test",
"function": {
"result": 1
}
}
}
# Force to False as if already initialized, tracer will be None.
# see: jaeger_client/config.py#L374
Config._initialized = False
self.driver = jaeger.Jaeger("jaeger://127.0.0.1:6831",
project="nova", service="api",
conf=cfg.CONF)
@mock.patch("osprofiler._utils.shorten_id")
def test_notify_start(self, mock_shorten_id):
self.driver.notify(self.payload_start)
calls = [
mock.call(self.payload_start["base_id"]),
mock.call(self.payload_start["parent_id"]),
mock.call(self.payload_start["trace_id"])
]
mock_shorten_id.assert_has_calls(calls, any_order=True)
@mock.patch("jaeger_client.span.Span")
@mock.patch("time.time")
def test_notify_stop(self, mock_time, mock_span):
fake_time = 1525416065.5958152
mock_time.return_value = fake_time
span = mock_span()
self.driver.spans.append(mock_span())
self.driver.notify(self.payload_stop)
mock_time.assert_called_once()
mock_time.reset_mock()
span.finish.assert_called_once_with(finish_time=fake_time)
def test_service_name_default(self):
self.assertEqual("pr1-svc1", self.driver._get_service_name(
cfg.CONF, "pr1", "svc1"))
def test_service_name_prefix(self):
cfg.CONF.set_default(
"service_name_prefix", "prx1", "profiler_jaeger")
self.assertEqual("prx1-pr1-svc1", self.driver._get_service_name(
cfg.CONF, "pr1", "svc1"))
def test_process_tags(self):
tags = self.driver.tracer.tags
# Let's remove variable tags generated by Jaeger client
del tags['hostname']
del tags['jaeger.version']
del tags['ip']
self.assertEqual({'k1': 'v1', 'k2': 'v2'}, tags)

View File

@@ -0,0 +1,6 @@
---
upgrade:
- |
The Jager driver is no longer supported and now using it raises
CommandError. This is because the jaeger-client-python library, which is
its core dependency, was already retired.

View File

@@ -29,8 +29,6 @@ packages =
[extras]
elasticsearch =
elasticsearch>=2.0.0 # Apache-2.0
jaeger =
jaeger-client>=3.8.0 # Apache-2.0
messaging =
oslo.messaging>=14.1.0 # Apache-2.0
mongo =

View File

@@ -12,8 +12,6 @@ elasticsearch>=2.0.0 # Apache-2.0
# Redis python client
redis>=2.10.0 # MIT
# For Jaeger Tracing
jaeger-client>=3.8.0 # Apache-2.0
six>=1.10.0 # MIT
# For OTLP
opentelemetry-exporter-otlp>=1.16.0 # Apache-2.0
opentelemetry-sdk>=1.16.0 # Apache-2.0