Compare commits

..

No commits in common. "1484d678cee3c9767558a9f0bf3600a37f261e8e" and "90999bbefbb447e57628a20b90f74822485fd605" have entirely different histories.

8 changed files with 64 additions and 99 deletions

View File

@ -79,12 +79,6 @@ Then you can install the database tables and initial entries:
$ flaschengeist install $ flaschengeist install
### Run ### Run
Flaschengeist provides a CLI, based on the flask CLI, respectivly called `flaschengeist`.
⚠️ When using the CLI for running Flaschengeist, please note that logging will happen as configured,
with the difference of the main logger will be forced to output to `stderr` and the logging level
of the CLI will override the logging level you have configured for the main logger.
$ flaschengeist run $ flaschengeist run
or with debug messages: or with debug messages:

View File

@ -1,11 +1,12 @@
"""Flaschengeist""" """Flaschengeist"""
import logging import logging
from importlib.metadata import version import pkg_resources
from pathlib import Path from pathlib import Path
from werkzeug.local import LocalProxy
__version__ = version("flaschengeist") __version__ = pkg_resources.get_distribution("flaschengeist").version
_module_path = Path(__file__).parent _module_path = Path(__file__).parent
__pdoc__ = {} __pdoc__ = {}
logger = logging.getLogger(__name__) logger: logging.Logger = LocalProxy(lambda: logging.getLogger(__name__))
__pdoc__["logger"] = "Flaschengeist's logger instance (`werkzeug.local.LocalProxy`)" __pdoc__["logger"] = "Flaschengeist's logger instance (`werkzeug.local.LocalProxy`)"

View File

@ -1,6 +1,6 @@
import enum import enum
import pkg_resources
import pkg_resources
from flask import Flask, current_app from flask import Flask, current_app
from flask_cors import CORS from flask_cors import CORS
from datetime import datetime, date from datetime import datetime, date
@ -8,11 +8,11 @@ from flask.json import JSONEncoder, jsonify
from sqlalchemy.exc import OperationalError from sqlalchemy.exc import OperationalError
from werkzeug.exceptions import HTTPException from werkzeug.exceptions import HTTPException
from flaschengeist import logger from . import logger
from flaschengeist.utils.hook import Hook from .plugins import AuthPlugin
from flaschengeist.plugins import AuthPlugin
from flaschengeist.controller import roleController
from flaschengeist.config import config, configure_app from flaschengeist.config import config, configure_app
from flaschengeist.controller import roleController
from flaschengeist.utils.hook import Hook
class CustomJSONEncoder(JSONEncoder): class CustomJSONEncoder(JSONEncoder):
@ -95,15 +95,15 @@ def install_all():
roleController.create_permissions(plugin.permissions) roleController.create_permissions(plugin.permissions)
def create_app(test_config=None): def create_app(test_config=None, cli=False):
app = Flask("flaschengeist") app = Flask(__name__)
app.json_encoder = CustomJSONEncoder app.json_encoder = CustomJSONEncoder
CORS(app) CORS(app)
with app.app_context(): with app.app_context():
from flaschengeist.database import db from flaschengeist.database import db
configure_app(app, test_config) configure_app(app, test_config, cli)
db.init_app(app) db.init_app(app)
__load_plugins(app) __load_plugins(app)

View File

@ -1,14 +1,7 @@
from os import environ
import sys
import click import click
import logging
from flask.cli import FlaskGroup, with_appcontext from flask.cli import FlaskGroup, with_appcontext
from flaschengeist import logger
from flaschengeist.app import create_app from flaschengeist.app import create_app
from flaschengeist.config import configure_logger
LOGGING_MIN = 5 # TRACE (custom)
LOGGING_MAX = logging.ERROR
def get_version(ctx, param, value): def get_version(ctx, param, value):
@ -30,37 +23,19 @@ def get_version(ctx, param, value):
ctx.exit() ctx.exit()
def configure_logger(level):
"""Reconfigure main logger"""
global logger
# Handle TRACE -> meaning enable debug even for werkzeug
if level == 5:
level = 10
logging.getLogger("werkzeug").setLevel(level)
logger.setLevel(level)
environ["FG_LOGGING"] = logging.getLevelName(level)
for h in logger.handlers:
if isinstance(h, logging.StreamHandler) and h.name == "wsgi":
h.setLevel(level)
h.setStream(sys.stderr)
@with_appcontext @with_appcontext
def verbosity(ctx, param, value): def verbosity(ctx, param, value):
"""Callback: Toggle verbosity between ERROR <-> TRACE""" """Toggle verbosity between WARNING <-> DEBUG"""
if not value or ctx.resilient_parsing: if not value or ctx.resilient_parsing:
return return
configure_logger(LOGGING_MAX - max(LOGGING_MIN, min(value * 10, LOGGING_MAX - LOGGING_MIN))) configure_logger(cli=30 - max(0, min(value * 10, 20)))
@click.group( @click.group(
cls=FlaskGroup, cls=FlaskGroup,
add_version_option=False, add_version_option=False,
add_default_commands=False, add_default_commands=False,
create_app=create_app, create_app=lambda: create_app(cli=30),
) )
@click.option( @click.option(
"--version", "--version",
@ -84,15 +59,12 @@ def cli():
def main(*args, **kwargs): def main(*args, **kwargs):
# from .plugin_cmd import plugin from .plugin_cmd import plugin
from .export_cmd import export from .export_cmd import export
from .docs_cmd import docs from .docs_cmd import docs
from .run_cmd import run from .run_cmd import run
# Override logging level cli.add_command(plugin)
environ.setdefault("FG_LOGGING", logging.getLevelName(LOGGING_MAX))
# cli.add_command(plugin)
cli.add_command(export) cli.add_command(export)
cli.add_command(docs) cli.add_command(docs)
cli.add_command(run) cli.add_command(run)

View File

@ -28,11 +28,13 @@ class PrefixMiddleware(object):
@click.pass_context @click.pass_context
def run(ctx, host, port, debug): def run(ctx, host, port, debug):
"""Run Flaschengeist using a development server.""" """Run Flaschengeist using a development server."""
from flaschengeist.config import config from flaschengeist.config import config, configure_logger
# re configure logger, as we are no logger in CLI mode
configure_logger()
current_app.wsgi_app = PrefixMiddleware(current_app.wsgi_app, prefix=config["FLASCHENGEIST"].get("root", "")) current_app.wsgi_app = PrefixMiddleware(current_app.wsgi_app, prefix=config["FLASCHENGEIST"].get("root", ""))
if debug: if debug:
environ["FLASK_DEBUG"] = "1" environ["FLASK_DEBUG"] = "1"
environ["FLASK_ENV"] = "development" environ["FLASK_ENV"] = "development"
ctx.invoke(run_command, reload=True, host=host, port=port, debugger=debug) ctx.invoke(run_command, host=host, port=port, debugger=debug)

View File

@ -41,41 +41,36 @@ def read_configuration(test_config):
update_dict(config, test_config) update_dict(config, test_config)
def configure_logger(): def configure_logger(cli=False):
"""Configure the logger global config
force_console: Force a console handler
"""
def set_level(level):
# TRACE means even with werkzeug's request traces
if isinstance(level, str) and level.lower() == "trace":
level = "DEBUG"
logger_config["loggers"]["werkzeug"] = {"level": level}
logger_config["loggers"]["flaschengeist"] = {"level": level}
logger_config["handlers"]["wsgi"]["level"] = level
# Read default config # Read default config
logger_config = toml.load(_module_path / "logging.toml") logger_config = toml.load(_module_path / "logging.toml")
if "LOGGING" in config: if "LOGGING" in config:
# Override with user config # Override with user config
update_dict(logger_config, config.get("LOGGING")) update_dict(logger_config, config.get("LOGGING"))
# Check for shortcuts # Check for shortcuts
if "level" in config["LOGGING"]: if "level" in config["LOGGING"] or isinstance(cli, int):
set_level(config["LOGGING"]["level"]) level = cli if cli and isinstance(cli, int) else config["LOGGING"]["level"]
logger_config["loggers"]["flaschengeist"] = {"level": level}
# Override logging, used e.g. by CLI logger_config["handlers"]["console"]["level"] = level
if "FG_LOGGING" in os.environ: logger_config["handlers"]["file"]["level"] = level
set_level(os.environ.get("FG_LOGGING", "CRITICAL")) if cli is True or not config["LOGGING"].get("console", True):
logger_config["handlers"]["console"]["level"] = "CRITICAL"
if not cli and isinstance(config["LOGGING"].get("file", False), str):
logger_config["root"]["handlers"].append("file")
logger_config["handlers"]["file"]["filename"] = config["LOGGING"]["file"]
Path(config["LOGGING"]["file"]).parent.mkdir(parents=True, exist_ok=True)
else:
del logger_config["handlers"]["file"]
logging.config.dictConfig(logger_config) logging.config.dictConfig(logger_config)
def configure_app(app, test_config=None): def configure_app(app, test_config=None, cli=False):
global config global config
read_configuration(test_config) read_configuration(test_config)
configure_logger() configure_logger(cli)
# Always enable this builtin plugins! # Always enable this builtin plugins!
update_dict( update_dict(
@ -89,10 +84,10 @@ def configure_app(app, test_config=None):
) )
if "secret_key" not in config["FLASCHENGEIST"]: if "secret_key" not in config["FLASCHENGEIST"]:
logger.critical("No secret key was configured, please configure one for production systems!") logger.warning("No secret key was configured, please configure one for production systems!")
raise RuntimeError("No secret key was configured") app.config["SECRET_KEY"] = "0a657b97ef546da90b2db91862ad4e29"
else:
app.config["SECRET_KEY"] = config["FLASCHENGEIST"]["secret_key"] app.config["SECRET_KEY"] = config["FLASCHENGEIST"]["secret_key"]
if test_config is not None: if test_config is not None:
config["DATABASE"]["engine"] = "sqlite" config["DATABASE"]["engine"] = "sqlite"

View File

@ -19,20 +19,15 @@ secret_key = "V3ryS3cr3t"
[LOGGING] [LOGGING]
# You can override all settings from the logging.toml here # You can override all settings from the logging.toml here
# Default: Logging to WSGI stream (commonly stderr) # E.g. override the formatters etc
#
# Logging level, possible: TRACE DEBUG INFO WARNING ERROR CRITICAL # Logging level, possible: DEBUG INFO WARNING ERROR
# On TRACE level additionally every request will get logged
level = "DEBUG" level = "DEBUG"
# Logging to a file is simple, just add the path
# If you want the logger to log to a file, you could use: # file = "/tmp/flaschengeist-debug.log"
#[LOGGING.handlers.file] file = false
# class = "logging.handlers.WatchedFileHandler" # Uncomment to disable console logging
# level = "WARNING" # console = false
# formatter = "extended"
# encoding = "utf8"
# filename = "flaschengeist.log"
[DATABASE] [DATABASE]
# engine = "mysql" (default) # engine = "mysql" (default)

View File

@ -6,16 +6,22 @@ disable_existing_loggers = false
[formatters] [formatters]
[formatters.simple] [formatters.simple]
format = "[%(asctime)s] %(levelname)s - %(message)s" format = "%(asctime)s - %(levelname)s - %(message)s"
[formatters.extended] [formatters.extended]
format = "[%(asctime)s] %(levelname)s %(filename)s - %(funcName)s - %(lineno)d - %(threadName)s - %(name)s — %(message)s" format = "%(asctime)s — %(filename)s - %(funcName)s - %(lineno)d - %(threadName)s - %(name)s — %(levelname)s — %(message)s"
[handlers] [handlers]
[handlers.wsgi] [handlers.console]
stream = "ext://flask.logging.wsgi_errors_stream"
class = "logging.StreamHandler" class = "logging.StreamHandler"
formatter = "simple"
level = "DEBUG" level = "DEBUG"
formatter = "simple"
stream = "ext://sys.stderr"
[handlers.file]
class = "logging.handlers.WatchedFileHandler"
level = "WARNING"
formatter = "extended"
encoding = "utf8"
filename = "flaschengeist.log"
[loggers] [loggers]
[loggers.werkzeug] [loggers.werkzeug]
@ -23,4 +29,4 @@ disable_existing_loggers = false
[root] [root]
level = "WARNING" level = "WARNING"
handlers = ["wsgi"] handlers = ["console"]