def test_docker_kubernetes_system_info_from_environ_overrides_cgroups(): # initialize agent only after overriding environment elasticapm_client = Client(metrics_interval="0ms") # mock docker/kubernetes data here to get consistent behavior if test is run in docker with mock.patch("elasticapm.utils.cgroup.get_cgroup_container_metadata" ) as mock_metadata, mock.patch( "socket.gethostname") as mock_gethostname: mock_metadata.return_value = { "container": { "id": "123" }, "kubernetes": { "pod": { "uid": "456" } } } mock_gethostname.return_value = "foo" system_info = elasticapm_client.get_system_info() assert "kubernetes" in system_info assert system_info["kubernetes"] == { "pod": { "uid": "podid", "name": "pod" }, "node": { "name": "node" }, "namespace": "namespace", } assert system_info["container"] == {"id": "123"}
def test_send_remote_failover_sync_non_transport_exception_error(should_try, http_send, caplog): should_try.return_value = True client = Client( server_url="http://example.com", service_name="app_name", secret_token="secret", transport_class="elasticapm.transport.http.Transport", metrics_interval="0ms", metrics_sets=[], ) # test error http_send.side_effect = ValueError("oopsie") with caplog.at_level("ERROR", "elasticapm.transport"): client.capture_message("foo", handled=False) client._transport.flush() record = caplog.records[0] assert client._transport.state.did_fail() assert "oopsie" in record.message # test recovery http_send.side_effect = None client.capture_message("foo", handled=False) client.close() assert not client._transport.state.did_fail() client.close()
def test_client_doesnt_flush_when_in_master_process(is_master_process, mock_queue): # when in the master process, the client should not flush the # HTTP transport is_master_process.return_value = True client = Client( server_url="http://example.com", service_name="app_name", secret_token="secret", metrics_interval="0ms" ) client.queue("x", {}, flush=True) assert mock_queue.call_count == 1 assert mock_queue.call_args[0] == ("x", {}, False)
def test_client_uses_sync_mode_when_master_process(is_master_process): # when in the master process, the client should use the non-async # HTTP transport, even if async_mode is True is_master_process.return_value = True client = Client(server_url="http://example.com", service_name="app_name", secret_token="secret", async_mode=True) transport = client._get_transport( compat.urlparse.urlparse("http://exampe.com")) assert transport.async_mode is False
def sending_elasticapm_client(request, validating_httpserver): validating_httpserver.serve_content( code=202, content='', headers={'Location': 'http://example.com/foo'}) client_config = getattr(request, 'param', {}) client_config.setdefault('server_url', validating_httpserver.url) client_config.setdefault('service_name', 'myapp') client_config.setdefault('secret_token', 'test_key') client_config.setdefault('transport_class', 'elasticapm.transport.http.Transport') client = Client(**client_config) client.httpserver = validating_httpserver yield client client.close()
def test_client_uses_sync_mode_when_master_process(self, is_master_process): # when in the master process, the client should use the non-async # HTTP transport, even if async_mode is True is_master_process.return_value = True client = Client( servers=['http://example.com'], app_name='app_name', secret_token='secret', async_mode=True, ) self.assertIsInstance( client._get_transport(urlparse.urlparse('http://exampe.com')), HTTPTransport)
def test_send_not_enabled(self, time, send_remote): time.return_value = 1328055286.51 with mock.patch.dict('os.environ', {'ELASTIC_APM_DISABLE_SEND': 'true'}): client = Client( servers=['http://example.com'], app_name='app_name', secret_token='secret', ) client.send(**{ 'foo': 'bar', }) assert not send_remote.called
def sending_elasticapm_client(request, validating_httpserver): validating_httpserver.serve_content(code=202, content="", headers={"Location": "http://example.com/foo"}) client_config = getattr(request, "param", {}) client_config.setdefault("server_url", validating_httpserver.url) client_config.setdefault("service_name", "myapp") client_config.setdefault("secret_token", "test_key") client_config.setdefault("transport_class", "elasticapm.transport.http.Transport") client_config.setdefault("span_frames_min_duration", -1) client_config.setdefault("include_paths", ("*/tests/*",)) client_config.setdefault("metrics_interval", "0ms") client = Client(**client_config) client.httpserver = validating_httpserver yield client client.close()
def get_client(): if not hasattr(settings, 'ELASTIC_APM'): return None if not settings.DEBUG or settings.ELASTIC_APM.get('DEBUG', False): return Client(settings.ELASTIC_APM, framework_name='falcon', framework_version=falcon.__version__) return None
def test_ignore_patterns(self, should_collect, mock_send): client = Client( servers=['http://example.com'], app_name='app_name', secret_token='secret', async_mode=True, transactions_ignore_patterns=['^OPTIONS', 'views.api.v2']) should_collect.return_value = False client.begin_transaction("web") client.end_transaction('OPTIONS views.healthcheck', 200) client.begin_transaction("web") client.end_transaction('GET views.users', 200) self.assertEqual(len(client.instrumentation_store), 1)
def test_custom_transport(self): client = Client( servers=['localhost'], app_name='bar', secret_token='baz', transport_class='tests.client.client_tests.DummyTransport', ) self.assertEqual(client._transport_class, DummyTransport)
def test_config_by_environment(self): with mock.patch.dict( 'os.environ', { 'ELASTIC_APM_APP_NAME': 'app', 'ELASTIC_APM_SECRET_TOKEN': 'token', 'ELASTIC_APM_GIT_REF': 'aabbccdd' }): client = Client() self.assertEqual(client.app_name, 'app') self.assertEqual(client.secret_token, 'token') self.assertEqual(client.git_ref, 'aabbccdd') self.assertEqual(client.is_send_disabled, False) with mock.patch.dict('os.environ', { 'ELASTIC_APM_DISABLE_SEND': 'true', }): client = Client() self.assertEqual(client.is_send_disabled, True)
def test_empty_transport_disables_send(): client = Client(service_name="x", transport_class=None, metrics_interval="0ms") assert len(client.config.errors) == 1 assert "TRANSPORT_CLASS" in client.config.errors assert client.config.disable_send
def test_send_remote_failover_sync(self, should_try, http_send): should_try.return_value = True client = Client( servers=['http://example.com'], app_name='app_name', secret_token='secret', async_mode=False, ) logger = mock.Mock() client.error_logger.error = logger # test error encoded_data = client.encode({'message': 'oh no'}) http_send.side_effect = TransportException('oopsie', encoded_data) client.send_remote('http://example.com/api/store', data=encoded_data) assert client.state.status == client.state.ERROR assert len(logger.call_args_list) == 2 assert 'oopsie' in logger.call_args_list[0][0][0] assert 'oh no' in logger.call_args_list[1][0][1] # test recovery http_send.side_effect = None client.send_remote('http://example.com/api/store', 'foo') assert client.state.status == client.state.ONLINE
def test_empty_processor_list(self): client = Client( servers=['http://example.com'], app_name='app_name', secret_token='secret', processors=[], ) self.assertEqual(client.processors, [])
def test_metrics_collection(self, should_collect, mock_send): client = Client( servers=['http://example.com'], app_name='app_name', secret_token='secret', ) should_collect.return_value = False for i in range(7): client.begin_transaction("transaction.test") client.end_transaction('test-transaction', 200) self.assertEqual(len(client.instrumentation_store), 7) self.assertEqual(mock_send.call_count, 0) should_collect.return_value = True client.begin_transaction("transaction.test") client.end_transaction('my-other-transaction', 200) self.assertEqual(len(client.instrumentation_store), 0) self.assertEqual(mock_send.call_count, 1)
def __init__(self, name: Optional[str] = None, **kwargs) -> None: self.name = name self.event = {} self.context = {} self.response = None # Disable all background threads except for transport kwargs["metrics_interval"] = "0ms" kwargs["central_config"] = False kwargs["cloud_provider"] = "none" kwargs["framework_name"] = "AWS Lambda" if "service_name" not in kwargs: kwargs["service_name"] = os.environ["AWS_LAMBDA_FUNCTION_NAME"] self.client = get_client() if not self.client: self.client = Client(**kwargs) if not self.client.config.debug and self.client.config.instrument and self.client.config.enabled: elasticapm.instrument()
def test_send_with_auth_header(self, time, send_remote): time.return_value = 1328055286.51 client = Client( servers=['http://example.com'], app_name='app_name', secret_token='secret', ) client.send(auth_header='foo', **{ 'foo': 'bar', }) send_remote.assert_called_once_with( url='http://example.com', data=six.b('x\x9c\xabVJ\xcb\xcfW\xb2RPJJ,R\xaa\x05\x00 \x98\x04T'), headers={ 'Content-Type': 'application/json', 'Content-Encoding': 'deflate', 'Authorization': 'foo', 'User-Agent': 'elasticapm-python/%s' % elasticapm.VERSION, }, )
def __init__(self, hide_zerorpc_frames=True, client=None, **kwargs): """Create a middleware object that can be injected in a ZeroRPC server. - hide_zerorpc_frames: modify the exception stacktrace to remove the internal zerorpc frames (True by default to make the stacktrace as readable as possible); - client: use an existing raven.Client object, otherwise one will be instantiated from the keyword arguments. """ self._elasticapm_client = client or Client(**kwargs) self._hide_zerorpc_frames = hide_zerorpc_frames
def test_config_non_string_types(): """ tests if we can handle non string types as configuration, e.g. Value types from django-configuration """ class MyValue(object): def __init__(self, content): self.content = content def __str__(self): return str(self.content) def __repr__(self): return repr(self.content) client = Client(server_url="localhost", service_name=MyValue("bar"), secret_token=MyValue("bay")) assert isinstance(client.config.secret_token, compat.string_types) assert isinstance(client.config.service_name, compat.string_types) client.close()
def __init__(self, *args, **kwargs): self.client = None if "client" in kwargs: self.client = kwargs.pop("client") elif len(args) > 0: arg = args[0] if isinstance(arg, Client): self.client = arg if not self.client: client_cls = kwargs.pop("client_cls", None) if client_cls: self.client = client_cls(*args, **kwargs) else: # In 6.0, this should raise a ValueError warnings.warn( "LoggingHandler requires a Client instance. No Client was " "received. This will result in an error starting in v6.0 " "of the agent", PendingDeprecationWarning, ) self.client = Client(*args, **kwargs) logging.Handler.__init__(self, level=kwargs.get("level", logging.NOTSET))
def sending_elasticapm_client(request, validating_httpserver): validating_httpserver.serve_content( code=202, content="", headers={"Location": "http://example.com/foo"}) client_config = getattr(request, "param", {}) client_config.setdefault("server_url", validating_httpserver.url) client_config.setdefault("service_name", "myapp") client_config.setdefault("secret_token", "test_key") client_config.setdefault("transport_class", "elasticapm.transport.http.Transport") client_config.setdefault("span_frames_min_duration", -1) client_config.setdefault("span_compression_exact_match_max_duration", "0ms") client_config.setdefault("span_compression_same_kind_max_duration", "0ms") client_config.setdefault("include_paths", ("*/tests/*", )) client_config.setdefault("metrics_interval", "0ms") client_config.setdefault("central_config", "false") client_config.setdefault("server_version", (8, 0, 0)) client = Client(**client_config) client.httpserver = validating_httpserver yield client client.close() # clear any execution context that might linger around execution_context.set_transaction(None) execution_context.set_span(None)
def test_config_by_environment(): with mock.patch.dict("os.environ", {"ELASTIC_APM_SERVICE_NAME": "envapp", "ELASTIC_APM_SECRET_TOKEN": "envtoken"}): client = Client(metrics_interval="0ms") assert client.config.service_name == "envapp" assert client.config.secret_token == "envtoken" assert client.config.disable_send is False with mock.patch.dict("os.environ", {"ELASTIC_APM_DISABLE_SEND": "true"}): client = Client(metrics_interval="0ms") assert client.config.disable_send is True client.close()
def test_client_shutdown_async(self, mock_traces_collect, mock_send): client = Client( servers=['http://example.com'], app_name='app_name', secret_token='secret', async_mode=True, ) client.send(auth_header='foo', **{ 'foo': 'bar', }) client.close() self.assertEqual(mock_traces_collect.call_count, 1) self.assertEqual(mock_send.call_count, 1)
def test_config_non_string_types(self): """ tests if we can handle non string types as configuration, e.g. Value types from django-configuration """ class MyValue(object): def __init__(self, content): self.content = content def __str__(self): return str(self.content) def __repr__(self): return repr(self.content) client = Client(servers=['localhost'], app_name=MyValue('bar'), secret_token=MyValue('bay')) assert isinstance(client.secret_token, six.string_types) assert isinstance(client.app_name, six.string_types)
def test_config_by_environment(): with mock.patch.dict('os.environ', { 'ELASTIC_APM_SERVICE_NAME': 'app', 'ELASTIC_APM_SECRET_TOKEN': 'token', }): client = Client() assert client.config.service_name == 'app' assert client.config.secret_token == 'token' assert client.config.disable_send is False with mock.patch.dict('os.environ', { 'ELASTIC_APM_DISABLE_SEND': 'true', }): client = Client() assert client.config.disable_send is True client.close()
def test_send_remote_failover_sync_stdlib(should_try, http_send): should_try.return_value = True client = Client( server_url='http://example.com', service_name='app_name', secret_token='secret', transport_class='elasticapm.transport.http.Transport', ) logger = mock.Mock() client.error_logger.error = logger # test error http_send.side_effect = ValueError('oopsie') client.send('http://example.com/api/store', **{'message': 'oh no'}) assert client.state.status == client.state.ERROR assert len(logger.call_args_list) == 1 assert 'oopsie' in logger.call_args_list[0][0][1] # test recovery http_send.side_effect = None client.send('http://example.com/api/store', **{'message': 'oh no'}) assert client.state.status == client.state.ONLINE client.close()
def test_send_remote_failover_sync_stdlib(should_try, http_send): should_try.return_value = True client = Client( server_url="http://example.com", service_name="app_name", secret_token="secret", transport_class="elasticapm.transport.http.Transport", ) logger = mock.Mock() client.error_logger.error = logger # test error http_send.side_effect = ValueError("oopsie") client.send("http://example.com/api/store", **{"message": "oh no"}) assert client.state.status == client.state.ERROR assert len(logger.call_args_list) == 1 assert "oopsie" in logger.call_args_list[0][0][1] # test recovery http_send.side_effect = None client.send("http://example.com/api/store", **{"message": "oh no"}) assert client.state.status == client.state.ONLINE client.close()
class LoggingHandler(logging.Handler): def __init__(self, *args, **kwargs): self.client = None if "client" in kwargs: self.client = kwargs.pop("client") elif len(args) > 0: arg = args[0] if isinstance(arg, Client): self.client = arg if not self.client: client_cls = kwargs.pop("client_cls", None) if client_cls: self.client = client_cls(*args, **kwargs) else: # In 6.0, this should raise a ValueError warnings.warn( "LoggingHandler requires a Client instance. No Client was " "received. This will result in an error starting in v6.0 " "of the agent", PendingDeprecationWarning, ) self.client = Client(*args, **kwargs) logging.Handler.__init__(self, level=kwargs.get("level", logging.NOTSET)) def emit(self, record): self.format(record) # Avoid typical config issues by overriding loggers behavior if record.name.startswith(("elasticapm.errors",)): sys.stderr.write(to_unicode(record.message) + "\n") return try: return self._emit(record) except Exception: sys.stderr.write("Top level ElasticAPM exception caught - failed creating log record.\n") sys.stderr.write(to_unicode(record.msg + "\n")) sys.stderr.write(to_unicode(traceback.format_exc() + "\n")) try: self.client.capture("Exception") except Exception: pass def _emit(self, record, **kwargs): data = {} for k, v in compat.iteritems(record.__dict__): if "." not in k and k not in ("culprit",): continue data[k] = v stack = getattr(record, "stack", None) if stack is True: stack = iter_stack_frames(config=self.client.config) if stack: frames = [] started = False last_mod = "" for item in stack: if isinstance(item, (list, tuple)): frame, lineno = item else: frame, lineno = item, item.f_lineno if not started: f_globals = getattr(frame, "f_globals", {}) module_name = f_globals.get("__name__", "") if last_mod.startswith("logging") and not module_name.startswith("logging"): started = True else: last_mod = module_name continue frames.append((frame, lineno)) stack = frames custom = getattr(record, "data", {}) # Add in all of the data from the record that we aren't already capturing for k in record.__dict__.keys(): if k in ( "stack", "name", "args", "msg", "levelno", "exc_text", "exc_info", "data", "created", "levelname", "msecs", "relativeCreated", ): continue if k.startswith("_"): continue custom[k] = record.__dict__[k] # If there's no exception being processed, # exc_info may be a 3-tuple of None # http://docs.python.org/library/sys.html#sys.exc_info if record.exc_info and all(record.exc_info): handler = self.client.get_handler("elasticapm.events.Exception") exception = handler.capture(self.client, exc_info=record.exc_info) else: exception = None return self.client.capture( "Message", param_message={"message": compat.text_type(record.msg), "params": record.args}, stack=stack, custom=custom, exception=exception, level=record.levelno, logger_name=record.name, **kwargs )
def filter_factory(app, global_conf, **kwargs): client = Client(**kwargs) return ElasticAPM(app, client)