def test_config(): sha = "4de21f8ea228a082d4f039c0c991ee41dfb6f9d8" try: Config.set(revision_sha=sha) assert scout_config.value("revision_sha") == sha finally: scout_config.reset_all()
def app_with_scout(redis_conn, scout_config=None): """ Context manager that configures a Huey app with Scout installed. """ # Enable Scout by default in tests. if scout_config is None: scout_config = {} scout_config.setdefault("monitor", True) scout_config["core_agent_launch"] = False # Reset global state scout_apm.rq.install_attempted = False scout_apm.rq.installed = None # Setup according to https://docs.scoutapm.com/#rq # Using job_class argument to Queue Config.set(**scout_config) queue = Queue(name="myqueue", connection=redis_conn) worker = scout_apm.rq.SimpleWorker([queue], connection=queue.connection) App = namedtuple("App", ["queue", "worker"]) try: yield App(queue=queue, worker=worker) finally: Config.reset_all()
def app_with_scout(config=None): """ Context manager that configures and installs the Scout plugin for Bottle. """ # Enable Scout by default in tests. if config is None: config = {"SCOUT_MONITOR": True} # Disable running the agent. config["SCOUT_CORE_AGENT_LAUNCH"] = False # Setup according to http://help.apm.scoutapp.com/#django with override_settings(**config): # Prevent durable changes to MIDDLEWARE and MIDDLEWARE_CLASSES by # replacing them with a copy of their value. for name in ["MIDDLEWARE", "MIDDLEWARE_CLASSES"]: try: value = getattr(settings, name) except AttributeError: pass else: setattr(settings, name, value) # Scout settings must be overridden before inserting scout_apm.django # in INSTALLED_APPS because ScoutApmDjangoConfig.ready() accesses it. with modify_settings(INSTALLED_APPS={"prepend": "scout_apm.django"}): try: # Django initializes middleware when in creates the WSGI app. # Modifying MIDDLEWARE setting has no effect on the app. # Create a new WSGI app to account for the new middleware # that "scout_apm.django" injected. yield get_wsgi_application() finally: # Reset Scout configuration. Config.reset_all()
def app_with_scout(config=None): """ Context manager that configures and installs the Scout plugin for Bottle. """ # Enable Scout by default in tests. if config is None: config = {"SCOUT_MONITOR": True} # Disable running the agent. config["SCOUT_CORE_AGENT_LAUNCH"] = False # Setup according to http://help.apm.scoutapp.com/#flask scout = ScoutApm(app) for key, value in config.items(): app.config[key] = value try: yield app finally: # Restore original configuration. assert app.before_first_request_funcs == [scout.before_first_request] assert app.before_request_funcs == {None: [scout.process_request]} assert app.after_request_funcs == {None: [scout.process_response]} assert app.dispatch_request == scout.dispatch_request del app.before_first_request_funcs[:] del app.before_request_funcs[None][:] del app.after_request_funcs[None][:] del app.dispatch_request # Reset Scout configuration. Config.reset_all()
def app_with_scout(app=None, config=None): """ Context manager that configures a Celery app with Scout installed. """ if app is None: app = celery.Celery("tasks", broker="memory://") # Enable Scout by default in tests. if config is None: config = {"monitor": True} # Disable running the agent. config["core_agent_launch"] = False @app.task def hello(): return "Hello World!" # Setup according to https://docs.scoutapm.com/#celery Config.set(**config) scout_apm.celery.install() try: yield app finally: scout_apm.celery.uninstall() # Reset Scout configuration. Config.reset_all()
def app_with_scout(config=None): """ Context manager that configures and installs the Scout plugin for Bottle. """ # Enable Scout by default in tests. if config is None: config = {"scout.monitor": True} # Disable running the agent. config["scout.core_agent_launch"] = False # Save a reference to the original configuration and make changes in a copy. app_config = app.config.copy() # Setup according to http://help.apm.scoutapp.com/#bottle app.config.update(config) scout = ScoutPlugin() app.install(scout) try: yield app finally: # Restore original configuration and clear all caches. app.config = app_config app.reset() # Reset Scout configuration. Config.reset_all()
def app_with_scout(*, app=None, scout_config=None): """ Context manager that configures and installs the Scout plugin for a basic Starlette application. """ if scout_config is None: scout_config = {} scout_config["core_agent_launch"] = False scout_config.setdefault("monitor", True) if app is None: app = Starlette() @app.exception_handler(500) async def error(request, exc): # Always raise exceptions raise exc @app.route("/") async def home(request): return PlainTextResponse("Welcome home.") @app.route("/hello/") class HelloEndpoint(HTTPEndpoint): async def get(self, request): return PlainTextResponse("Hello World!") @app.route("/crash/") async def crash(request): raise ValueError("BØØM!") # non-ASCII @app.route("/background-jobs/") async def background_jobs(request): def sync_noop(): pass async def async_noop(): pass tasks = BackgroundTasks() tasks.add_task(sync_noop) tasks.add_task(async_noop) return PlainTextResponse("Triggering background jobs", background=tasks) # As per http://docs.scoutapm.com/#starlette Config.set(**scout_config) app.add_middleware(ScoutMiddleware) try: yield app finally: Config.reset_all()
def test_shutdown_message_disabled(capsys): report_app_metadata() # queued but thread not running try: scout_config.set(shutdown_timeout_seconds=0.1, shutdown_message_enabled=False) shutdown() finally: Config.reset_all() captured = capsys.readouterr() assert not captured.err
def test_shutdown(capsys): scout_config report_app_metadata() # queued but thread not running try: scout_config.set(shutdown_timeout_seconds=0.1) shutdown() finally: Config.reset_all() captured = capsys.readouterr() assert "Scout draining" in captured.err
def test_install_success(caplog): with mock.patch.object(CoreAgentManager, "launch"): try: installed = install(config={"monitor": True}) finally: Config.reset_all() assert installed is True assert ( "scout_apm.core", logging.DEBUG, "APM Launching on PID: %s" % os.getpid(), ) in caplog.record_tuples
def test_install_fail_monitor_false(caplog): try: installed = install(config={"monitor": False}) finally: Config.reset_all() assert installed is False assert ( "scout_apm.core", logging.INFO, ("APM Not Launching on PID: %s - Configuration 'monitor' is not true" % os.getpid()), ) in caplog.record_tuples
def app_with_scout(config=None, middleware=None, set_api=True): """ Context manager that yields a fresh Falcon app with Scout configured. """ # Enable Scout by default in tests. if config is None: config = {} config["core_agent_launch"] = False config.setdefault("monitor", True) if middleware is None: middleware = ["scout"] scout_index = middleware.index("scout") assert scout_index != -1 scout_middleware = ScoutMiddleware(config=config) middleware[scout_index] = scout_middleware app = falcon.API(middleware=middleware) if set_api: scout_middleware.set_api(app) class HomeResource(object): def on_get(self, req, resp): resp.status = falcon.HTTP_200 resp.content_type = falcon.MEDIA_TEXT resp.body = "Welcome home." def on_get_suffixed(self, req, resp): self.on_get(req, resp) resp.body = "Welcome home, suffixed." app.add_route("/", HomeResource()) app.add_route("/suffixed", HomeResource(), suffix="suffixed") class CrashResource(object): def on_get(self, req, resp): raise ValueError("BØØM!") # non-ASCII app.add_route("/crash", CrashResource()) class ErrorResource(object): def on_get(self, req, resp): raise falcon.HTTPStatus("748 Confounded by ponies") app.add_route("/error", ErrorResource()) try: yield app finally: Config.reset_all()
def app_with_scout(config=None): """ Context manager that configures and installs the Scout plugin for Bottle. """ if config is None: config = {} config["SCOUT_CORE_AGENT_LAUNCH"] = False config.setdefault("SCOUT_MONITOR", True) # Disable Flask's error page to improve debugging config.setdefault("PROPAGATE_EXCEPTIONS", True) # Basic Flask app app = flask.Flask("test_app") @app.route("/") def home(): return "Welcome home." @app.route("/hello/", methods=["GET", "OPTIONS"], provide_automatic_options=False) def hello(): if flask.request.method == "OPTIONS": return "Hello Options!" return "Hello World!" @app.route("/set-session/") def set_session(): flask.session["session_var"] = 1 return "Set session" @app.route("/crash/") def crash(): raise ValueError("BØØM!") # non-ASCII @app.route("/return-error/") def return_error(): return "Something went wrong", 503 # Setup according to https://docs.scoutapm.com/#flask ScoutApm(app) app.config.update(config) app.secret_key = "123" try: yield app finally: Config.reset_all()
def app_with_scout(scout_config=None): """ Context manager that configures and installs the Scout plugin. """ # Enable Scout by default in tests. if scout_config is None: scout_config = {} scout_config.setdefault("monitor", True) scout_config.setdefault("errors_enabled", True) Config.set(**scout_config) try: yield finally: # Reset Scout configuration. Config.reset_all()
def app_with_scout(config=None, catchall=False): """ Context manager that configures and installs the Scout plugin for Bottle. """ if config is None: config = {} # Enable Scout by default in tests. config.setdefault("scout.monitor", True) # Disable running the agent. config["scout.core_agent_launch"] = False app = Bottle(catchall=catchall) @app.route("/") def home(): return "Welcome home." @app.route("/hello/") def hello(): return "Hello World!" @app.route("/crash/") def crash(): raise ValueError("BØØM!") # non-ASCII @app.route("/return-error/") def return_error(): response.status = 503 return "Something went wrong" @app.route("/named/", name="named_route") def named(): return "Response from a named route." # Setup according to https://docs.scoutapm.com/#bottle app.config.update(config) scout = ScoutPlugin() app.install(scout) try: yield app finally: # Reset Scout configuration. Config.reset_all()
def app_with_scout(config=None): """ Context manager that configures and installs the Scout plugin for Bottle. """ # Enable Scout by default in tests. if config is None: config = {} config.setdefault("SCOUT_MONITOR", True) # Disable running the agent. config["SCOUT_CORE_AGENT_LAUNCH"] = False def home(request): return Response("Welcome home.") def hello(request): return Response("Hello World!") def crash(request): raise ValueError("BØØM!") # non-ASCII def return_error(request): return Response("Something went wrong", status=503) with Configurator() as configurator: configurator.add_route("home", "/") configurator.add_view(home, route_name="home", request_method="GET") configurator.add_route("hello", "/hello/") configurator.add_view(hello, route_name="hello") configurator.add_route("crash", "/crash/") configurator.add_view(crash, route_name="crash") configurator.add_route("return_error", "/return-error/") configurator.add_view(return_error, route_name="return_error") # Setup according to https://docs.scoutapm.com/#pyramid configurator.add_settings(**config) configurator.include("scout_apm.pyramid") app = configurator.make_wsgi_app() try: yield app finally: # Reset Scout configuration. Config.reset_all()
def app_with_scout(nameko_config=None, scout_config=None): """ Context manager that yields a fresh Nameko WSGI app with Scout configured. """ if scout_config is None: scout_config = {} scout_config["core_agent_launch"] = False scout_config.setdefault("monitor", True) Config.set(**scout_config) class Service(object): name = "myservice" scout = ScoutReporter() @http("GET", "/") def home(self, request): return "Welcome home." @http("GET", "/crash/") def crash(self, request): raise ValueError("BØØM!") # non-ASCII if nameko_config is None: nameko_config = {} # Container setup copied from Nameko's container_factory pytest fixture, # which we don't use - see pytest.ini container_cls = get_container_cls(nameko_config) container = container_cls(Service, nameko_config) try: container.start() # A bit of introspection to look inside the container and pull out the WSGI # app app = list(container.subextensions)[0].get_wsgi_app() # N.B. We're sidestepping the Nameko testing conventions # (https://docs.nameko.io/en/stable/testing.html) to make our tests more # uniform between frameworks yield app finally: container.kill() Config.reset_all()
def load_scout_apm(app, db, celery=False): logger = _get_logger(app, celery) try: if celery: import scout_apm.celery from scout_apm.api import Config Config.set(key=app.config['SCOUT_KEY'], monitor=app.config['SCOUT_MONITOR'], name=app.config['SCOUT_NAME']) scout_apm.celery.install() else: from scout_apm.flask import ScoutApm from scout_apm.flask.sqlalchemy import instrument_sqlalchemy ScoutApm(app) instrument_sqlalchemy(db) except ImportError: logger.warning('Scout APM modules not found') else: logger.info('Scout APM initialized')
def app_with_scout(scout_config=None): """ Context manager that configures and installs the Scout plugin for CherryPy. """ if scout_config is None: scout_config = {} scout_config["core_agent_launch"] = False scout_config.setdefault("monitor", True) Config.set(**scout_config) class Views(object): @cherrypy.expose def index(self, **params): # Take all params so CherryPy doesn't 404 return "Welcome home." @cherrypy.expose def hello(self): return "Hello World!" @cherrypy.expose def crash(self): raise ValueError("BØØM!") # non-ASCII @cherrypy.expose def return_error(self): cherrypy.response.status = 503 return "Something went wrong" # Serve this source file statically static_file = cherrypy.tools.staticfile.handler(__file__) app = cherrypy.Application(Views(), "/", config=None) # Setup according to https://docs.scoutapm.com/#cherrypy plugin = ScoutPlugin(cherrypy.engine) plugin.subscribe() try: yield app finally: plugin.unsubscribe() Config.reset_all()
def app_with_scout(config=None): """ Context manager that configures and installs the Scout plugin for Bottle. """ # Enable Scout by default in tests. if config is None: config = {"SCOUT_MONITOR": True} # Disable running the agent. config["SCOUT_CORE_AGENT_LAUNCH"] = False # Basic Flask app app = flask.Flask("test_app") # Enable the following for debugging exceptions: # app.config["PROPAGATE_EXCEPTIONS"] = True @app.route("/") def home(): return "Welcome home." @app.route("/hello/", methods=["GET", "OPTIONS"], provide_automatic_options=False) def hello(): if flask.request.method == "OPTIONS": return "Hello Options!" return "Hello World!" @app.route("/crash/") def crash(): raise ValueError("BØØM!") # non-ASCII # Setup according to https://docs.scoutapm.com/#flask ScoutApm(app) app.config.update(config) try: yield app finally: Config.reset_all()
def app_with_scout(config=None): """ Context manager that configures and installs the Scout plugin for Bottle. """ # Enable Scout by default in tests. if config is None: config = {"monitor": True} # Disable running the agent. config["core_agent_launch"] = False # Setup according to https://docs.scoutapm.com/#celery Config.set(**config) scout_apm.celery.install() try: yield app finally: scout_apm.celery.uninstall() # Reset Scout configuration. Config.reset_all()
def app_with_scout(config=None): """ Context manager that configures a Dramatiq app with Scout middleware installed. """ # Enable Scout by default in tests. if config is None: config = {"monitor": True} # Disable running the agent. config["core_agent_launch"] = False broker = StubBroker() broker.emit_after("process_boot") dramatiq.set_broker(broker) @dramatiq.actor(max_retries=0) def hello(): return "Hello World!" @dramatiq.actor(max_retries=0) def fail(): raise ValueError("BØØM!") # non-ASCII worker = dramatiq.Worker(broker, worker_timeout=0) # Setup according to https://docs.scoutapm.com/#dramatiq Config.set(**config) broker.add_middleware(ScoutMiddleware(), before=broker.middleware[0].__class__) worker.start() App = namedtuple("App", ["broker", "worker", "hello", "fail"]) try: yield App(broker=broker, worker=worker, hello=hello, fail=fail) finally: worker.stop() # Reset Scout configuration. Config.reset_all()
def app_with_scout(config=None): """ Context manager that configures and installs the Scout plugin for Bottle. """ # Enable Scout by default in tests. if config is None: config = {"SCOUT_MONITOR": True} # Disable running the agent. config["SCOUT_CORE_AGENT_LAUNCH"] = False # Setup according to https://docs.scoutapm.com/#pyramid with app_configurator() as configurator: configurator.add_settings(**config) configurator.include("scout_apm.pyramid") app = configurator.make_wsgi_app() try: yield app finally: # Reset Scout configuration. Config.reset_all()
def app_with_scout(scout_config=None): """ Context manager that configures a Huey app with Scout installed. """ # Enable Scout by default in tests. if scout_config is None: scout_config = {} scout_config.setdefault("monitor", True) scout_config["core_agent_launch"] = False huey = MemoryHuey(immediate=True) @huey.task() @huey.lock_task("hello") def hello(): return "Hello World!" @huey.task() def retry_once(): if not retry_once._did_retry: retry_once._did_retry = True raise RetryTask() return "Done." retry_once._did_retry = False @huey.task() def fail(): raise ValueError("BØØM!") # non-ASCII # Setup according to https://docs.scoutapm.com/#huey Config.set(**scout_config) attach_scout(huey) App = namedtuple("App", ["huey", "hello", "retry_once", "fail"]) try: yield App(huey=huey, hello=hello, retry_once=retry_once, fail=fail) finally: Config.reset_all()
def test_application_root(): """ A BASE_DIR setting is mapped to the application_root config parameter. Django doesn't have a BASE_DIR setting. However the default project template creates it in order to define other settings. As a consequence, most Django projets have it. """ base_dir = os.path.dirname(__file__) with override_settings(BASE_DIR=base_dir): with app_with_scout(): assert Config().value("application_root") == base_dir
def app_with_scout(scout_config=None): """ Context manager that yields the global Hug app with Scout configured. """ global scout_integrated if scout_config is None: scout_config = {} scout_config["core_agent_launch"] = False scout_config.setdefault("monitor", True) Config.set(**scout_config) if not scout_integrated: integrate_scout(__name__, config={}) scout_integrated = True try: # Hug attaches magic names to the current module when you use the @hug # decorators, we're interested in the WSGI app: yield __hug_wsgi__ # noqa: F821 finally: Config.reset_all()
sentry_sdk.init( sentry_url, environment=_config["env"], integrations=[SqlalchemyIntegration(), AioHttpIntegration()], release=f"{{ cookiecutter.package_name }}@{app_version}", ) app.add_middleware(SentryAsgiMiddleware) if scout_config := _config.get("scout"): # pragma: no cover from scout_apm.api import Config from scout_apm.async_.starlette import ScoutMiddleware Config.set( key=scout_config["key"], name=f"{{ cookiecutter.package_name }} {_config['env'].capitalize()}", monitor=True, revision_sha=app_version, ) app.add_middleware(ScoutMiddleware) _secure_headers = SecureHeaders() app.add_middleware( CORSMiddleware, allow_origins="*", allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.add_middleware(GZipMiddleware)
def test_on_setting_changed_monitor(): with app_with_scout(SCOUT_MONITOR=True): assert Config().value("monitor") is True assert Config().value("monitor") is False
def test_on_setting_changed_application_root(): with app_with_scout(BASE_DIR="/tmp/foobar"): assert Config().value("application_root") == "/tmp/foobar" assert Config().value("application_root") == ""
def index(self, **params): return "Welcome home." @cherrypy.expose def hello(self, name="World"): return f"Hello {name}!" @cherrypy.expose def crash(self): raise ValueError("BØØM!") @cherrypy.expose def return_error(self): cherrypy.response.status = 503 return "Something went wrong" app = cherrypy.Application(Views(), "/", config=None) # https://docs.scoutapm.com/#cherrypy Config.set( monitor=True, key=os.environ["SCOUT_KEY"], name="Test CherryPy App", ) plugin = ScoutPlugin(cherrypy.engine) plugin.subscribe() if __name__ == '__main__': cherrypy.quickstart(Views())