We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Support emitting logs to stdout
**Context**
We currently deploy Lemur in a containerized environment and would like to have Lemur emit JSON logs to `stdout` instead of in a rotating log file. Our reason is that it is generally regarded as a best practice when running in a container to write all logs to stdout and have the orchestrator do the collection. This is something we really embrace in our company and our tooling is built with this assumption in mind.

**Issue**
We tried configuring Lemur with `LOG_FILE = "/dev/stdout"` but the `RotatingFileHandler` throws an exception when attempting a `os.seek(...)`  on the file descriptor. Unstructured logs are already emitted on `stderr` but that unfortunately doesn't address our need since splitting those logs is essentially impossible.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lemur/factory.py`
Content:
```
1 """
2 .. module: lemur.factory
3     :platform: Unix
4     :synopsis: This module contains all the needed functions to allow
5     the factory app creation.
6 
7     :copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
8     :license: Apache, see LICENSE for more details.
9 .. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
10 
11 """
12 import os
13 import importlib
14 import logmatic
15 import errno
16 import pkg_resources
17 import socket
18 
19 from logging import Formatter, StreamHandler
20 from logging.handlers import RotatingFileHandler
21 
22 from flask import Flask
23 from flask_replicated import FlaskReplicated
24 
25 import sentry_sdk
26 from sentry_sdk.integrations.celery import CeleryIntegration
27 from sentry_sdk.integrations.redis import RedisIntegration
28 from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
29 from sentry_sdk.integrations.flask import FlaskIntegration
30 
31 from lemur.certificates.hooks import activate_debug_dump
32 from lemur.common.health import mod as health
33 from lemur.extensions import db, migrate, principal, smtp_mail, metrics, cors
34 
35 
36 DEFAULT_BLUEPRINTS = (health,)
37 
38 API_VERSION = 1
39 
40 
41 def create_app(app_name=None, blueprints=None, config=None):
42     """
43     Lemur application factory
44 
45     :param config:
46     :param app_name:
47     :param blueprints:
48     :return:
49     """
50     if not blueprints:
51         blueprints = DEFAULT_BLUEPRINTS
52     else:
53         blueprints = blueprints + DEFAULT_BLUEPRINTS
54 
55     if not app_name:
56         app_name = __name__
57 
58     app = Flask(app_name)
59     configure_app(app, config)
60     configure_blueprints(app, blueprints)
61     configure_extensions(app)
62     configure_logging(app)
63     configure_database(app)
64     install_plugins(app)
65 
66     @app.teardown_appcontext
67     def teardown(exception=None):
68         if db.session:
69             db.session.remove()
70 
71     return app
72 
73 
74 def from_file(file_path, silent=False):
75     """
76     Updates the values in the config from a Python file.  This function
77     behaves as if the file was imported as module with the
78 
79     :param file_path:
80     :param silent:
81     """
82     module_spec = importlib.util.spec_from_file_location("config", file_path)
83     d = importlib.util.module_from_spec(module_spec)
84 
85     try:
86         with open(file_path) as config_file:
87             exec(  # nosec: config file safe
88                 compile(config_file.read(), file_path, "exec"), d.__dict__
89             )
90     except IOError as e:
91         if silent and e.errno in (errno.ENOENT, errno.EISDIR):
92             return False
93         e.strerror = "Unable to load configuration file (%s)" % e.strerror
94         raise
95     return d
96 
97 
98 def configure_app(app, config=None):
99     """
100     Different ways of configuration
101 
102     :param app:
103     :param config:
104     :return:
105     """
106     # respect the config first
107     if config and config != "None":
108         app.config["CONFIG_PATH"] = config
109         app.config.from_object(from_file(config))
110     else:
111         try:
112             app.config.from_envvar("LEMUR_CONF")
113         except RuntimeError:
114             # look in default paths
115             if os.path.isfile(os.path.expanduser("~/.lemur/lemur.conf.py")):
116                 app.config.from_object(
117                     from_file(os.path.expanduser("~/.lemur/lemur.conf.py"))
118                 )
119             else:
120                 app.config.from_object(
121                     from_file(
122                         os.path.join(
123                             os.path.dirname(os.path.realpath(__file__)),
124                             "default.conf.py",
125                         )
126                     )
127                 )
128 
129     # we don't use this
130     app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
131 
132 
133 def configure_extensions(app):
134     """
135     Attaches and configures any needed flask extensions
136     to our app.
137 
138     :param app:
139     """
140     db.init_app(app)
141     migrate.init_app(app, db)
142     principal.init_app(app)
143     smtp_mail.init_app(app)
144     metrics.init_app(app)
145 
146     # the legacy Raven[flask] relied on SENTRY_CONFIG
147     if app.config.get("SENTRY_DSN", None) or app.config.get("SENTRY_CONFIG", None):
148         # priority given to SENTRY_DSN
149         sentry_dsn = app.config.get("SENTRY_DSN", None) or app.config["SENTRY_CONFIG"]['dsn']
150         sentry_sdk.init(
151             dsn=sentry_dsn,
152             integrations=[SqlalchemyIntegration(),
153                           CeleryIntegration(),
154                           RedisIntegration(),
155                           FlaskIntegration()],
156             # associating users to errors
157             send_default_pii=True,
158             shutdown_timeout=60,
159             environment=app.config.get("LEMUR_ENV", ''),
160         )
161 
162     if app.config["CORS"]:
163         app.config["CORS_HEADERS"] = "Content-Type"
164         cors.init_app(
165             app,
166             resources=r"/api/*",
167             headers="Content-Type",
168             origin="*",
169             supports_credentials=True,
170         )
171 
172 
173 def configure_blueprints(app, blueprints):
174     """
175     We prefix our APIs with their given version so that we can support
176     multiple concurrent API versions.
177 
178     :param app:
179     :param blueprints:
180     """
181     for blueprint in blueprints:
182         app.register_blueprint(blueprint, url_prefix="/api/{0}".format(API_VERSION))
183 
184 
185 def configure_database(app):
186     if app.config.get("SQLALCHEMY_ENABLE_FLASK_REPLICATED"):
187         FlaskReplicated(app)
188 
189 
190 def configure_logging(app):
191     """
192     Sets up application wide logging.
193 
194     :param app:
195     """
196     handler = RotatingFileHandler(
197         app.config.get("LOG_FILE", "lemur.log"), maxBytes=10000000, backupCount=100
198     )
199 
200     handler.setFormatter(
201         Formatter(
202             "%(asctime)s %(levelname)s: %(message)s " "[in %(pathname)s:%(lineno)d]"
203         )
204     )
205 
206     if app.config.get("LOG_JSON", False):
207         handler.setFormatter(
208             logmatic.JsonFormatter(extra={"hostname": socket.gethostname()})
209         )
210 
211     handler.setLevel(app.config.get("LOG_LEVEL", "DEBUG"))
212     app.logger.setLevel(app.config.get("LOG_LEVEL", "DEBUG"))
213     app.logger.addHandler(handler)
214 
215     stream_handler = StreamHandler()
216     stream_handler.setLevel(app.config.get("LOG_LEVEL", "DEBUG"))
217     app.logger.addHandler(stream_handler)
218 
219     if app.config.get("DEBUG_DUMP", False):
220         activate_debug_dump()
221 
222 
223 def install_plugins(app):
224     """
225     Installs new issuers that are not currently bundled with Lemur.
226 
227     :param app:
228     :return:
229     """
230     from lemur.plugins import plugins
231     from lemur.plugins.base import register
232 
233     # entry_points={
234     #    'lemur.plugins': [
235     #         'verisign = lemur_verisign.plugin:VerisignPlugin'
236     #     ],
237     # },
238     for ep in pkg_resources.iter_entry_points("lemur.plugins"):
239         try:
240             plugin = ep.load()
241         except Exception:
242             import traceback
243 
244             app.logger.error(
245                 "Failed to load plugin %r:\n%s\n" % (ep.name, traceback.format_exc())
246             )
247         else:
248             register(plugin)
249 
250     # ensure that we have some way to notify
251     with app.app_context():
252         slug = app.config.get("LEMUR_DEFAULT_NOTIFICATION_PLUGIN", "email-notification")
253         try:
254             plugins.get(slug)
255         except KeyError:
256             raise Exception(
257                 "Unable to location notification plugin: {slug}. Ensure that "
258                 "LEMUR_DEFAULT_NOTIFICATION_PLUGIN is set to a valid and installed notification plugin.".format(
259                     slug=slug
260                 )
261             )
262
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lemur/factory.py b/lemur/factory.py
--- a/lemur/factory.py
+++ b/lemur/factory.py
@@ -15,6 +15,7 @@
 import errno
 import pkg_resources
 import socket
+import stat
 
 from logging import Formatter, StreamHandler
 from logging.handlers import RotatingFileHandler
@@ -193,9 +194,14 @@
 
     :param app:
     """
-    handler = RotatingFileHandler(
-        app.config.get("LOG_FILE", "lemur.log"), maxBytes=10000000, backupCount=100
-    )
+    logfile = app.config.get("LOG_FILE", "lemur.log")
+    # if the log file is a character special device file (ie. stdout/stderr),
+    # file rotation will not work and must be disabled.
+    disable_file_rotation = os.path.exists(logfile) and stat.S_ISCHR(os.stat(logfile).st_mode)
+    if disable_file_rotation:
+        handler = StreamHandler(open(logfile, 'a'))
+    else:
+        handler = RotatingFileHandler(logfile, maxBytes=10000000, backupCount=100)
 
     handler.setFormatter(
         Formatter(

{"golden_diff": "diff --git a/lemur/factory.py b/lemur/factory.py\n--- a/lemur/factory.py\n+++ b/lemur/factory.py\n@@ -15,6 +15,7 @@\n import errno\n import pkg_resources\n import socket\n+import stat\n \n from logging import Formatter, StreamHandler\n from logging.handlers import RotatingFileHandler\n@@ -193,9 +194,14 @@\n \n     :param app:\n     \"\"\"\n-    handler = RotatingFileHandler(\n-        app.config.get(\"LOG_FILE\", \"lemur.log\"), maxBytes=10000000, backupCount=100\n-    )\n+    logfile = app.config.get(\"LOG_FILE\", \"lemur.log\")\n+    # if the log file is a character special device file (ie. stdout/stderr),\n+    # file rotation will not work and must be disabled.\n+    disable_file_rotation = os.path.exists(logfile) and stat.S_ISCHR(os.stat(logfile).st_mode)\n+    if disable_file_rotation:\n+        handler = StreamHandler(open(logfile, 'a'))\n+    else:\n+        handler = RotatingFileHandler(logfile, maxBytes=10000000, backupCount=100)\n \n     handler.setFormatter(\n         Formatter(\n", "issue": "Support emitting logs to stdout\n**Context**\r\nWe currently deploy Lemur in a containerized environment and would like to have Lemur emit JSON logs to `stdout` instead of in a rotating log file. Our reason is that it is generally regarded as a best practice when running in a container to write all logs to stdout and have the orchestrator do the collection. This is something we really embrace in our company and our tooling is built with this assumption in mind.\r\n\r\n**Issue**\r\nWe tried configuring Lemur with `LOG_FILE = \"/dev/stdout\"` but the `RotatingFileHandler` throws an exception when attempting a `os.seek(...)`  on the file descriptor. Unstructured logs are already emitted on `stderr` but that unfortunately doesn't address our need since splitting those logs is essentially impossible.\n", "before_files": [{"content": "\"\"\"\n.. module: lemur.factory\n    :platform: Unix\n    :synopsis: This module contains all the needed functions to allow\n    the factory app creation.\n\n    :copyright: (c) 2018 by Netflix Inc., see AUTHORS for more\n    :license: Apache, see LICENSE for more details.\n.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>\n\n\"\"\"\nimport os\nimport importlib\nimport logmatic\nimport errno\nimport pkg_resources\nimport socket\n\nfrom logging import Formatter, StreamHandler\nfrom logging.handlers import RotatingFileHandler\n\nfrom flask import Flask\nfrom flask_replicated import FlaskReplicated\n\nimport sentry_sdk\nfrom sentry_sdk.integrations.celery import CeleryIntegration\nfrom sentry_sdk.integrations.redis import RedisIntegration\nfrom sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration\nfrom sentry_sdk.integrations.flask import FlaskIntegration\n\nfrom lemur.certificates.hooks import activate_debug_dump\nfrom lemur.common.health import mod as health\nfrom lemur.extensions import db, migrate, principal, smtp_mail, metrics, cors\n\n\nDEFAULT_BLUEPRINTS = (health,)\n\nAPI_VERSION = 1\n\n\ndef create_app(app_name=None, blueprints=None, config=None):\n    \"\"\"\n    Lemur application factory\n\n    :param config:\n    :param app_name:\n    :param blueprints:\n    :return:\n    \"\"\"\n    if not blueprints:\n        blueprints = DEFAULT_BLUEPRINTS\n    else:\n        blueprints = blueprints + DEFAULT_BLUEPRINTS\n\n    if not app_name:\n        app_name = __name__\n\n    app = Flask(app_name)\n    configure_app(app, config)\n    configure_blueprints(app, blueprints)\n    configure_extensions(app)\n    configure_logging(app)\n    configure_database(app)\n    install_plugins(app)\n\n    @app.teardown_appcontext\n    def teardown(exception=None):\n        if db.session:\n            db.session.remove()\n\n    return app\n\n\ndef from_file(file_path, silent=False):\n    \"\"\"\n    Updates the values in the config from a Python file.  This function\n    behaves as if the file was imported as module with the\n\n    :param file_path:\n    :param silent:\n    \"\"\"\n    module_spec = importlib.util.spec_from_file_location(\"config\", file_path)\n    d = importlib.util.module_from_spec(module_spec)\n\n    try:\n        with open(file_path) as config_file:\n            exec(  # nosec: config file safe\n                compile(config_file.read(), file_path, \"exec\"), d.__dict__\n            )\n    except IOError as e:\n        if silent and e.errno in (errno.ENOENT, errno.EISDIR):\n            return False\n        e.strerror = \"Unable to load configuration file (%s)\" % e.strerror\n        raise\n    return d\n\n\ndef configure_app(app, config=None):\n    \"\"\"\n    Different ways of configuration\n\n    :param app:\n    :param config:\n    :return:\n    \"\"\"\n    # respect the config first\n    if config and config != \"None\":\n        app.config[\"CONFIG_PATH\"] = config\n        app.config.from_object(from_file(config))\n    else:\n        try:\n            app.config.from_envvar(\"LEMUR_CONF\")\n        except RuntimeError:\n            # look in default paths\n            if os.path.isfile(os.path.expanduser(\"~/.lemur/lemur.conf.py\")):\n                app.config.from_object(\n                    from_file(os.path.expanduser(\"~/.lemur/lemur.conf.py\"))\n                )\n            else:\n                app.config.from_object(\n                    from_file(\n                        os.path.join(\n                            os.path.dirname(os.path.realpath(__file__)),\n                            \"default.conf.py\",\n                        )\n                    )\n                )\n\n    # we don't use this\n    app.config[\"SQLALCHEMY_TRACK_MODIFICATIONS\"] = False\n\n\ndef configure_extensions(app):\n    \"\"\"\n    Attaches and configures any needed flask extensions\n    to our app.\n\n    :param app:\n    \"\"\"\n    db.init_app(app)\n    migrate.init_app(app, db)\n    principal.init_app(app)\n    smtp_mail.init_app(app)\n    metrics.init_app(app)\n\n    # the legacy Raven[flask] relied on SENTRY_CONFIG\n    if app.config.get(\"SENTRY_DSN\", None) or app.config.get(\"SENTRY_CONFIG\", None):\n        # priority given to SENTRY_DSN\n        sentry_dsn = app.config.get(\"SENTRY_DSN\", None) or app.config[\"SENTRY_CONFIG\"]['dsn']\n        sentry_sdk.init(\n            dsn=sentry_dsn,\n            integrations=[SqlalchemyIntegration(),\n                          CeleryIntegration(),\n                          RedisIntegration(),\n                          FlaskIntegration()],\n            # associating users to errors\n            send_default_pii=True,\n            shutdown_timeout=60,\n            environment=app.config.get(\"LEMUR_ENV\", ''),\n        )\n\n    if app.config[\"CORS\"]:\n        app.config[\"CORS_HEADERS\"] = \"Content-Type\"\n        cors.init_app(\n            app,\n            resources=r\"/api/*\",\n            headers=\"Content-Type\",\n            origin=\"*\",\n            supports_credentials=True,\n        )\n\n\ndef configure_blueprints(app, blueprints):\n    \"\"\"\n    We prefix our APIs with their given version so that we can support\n    multiple concurrent API versions.\n\n    :param app:\n    :param blueprints:\n    \"\"\"\n    for blueprint in blueprints:\n        app.register_blueprint(blueprint, url_prefix=\"/api/{0}\".format(API_VERSION))\n\n\ndef configure_database(app):\n    if app.config.get(\"SQLALCHEMY_ENABLE_FLASK_REPLICATED\"):\n        FlaskReplicated(app)\n\n\ndef configure_logging(app):\n    \"\"\"\n    Sets up application wide logging.\n\n    :param app:\n    \"\"\"\n    handler = RotatingFileHandler(\n        app.config.get(\"LOG_FILE\", \"lemur.log\"), maxBytes=10000000, backupCount=100\n    )\n\n    handler.setFormatter(\n        Formatter(\n            \"%(asctime)s %(levelname)s: %(message)s \" \"[in %(pathname)s:%(lineno)d]\"\n        )\n    )\n\n    if app.config.get(\"LOG_JSON\", False):\n        handler.setFormatter(\n            logmatic.JsonFormatter(extra={\"hostname\": socket.gethostname()})\n        )\n\n    handler.setLevel(app.config.get(\"LOG_LEVEL\", \"DEBUG\"))\n    app.logger.setLevel(app.config.get(\"LOG_LEVEL\", \"DEBUG\"))\n    app.logger.addHandler(handler)\n\n    stream_handler = StreamHandler()\n    stream_handler.setLevel(app.config.get(\"LOG_LEVEL\", \"DEBUG\"))\n    app.logger.addHandler(stream_handler)\n\n    if app.config.get(\"DEBUG_DUMP\", False):\n        activate_debug_dump()\n\n\ndef install_plugins(app):\n    \"\"\"\n    Installs new issuers that are not currently bundled with Lemur.\n\n    :param app:\n    :return:\n    \"\"\"\n    from lemur.plugins import plugins\n    from lemur.plugins.base import register\n\n    # entry_points={\n    #    'lemur.plugins': [\n    #         'verisign = lemur_verisign.plugin:VerisignPlugin'\n    #     ],\n    # },\n    for ep in pkg_resources.iter_entry_points(\"lemur.plugins\"):\n        try:\n            plugin = ep.load()\n        except Exception:\n            import traceback\n\n            app.logger.error(\n                \"Failed to load plugin %r:\\n%s\\n\" % (ep.name, traceback.format_exc())\n            )\n        else:\n            register(plugin)\n\n    # ensure that we have some way to notify\n    with app.app_context():\n        slug = app.config.get(\"LEMUR_DEFAULT_NOTIFICATION_PLUGIN\", \"email-notification\")\n        try:\n            plugins.get(slug)\n        except KeyError:\n            raise Exception(\n                \"Unable to location notification plugin: {slug}. Ensure that \"\n                \"LEMUR_DEFAULT_NOTIFICATION_PLUGIN is set to a valid and installed notification plugin.\".format(\n                    slug=slug\n                )\n            )\n", "path": "lemur/factory.py"}], "after_files": [{"content": "\"\"\"\n.. module: lemur.factory\n    :platform: Unix\n    :synopsis: This module contains all the needed functions to allow\n    the factory app creation.\n\n    :copyright: (c) 2018 by Netflix Inc., see AUTHORS for more\n    :license: Apache, see LICENSE for more details.\n.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>\n\n\"\"\"\nimport os\nimport importlib\nimport logmatic\nimport errno\nimport pkg_resources\nimport socket\nimport stat\n\nfrom logging import Formatter, StreamHandler\nfrom logging.handlers import RotatingFileHandler\n\nfrom flask import Flask\nfrom flask_replicated import FlaskReplicated\n\nimport sentry_sdk\nfrom sentry_sdk.integrations.celery import CeleryIntegration\nfrom sentry_sdk.integrations.redis import RedisIntegration\nfrom sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration\nfrom sentry_sdk.integrations.flask import FlaskIntegration\n\nfrom lemur.certificates.hooks import activate_debug_dump\nfrom lemur.common.health import mod as health\nfrom lemur.extensions import db, migrate, principal, smtp_mail, metrics, cors\n\n\nDEFAULT_BLUEPRINTS = (health,)\n\nAPI_VERSION = 1\n\n\ndef create_app(app_name=None, blueprints=None, config=None):\n    \"\"\"\n    Lemur application factory\n\n    :param config:\n    :param app_name:\n    :param blueprints:\n    :return:\n    \"\"\"\n    if not blueprints:\n        blueprints = DEFAULT_BLUEPRINTS\n    else:\n        blueprints = blueprints + DEFAULT_BLUEPRINTS\n\n    if not app_name:\n        app_name = __name__\n\n    app = Flask(app_name)\n    configure_app(app, config)\n    configure_blueprints(app, blueprints)\n    configure_extensions(app)\n    configure_logging(app)\n    configure_database(app)\n    install_plugins(app)\n\n    @app.teardown_appcontext\n    def teardown(exception=None):\n        if db.session:\n            db.session.remove()\n\n    return app\n\n\ndef from_file(file_path, silent=False):\n    \"\"\"\n    Updates the values in the config from a Python file.  This function\n    behaves as if the file was imported as module with the\n\n    :param file_path:\n    :param silent:\n    \"\"\"\n    module_spec = importlib.util.spec_from_file_location(\"config\", file_path)\n    d = importlib.util.module_from_spec(module_spec)\n\n    try:\n        with open(file_path) as config_file:\n            exec(  # nosec: config file safe\n                compile(config_file.read(), file_path, \"exec\"), d.__dict__\n            )\n    except IOError as e:\n        if silent and e.errno in (errno.ENOENT, errno.EISDIR):\n            return False\n        e.strerror = \"Unable to load configuration file (%s)\" % e.strerror\n        raise\n    return d\n\n\ndef configure_app(app, config=None):\n    \"\"\"\n    Different ways of configuration\n\n    :param app:\n    :param config:\n    :return:\n    \"\"\"\n    # respect the config first\n    if config and config != \"None\":\n        app.config[\"CONFIG_PATH\"] = config\n        app.config.from_object(from_file(config))\n    else:\n        try:\n            app.config.from_envvar(\"LEMUR_CONF\")\n        except RuntimeError:\n            # look in default paths\n            if os.path.isfile(os.path.expanduser(\"~/.lemur/lemur.conf.py\")):\n                app.config.from_object(\n                    from_file(os.path.expanduser(\"~/.lemur/lemur.conf.py\"))\n                )\n            else:\n                app.config.from_object(\n                    from_file(\n                        os.path.join(\n                            os.path.dirname(os.path.realpath(__file__)),\n                            \"default.conf.py\",\n                        )\n                    )\n                )\n\n    # we don't use this\n    app.config[\"SQLALCHEMY_TRACK_MODIFICATIONS\"] = False\n\n\ndef configure_extensions(app):\n    \"\"\"\n    Attaches and configures any needed flask extensions\n    to our app.\n\n    :param app:\n    \"\"\"\n    db.init_app(app)\n    migrate.init_app(app, db)\n    principal.init_app(app)\n    smtp_mail.init_app(app)\n    metrics.init_app(app)\n\n    # the legacy Raven[flask] relied on SENTRY_CONFIG\n    if app.config.get(\"SENTRY_DSN\", None) or app.config.get(\"SENTRY_CONFIG\", None):\n        # priority given to SENTRY_DSN\n        sentry_dsn = app.config.get(\"SENTRY_DSN\", None) or app.config[\"SENTRY_CONFIG\"]['dsn']\n        sentry_sdk.init(\n            dsn=sentry_dsn,\n            integrations=[SqlalchemyIntegration(),\n                          CeleryIntegration(),\n                          RedisIntegration(),\n                          FlaskIntegration()],\n            # associating users to errors\n            send_default_pii=True,\n            shutdown_timeout=60,\n            environment=app.config.get(\"LEMUR_ENV\", ''),\n        )\n\n    if app.config[\"CORS\"]:\n        app.config[\"CORS_HEADERS\"] = \"Content-Type\"\n        cors.init_app(\n            app,\n            resources=r\"/api/*\",\n            headers=\"Content-Type\",\n            origin=\"*\",\n            supports_credentials=True,\n        )\n\n\ndef configure_blueprints(app, blueprints):\n    \"\"\"\n    We prefix our APIs with their given version so that we can support\n    multiple concurrent API versions.\n\n    :param app:\n    :param blueprints:\n    \"\"\"\n    for blueprint in blueprints:\n        app.register_blueprint(blueprint, url_prefix=\"/api/{0}\".format(API_VERSION))\n\n\ndef configure_database(app):\n    if app.config.get(\"SQLALCHEMY_ENABLE_FLASK_REPLICATED\"):\n        FlaskReplicated(app)\n\n\ndef configure_logging(app):\n    \"\"\"\n    Sets up application wide logging.\n\n    :param app:\n    \"\"\"\n    logfile = app.config.get(\"LOG_FILE\", \"lemur.log\")\n    # if the log file is a character special device file (ie. stdout/stderr),\n    # file rotation will not work and must be disabled.\n    disable_file_rotation = os.path.exists(logfile) and stat.S_ISCHR(os.stat(logfile).st_mode)\n    if disable_file_rotation:\n        handler = StreamHandler(open(logfile, 'a'))\n    else:\n        handler = RotatingFileHandler(logfile, maxBytes=10000000, backupCount=100)\n\n    handler.setFormatter(\n        Formatter(\n            \"%(asctime)s %(levelname)s: %(message)s \" \"[in %(pathname)s:%(lineno)d]\"\n        )\n    )\n\n    if app.config.get(\"LOG_JSON\", False):\n        handler.setFormatter(\n            logmatic.JsonFormatter(extra={\"hostname\": socket.gethostname()})\n        )\n\n    handler.setLevel(app.config.get(\"LOG_LEVEL\", \"DEBUG\"))\n    app.logger.setLevel(app.config.get(\"LOG_LEVEL\", \"DEBUG\"))\n    app.logger.addHandler(handler)\n\n    stream_handler = StreamHandler()\n    stream_handler.setLevel(app.config.get(\"LOG_LEVEL\", \"DEBUG\"))\n    app.logger.addHandler(stream_handler)\n\n    if app.config.get(\"DEBUG_DUMP\", False):\n        activate_debug_dump()\n\n\ndef install_plugins(app):\n    \"\"\"\n    Installs new issuers that are not currently bundled with Lemur.\n\n    :param app:\n    :return:\n    \"\"\"\n    from lemur.plugins import plugins\n    from lemur.plugins.base import register\n\n    # entry_points={\n    #    'lemur.plugins': [\n    #         'verisign = lemur_verisign.plugin:VerisignPlugin'\n    #     ],\n    # },\n    for ep in pkg_resources.iter_entry_points(\"lemur.plugins\"):\n        try:\n            plugin = ep.load()\n        except Exception:\n            import traceback\n\n            app.logger.error(\n                \"Failed to load plugin %r:\\n%s\\n\" % (ep.name, traceback.format_exc())\n            )\n        else:\n            register(plugin)\n\n    # ensure that we have some way to notify\n    with app.app_context():\n        slug = app.config.get(\"LEMUR_DEFAULT_NOTIFICATION_PLUGIN\", \"email-notification\")\n        try:\n            plugins.get(slug)\n        except KeyError:\n            raise Exception(\n                \"Unable to location notification plugin: {slug}. Ensure that \"\n                \"LEMUR_DEFAULT_NOTIFICATION_PLUGIN is set to a valid and installed notification plugin.\".format(\n                    slug=slug\n                )\n            )\n", "path": "lemur/factory.py"}]}