Skip to content

Commit 74fe1e8

Browse files
authored
[WIP] Make Client object globally available (#1043)
* Make Client object globally available * Remove global client object on Client.close() * Fix django's get_client to use elasticapm.get_client * Clean up more client creation * CHANGELOG
1 parent c58b213 commit 74fe1e8

File tree

11 files changed

+53
-56
lines changed

11 files changed

+53
-56
lines changed

CHANGELOG.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ endif::[]
3737
[float]
3838
===== Features
3939
40+
* Add global access to Client singleton object at `elasticapm.get_client()` {pull}1043[#1043]
41+
4042
4143
[float]
4244
===== Bug fixes

elasticapm/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3030
import sys
3131

32-
from elasticapm.base import Client
32+
from elasticapm.base import Client, get_client # noqa: F401
3333
from elasticapm.conf import setup_logging # noqa: F401
3434
from elasticapm.instrumentation.control import instrument, uninstrument # noqa: F401
3535
from elasticapm.traces import ( # noqa: F401

elasticapm/base.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454

5555
__all__ = ("Client",)
5656

57+
CLIENT_SINGLETON = None
58+
5759

5860
class Client(object):
5961
"""
@@ -205,6 +207,9 @@ def __init__(self, config=None, **inline):
205207
if config.enabled:
206208
self.start_threads()
207209

210+
# Save this Client object as the global CLIENT_SINGLETON
211+
set_client(self)
212+
208213
def start_threads(self):
209214
with self._thread_starter_lock:
210215
current_pid = os.getpid()
@@ -298,6 +303,8 @@ def close(self):
298303
with self._thread_starter_lock:
299304
for _, manager in self._thread_managers.items():
300305
manager.stop_thread()
306+
global CLIENT_SINGLETON
307+
CLIENT_SINGLETON = None
301308

302309
def get_service_info(self):
303310
if self._service_info:
@@ -611,3 +618,15 @@ class DummyClient(Client):
611618

612619
def send(self, url, **kwargs):
613620
return None
621+
622+
623+
def get_client():
624+
return CLIENT_SINGLETON
625+
626+
627+
def set_client(client):
628+
global CLIENT_SINGLETON
629+
if CLIENT_SINGLETON:
630+
logger = get_logger("elasticapm")
631+
logger.debug("Client object is being set more than once")
632+
CLIENT_SINGLETON = client

elasticapm/contrib/aiohttp/__init__.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@
3333
import elasticapm
3434
from elasticapm import Client
3535

36-
CLIENT_KEY = "_elasticapm_client_instance"
37-
3836

3937
class ElasticAPM:
4038
def __init__(self, app, client=None):
@@ -43,7 +41,6 @@ def __init__(self, app, client=None):
4341
config.setdefault("framework_name", "aiohttp")
4442
config.setdefault("framework_version", aiohttp.__version__)
4543
client = Client(config=config)
46-
app[CLIENT_KEY] = client
4744
self.app = app
4845
self.client = client
4946
self.install_tracing(app, client)

elasticapm/contrib/aiohttp/middleware.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from aiohttp.web import HTTPException, Response, middleware
3333

3434
import elasticapm
35+
from elasticapm import get_client
3536
from elasticapm.conf import constants
3637
from elasticapm.contrib.aiohttp.utils import get_data_from_request, get_data_from_response
3738
from elasticapm.utils.disttracing import TraceParent
@@ -44,13 +45,10 @@ def merge_duplicate_headers(cls, headers, key):
4445

4546

4647
def tracing_middleware(app):
47-
from elasticapm.contrib.aiohttp import CLIENT_KEY # noqa
48-
4948
async def handle_request(request, handler):
50-
elasticapm_client = app.get(CLIENT_KEY)
49+
elasticapm_client = get_client()
5150
should_trace = elasticapm_client and not elasticapm_client.should_ignore_url(request.path)
5251
if should_trace:
53-
request[CLIENT_KEY] = elasticapm_client
5452
trace_parent = AioHttpTraceParent.from_headers(request.headers)
5553
elasticapm_client.begin_transaction("request", trace_parent=trace_parent)
5654
resource = request.match_info.route.resource

elasticapm/contrib/django/client.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from django.db import DatabaseError
3838
from django.http import HttpRequest
3939

40+
from elasticapm import get_client as _get_client
4041
from elasticapm.base import Client
4142
from elasticapm.conf import constants
4243
from elasticapm.contrib.django.utils import iterate_with_template_sources
@@ -49,31 +50,26 @@
4950

5051

5152
default_client_class = "elasticapm.contrib.django.DjangoClient"
52-
_client = (None, None)
5353

5454

55-
def get_client(client=None):
55+
def get_client():
5656
"""
5757
Get an ElasticAPM client.
5858
5959
:param client:
6060
:return:
6161
:rtype: elasticapm.base.Client
6262
"""
63-
global _client
64-
65-
tmp_client = client is not None
66-
if not tmp_client:
67-
config = getattr(django_settings, "ELASTIC_APM", {})
68-
client = config.get("CLIENT", default_client_class)
69-
70-
if _client[0] != client:
71-
client_class = import_string(client)
72-
instance = client_class()
73-
if not tmp_client:
74-
_client = (client, instance)
75-
return instance
76-
return _client[1]
63+
if _get_client():
64+
return _get_client()
65+
66+
config = getattr(django_settings, "ELASTIC_APM", {})
67+
client = config.get("CLIENT", default_client_class)
68+
client_class = import_string(client)
69+
instance = client_class()
70+
# `instance` will already be in elasticapm.base.CLIENT_SINGLETON due to the
71+
# `__init__()` for Client
72+
return instance
7773

7874

7975
class DjangoClient(Client):

elasticapm/contrib/flask/__init__.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
import elasticapm
4040
import elasticapm.instrumentation.control
41+
from elasticapm import get_client
4142
from elasticapm.base import Client
4243
from elasticapm.conf import constants, setup_logging
4344
from elasticapm.contrib.flask.utils import get_data_from_request, get_data_from_response
@@ -50,17 +51,6 @@
5051
logger = get_logger("elasticapm.errors.client")
5152

5253

53-
def make_client(client_cls, app, **defaults):
54-
config = app.config.get("ELASTIC_APM", {})
55-
56-
if "framework_name" not in defaults:
57-
defaults["framework_name"] = "flask"
58-
defaults["framework_version"] = getattr(flask, "__version__", "<0.7")
59-
60-
client = client_cls(config, **defaults)
61-
return client
62-
63-
6454
class ElasticAPM(object):
6555
"""
6656
Flask application for Elastic APM.
@@ -97,10 +87,18 @@ class ElasticAPM(object):
9787
def __init__(self, app=None, client=None, client_cls=Client, logging=False, **defaults):
9888
self.app = app
9989
self.logging = logging
100-
self.client_cls = client_cls
101-
self.client = client
90+
self.client = client or get_client()
10291

10392
if app:
93+
if not self.client:
94+
config = app.config.get("ELASTIC_APM", {})
95+
96+
if "framework_name" not in defaults:
97+
defaults["framework_name"] = "flask"
98+
defaults["framework_version"] = getattr(flask, "__version__", "<0.7")
99+
100+
self.client = client_cls(config, **defaults)
101+
104102
self.init_app(app, **defaults)
105103

106104
def handle_exception(self, *args, **kwargs):
@@ -125,8 +123,6 @@ def handle_exception(self, *args, **kwargs):
125123

126124
def init_app(self, app, **defaults):
127125
self.app = app
128-
if not self.client:
129-
self.client = make_client(self.client_cls, app, **defaults)
130126

131127
# 0 is a valid log level (NOTSET), so we need to check explicitly for it
132128
if self.logging or self.logging is logging.NOTSET:

elasticapm/contrib/opentracing/tracer.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,15 @@
3636
from opentracing.tracer import Tracer as TracerBase
3737

3838
import elasticapm
39-
from elasticapm import instrument, traces
39+
from elasticapm import get_client, instrument, traces
4040
from elasticapm.conf import constants
4141
from elasticapm.contrib.opentracing.span import OTSpan, OTSpanContext
4242
from elasticapm.utils import compat, disttracing
4343

4444

4545
class Tracer(TracerBase):
46-
_elasticapm_client_class = elasticapm.Client
47-
4846
def __init__(self, client_instance=None, config=None, scope_manager=None):
49-
self._agent = client_instance or self._elasticapm_client_class(config=config)
47+
self._agent = client_instance or get_client() or elasticapm.Client(config=config)
5048
if scope_manager and not isinstance(scope_manager, ThreadLocalScopeManager):
5149
warnings.warn(
5250
"Currently, the Elastic APM opentracing bridge only supports the ThreadLocalScopeManager. "

elasticapm/contrib/paste.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@
2929
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3030

3131

32+
from elasticapm import get_client
3233
from elasticapm.base import Client
3334
from elasticapm.middleware import ElasticAPM
3435

3536

3637
def filter_factory(app, global_conf, **kwargs):
37-
client = Client(**kwargs)
38+
client = get_client() or Client(**kwargs)
3839
return ElasticAPM(app, client)

elasticapm/contrib/pylons/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3030

3131

32+
from elasticapm import get_client
3233
from elasticapm.base import Client
3334
from elasticapm.middleware import ElasticAPM as Middleware
3435
from elasticapm.utils import compat
@@ -44,5 +45,5 @@ def list_from_setting(config, setting):
4445
class ElasticAPM(Middleware):
4546
def __init__(self, app, config, client_cls=Client):
4647
client_config = {key[11:]: val for key, val in compat.iteritems(config) if key.startswith("elasticapm.")}
47-
client = client_cls(**client_config)
48+
client = get_client() or client_cls(**client_config)
4849
super(ElasticAPM, self).__init__(app, client)

0 commit comments

Comments
 (0)