Browse Source

Merge pull request #327 from justanr/Improve-Logging

Improve logging
Peter Justin 7 years ago
parent
commit
8d4ea6b2ea

+ 4 - 0
flaskbb/__init__.py

@@ -10,4 +10,8 @@
     :license: BSD, see LICENSE for more details.
 """
 __version__ = '1.0'  # noqa
+import logging
+
+logger = logging.getLogger(__name__)
+
 from flaskbb.app import create_app  # noqa

+ 55 - 34
flaskbb/app.py

@@ -10,6 +10,7 @@
 """
 import os
 import logging
+import logging.config
 import time
 from functools import partial
 
@@ -58,6 +59,9 @@ from flaskbb.plugins.utils import remove_zombie_plugins_from_db, template_hook
 from flaskbb.plugins import spec
 
 
+logger = logging.getLogger(__name__)
+
+
 def create_app(config=None):
     """Creates the app.
 
@@ -69,6 +73,7 @@ def create_app(config=None):
     """
     app = Flask("flaskbb")
     configure_app(app, config)
+    configure_logging(app)
     configure_celery_app(app, celery)
     configure_extensions(app)
     load_plugins(app)
@@ -79,7 +84,6 @@ def create_app(config=None):
     configure_errorhandlers(app)
     configure_migrations(app)
     configure_translations(app)
-    configure_logging(app)
 
     app.pluggy.hook.flaskbb_additional_setup(app=app, pluggy=app.pluggy)
 
@@ -324,31 +328,60 @@ def configure_translations(app):
 
 def configure_logging(app):
     """Configures logging."""
+    if app.config.get('USE_DEFAULT_LOGGING'):
+        configure_default_logging(app)
+
+    if app.config.get('LOG_CONF_FILE'):
+        logging.config.fileConfig(
+            app.config['LOG_CONF_FILE'], disable_existing_loggers=False
+        )
+
+    if app.config["SQLALCHEMY_ECHO"]:
+        # Ref: http://stackoverflow.com/a/8428546
+        @event.listens_for(Engine, "before_cursor_execute")
+        def before_cursor_execute(
+                conn, cursor, statement, parameters, context, executemany
+        ):
+            conn.info.setdefault('query_start_time', []).append(time.time())
+
+        @event.listens_for(Engine, "after_cursor_execute")
+        def after_cursor_execute(
+                conn, cursor, statement, parameters, context, executemany
+        ):
+            total = time.time() - conn.info['query_start_time'].pop(-1)
+            app.logger.debug("Total Time: %f", total)
+
 
-    logs_folder = os.path.join(app.root_path, os.pardir, "logs")
+def configure_default_logging(app):
     from logging.handlers import SMTPHandler
+
+    logs_folder = app.config.get('LOG_PATH')
+
+    if logs_folder is None:
+        logs_folder = os.path.join(app.root_path, os.pardir, "logs")
+
+    default_log_format = '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
+
     formatter = logging.Formatter(
-        '%(asctime)s %(levelname)s: %(message)s '
-        '[in %(pathname)s:%(lineno)d]')
+        app.config.get('LOG_FORMAT') or default_log_format
+    )
 
     info_log = os.path.join(logs_folder, app.config['INFO_LOG'])
 
     info_file_handler = logging.handlers.RotatingFileHandler(
-        info_log,
-        maxBytes=100000,
-        backupCount=10
+        info_log, maxBytes=100000, backupCount=10
     )
 
-    info_file_handler.setLevel(logging.INFO)
+    log_level = app.config.get('LOG_LEVEL') or logging.INFO
+
+    info_file_handler.setLevel(log_level)
     info_file_handler.setFormatter(formatter)
     app.logger.addHandler(info_file_handler)
 
     error_log = os.path.join(logs_folder, app.config['ERROR_LOG'])
 
     error_file_handler = logging.handlers.RotatingFileHandler(
-        error_log,
-        maxBytes=100000,
-        backupCount=10
+        error_log, maxBytes=100000, backupCount=10
     )
 
     error_file_handler.setLevel(logging.ERROR)
@@ -356,31 +389,19 @@ def configure_logging(app):
     app.logger.addHandler(error_file_handler)
 
     if app.config["SEND_LOGS"]:
-        mail_handler = \
-            SMTPHandler(
-                app.config['MAIL_SERVER'],
-                app.config['MAIL_DEFAULT_SENDER'],
-                app.config['ADMINS'],
-                'application error, no admins specified',
-                (app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD'])
-            )
-
-        mail_handler.setLevel(logging.ERROR)
-        mail_handler.setFormatter(formatter)
-        app.logger.addHandler(mail_handler)
+        configure_mail_logs(app)
 
-    if app.config["SQLALCHEMY_ECHO"]:
-        # Ref: http://stackoverflow.com/a/8428546
-        @event.listens_for(Engine, "before_cursor_execute")
-        def before_cursor_execute(conn, cursor, statement,
-                                  parameters, context, executemany):
-            conn.info.setdefault('query_start_time', []).append(time.time())
 
-        @event.listens_for(Engine, "after_cursor_execute")
-        def after_cursor_execute(conn, cursor, statement,
-                                 parameters, context, executemany):
-            total = time.time() - conn.info['query_start_time'].pop(-1)
-            app.logger.debug("Total Time: %f", total)
+def configure_mail_logs(app):
+    mail_handler = SMTPHandler(
+        app.config['MAIL_SERVER'], app.config['MAIL_DEFAULT_SENDER'],
+        app.config['ADMINS'], 'application error, no admins specified',
+        (app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD'])
+    )
+
+    mail_handler.setLevel(logging.ERROR)
+    mail_handler.setFormatter(formatter)
+    app.logger.addHandler(mail_handler)
 
 
 def load_plugins(app):

+ 3 - 0
flaskbb/auth/__init__.py

@@ -0,0 +1,3 @@
+import logging
+
+logger = logging.getLogger(__name__)

+ 5 - 0
flaskbb/auth/forms.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 from flask_wtf import FlaskForm
 from wtforms import (StringField, PasswordField, BooleanField, HiddenField,
                      SubmitField, SelectField)
@@ -20,6 +21,10 @@ from flaskbb.utils.settings import flaskbb_config
 from flaskbb.utils.helpers import time_utcnow
 from flaskbb.utils.fields import RecaptchaField
 
+
+logger = logging.getLogger(__name__)
+
+
 USERNAME_RE = r'^[\w.+-]+$'
 is_valid_username = regexp(
     USERNAME_RE, message=_("You can only use letters, numbers or dashes.")

+ 5 - 0
flaskbb/auth/views.py

@@ -10,6 +10,7 @@
     :license: BSD, see LICENSE for more details.
 """
 from datetime import datetime
+import logging
 
 from flask import Blueprint, flash, g, redirect, request, url_for
 from flask.views import MethodView
@@ -33,6 +34,10 @@ from flaskbb.utils.helpers import (anonymous_required, enforce_recaptcha,
 from flaskbb.utils.settings import flaskbb_config
 from flaskbb.utils.tokens import get_token_status
 
+
+logger = logging.getLogger(__name__)
+
+
 auth = Blueprint("auth", __name__)
 
 

+ 10 - 1
flaskbb/cli/main.py

@@ -504,7 +504,8 @@ def generate_config(development, output, force):
         "mail_admin_address": "admin@yourdomain",
         "secret_key": binascii.hexlify(os.urandom(24)).decode(),
         "csrf_secret_key": binascii.hexlify(os.urandom(24)).decode(),
-        "timestamp": datetime.utcnow().strftime("%A, %d. %B %Y at %H:%M")
+        "timestamp": datetime.utcnow().strftime("%A, %d. %B %Y at %H:%M"),
+        "log_config_path": "",
     }
 
     if not force:
@@ -615,6 +616,14 @@ def generate_config(development, output, force):
         click.style("Mail Admin Email", fg="magenta"),
         default=default_conf.get("mail_admin_address"))
 
+    click.secho("Optional filepath to load a logging configuration file from. "
+                "See the Python logging documentation for more detail.\n"
+                "\thttps://docs.python.org/library/logging.config.html#logging-config-fileformat",
+                fg="cyan")
+    default_conf["log_config_path"] =click.prompt(
+        click.style("Logging Config Path", fg="magenta"),
+        default=default_conf.get("log_config_path"))
+
     write_config(default_conf, config_template, config_path)
 
     # Finished

+ 1 - 0
flaskbb/configs/config.cfg.template

@@ -154,6 +154,7 @@ ADMINS = ["{{ mail_admin_address }}"]
 # If SEND_LOGS is set to True, the admins (see the mail configuration) will
 # recieve the error logs per email.
 SEND_LOGS = False
+LOG_CONF_FILE = {{ log_config_path }}
 
 
 # FlaskBB Settings

+ 37 - 2
flaskbb/configs/default.py

@@ -47,15 +47,48 @@ class DefaultConfig(object):
     # This only affects the url generation with 'url_for'.
     PREFERRED_URL_SCHEME = "http"
 
+    # Logging Settings
+    # ------------------------------
+    # This config section will deal with the logging settings
+    # for FlaskBB, adjust as needed.
+
+    # Logging Config Path
+    # see https://docs.python.org/library/logging.config.html#logging.config.fileConfig
+    # for more details. Should either be None or a path to a file
+    # If this is set to a path, consider setting USE_DEFAULT_LOGGING to False
+    # otherwise there may be interactions between the log configuration file
+    # and the default logging setting.
+    #
+    # If set to a file path, this should be an absolute file path
+    LOG_CONF_FILE = None
+
+    # When set to True this will enable the default
+    # FlaskBB logging configuration which uses the settings
+    # below to determine logging
+    USE_DEFAULT_LOGGING = True
+
+    # Log format FlaskBB will use
+    LOG_FORMAT = '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
+
+    # Log level FlaskBB will use
+    LOG_LEVEL = "INFO"
+
     # If SEND_LOGS is set to True, the admins (see the mail configuration) will
     # recieve the error logs per email.
     SEND_LOGS = False
 
-    # The filename for the info and error logs. The logfiles are stored at
-    # flaskbb/logs
+    # Path to store the INFO and ERROR logs
+    # If None this defaults to flaskbb/logs
+    #
+    # If set to a file path, this should be an absolute path
+    LOG_PATH = None
+
+    # The filename for the info and error logs. The logfiles are stored
+    # at the path specified in LOG_PATH
     INFO_LOG = "info.log"
     ERROR_LOG = "error.log"
 
+
     # Database
     # ------------------------------
     # For PostgresSQL:
@@ -191,6 +224,8 @@ class DefaultConfig(object):
     MESSAGE_URL_PREFIX = "/message"
     AUTH_URL_PREFIX = "/auth"
     ADMIN_URL_PREFIX = "/admin"
+
+
     # Remove dead plugins - useful if you want to migrate your instance
     # somewhere else and forgot to reinstall the plugins.
     # If set to `False` it will NOT remove plugins that are NOT installed on

+ 4 - 0
flaskbb/email.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 from flask import render_template
 from flask_mail import Message
 from flask_babelplus import lazy_gettext as _
@@ -16,6 +17,9 @@ from flaskbb.extensions import mail, celery
 from flaskbb.utils.tokens import make_token
 
 
+logger = logging.getLogger(__name__)
+
+
 @celery.task
 def send_reset_token(user):
     """Sends the reset token to the user's email address.

+ 3 - 0
flaskbb/forum/__init__.py

@@ -0,0 +1,3 @@
+import logging
+
+logger = logging.getLogger(__name__)

+ 4 - 0
flaskbb/forum/forms.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 from flask_wtf import FlaskForm
 from wtforms import (TextAreaField, StringField, SelectMultipleField,
                      BooleanField, SubmitField)
@@ -18,6 +19,9 @@ from flaskbb.forum.models import Topic, Post, Report, Forum
 from flaskbb.user.models import User
 
 
+logger = logging.getLogger(__name__)
+
+
 class QuickreplyForm(FlaskForm):
     content = TextAreaField(_("Quick reply"), validators=[
         DataRequired(message=_("You cannot post a reply without content."))])

+ 4 - 0
flaskbb/forum/models.py

@@ -9,6 +9,7 @@
     :license: BSD, see LICENSE for more details.
 """
 from datetime import timedelta
+import logging
 
 from flask import abort, url_for
 from sqlalchemy.orm import aliased
@@ -21,6 +22,9 @@ from flaskbb.utils.helpers import (get_categories_and_forums, get_forums,
 from flaskbb.utils.settings import flaskbb_config
 
 
+logger = logging.getLogger(__name__)
+
+
 moderators = db.Table(
     'moderators',
     db.Column('user_id', db.Integer(), db.ForeignKey('users.id'),

+ 5 - 1
flaskbb/forum/views.py

@@ -9,6 +9,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 '''
+import logging
 import math
 
 from flask import (Blueprint, abort, current_app, flash, redirect, request,
@@ -35,7 +36,10 @@ from flaskbb.utils.requirements import (CanAccessForum, CanAccessTopic,
                                         IsAtleastModeratorInForum)
 from flaskbb.utils.settings import flaskbb_config
 
-forum = Blueprint('forum', __name__)
+logger = logging.getLogger(__name__)
+
+
+forum = Blueprint("forum", __name__)
 
 
 class ForumIndex(MethodView):

+ 3 - 0
flaskbb/management/__init__.py

@@ -0,0 +1,3 @@
+import logging
+
+logger = logging.getLogger(__name__)

+ 4 - 0
flaskbb/management/forms.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 from flask_wtf import FlaskForm
 from wtforms import (BooleanField, HiddenField, IntegerField, PasswordField,
                      SelectField, StringField, SubmitField, TextAreaField)
@@ -26,6 +27,9 @@ from flaskbb.utils.requirements import IsAtleastModerator
 from flask_allows import Permission
 
 
+logger = logging.getLogger(__name__)
+
+
 USERNAME_RE = r'^[\w.+-]+$'
 is_username = regexp(USERNAME_RE,
                      message=_("You can only use letters, numbers or dashes."))

+ 10 - 3
flaskbb/management/models.py

@@ -8,10 +8,17 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
-from flaskbb._compat import iteritems
-from flaskbb.extensions import db, cache
+import logging
+
+from flask_wtf import FlaskForm
+from flaskbb._compat import iteritems, text_type
+from flaskbb.extensions import cache, db
 from flaskbb.utils.database import CRUDMixin
-from flaskbb.utils.forms import generate_settings_form, SettingValueType
+from flaskbb.utils.forms import SettingValueType, generate_settings_form
+from wtforms import (BooleanField, FloatField, IntegerField, SelectField,
+                     SelectMultipleField, TextField, validators)
+
+logger = logging.getLogger(__name__)
 
 
 class SettingsGroup(db.Model, CRUDMixin):

+ 4 - 0
flaskbb/management/views.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 import sys
 
 from celery import __version__ as celery_version
@@ -37,6 +38,9 @@ from flaskbb.utils.requirements import (CanBanUser, CanEditUser, IsAdmin,
 from flaskbb.utils.settings import flaskbb_config
 from flaskbb.utils.forms import populate_settings_dict, populate_settings_form
 
+logger = logging.getLogger(__name__)
+
+
 management = Blueprint("management", __name__)
 
 

+ 3 - 0
flaskbb/message/__init__.py

@@ -0,0 +1,3 @@
+import logging
+
+logger = logging.getLogger(__name__)

+ 4 - 0
flaskbb/message/forms.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 from flask_login import current_user
 from flask_wtf import FlaskForm
 from wtforms import StringField, TextAreaField, ValidationError, SubmitField
@@ -18,6 +19,9 @@ from flaskbb.user.models import User
 from flaskbb.message.models import Conversation, Message
 
 
+logger = logging.getLogger(__name__)
+
+
 class ConversationForm(FlaskForm):
     to_user = StringField(_("Recipient"), validators=[
         DataRequired(message=_("A valid username is required."))])

+ 4 - 0
flaskbb/message/models.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 from sqlalchemy_utils import UUIDType
 
 from flaskbb.extensions import db
@@ -15,6 +16,9 @@ from flaskbb.utils.helpers import time_utcnow
 from flaskbb.utils.database import CRUDMixin, UTCDateTime
 
 
+logger = logging.getLogger(__name__)
+
+
 class Conversation(db.Model, CRUDMixin):
     __tablename__ = "conversations"
 

+ 4 - 0
flaskbb/message/views.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 import uuid
 from functools import wraps
 
@@ -24,6 +25,9 @@ from flaskbb.utils.helpers import (format_quote, register_view,
                                    render_template, time_utcnow)
 from flaskbb.utils.settings import flaskbb_config
 
+
+logger = logging.getLogger(__name__)
+
 message = Blueprint("message", __name__)
 
 

+ 11 - 0
flaskbb/plugins/manager.py

@@ -8,12 +8,16 @@
     :copyright: 2017, the FlaskBB Team
     :license: BSD, see LICENSE for more details
 """
+import logging
+
 from pkg_resources import (DistributionNotFound, VersionConflict,
                            iter_entry_points)
 
 import pluggy
 from flaskbb.utils.helpers import parse_pkg_metadata
 
+logger = logging.getLogger(__name__)
+
 
 class FlaskBBPluginManager(pluggy.PluginManager):
     """Overwrites :class:`pluggy.PluginManager` to add FlaskBB
@@ -30,6 +34,7 @@ class FlaskBBPluginManager(pluggy.PluginManager):
     def load_setuptools_entrypoints(self, entrypoint_name):
         """Load modules from querying the specified setuptools entrypoint name.
         Return the number of loaded plugins. """
+        logger.info("Loading plugins under entrypoint {}".format(entrypoint_name))
         for ep in iter_entry_points(entrypoint_name):
             if self.get_plugin(ep.name):
                 continue
@@ -43,6 +48,7 @@ class FlaskBBPluginManager(pluggy.PluginManager):
             try:
                 plugin = ep.load()
             except DistributionNotFound:
+                logger.warn("Could not load plugin {}. Passing.".format(ep.name))
                 continue
             except VersionConflict as e:
                 raise pluggy.PluginValidationError(
@@ -51,6 +57,11 @@ class FlaskBBPluginManager(pluggy.PluginManager):
             self.register(plugin, name=ep.name)
             self._plugin_distinfo.append((plugin, ep.dist))
             self._plugin_metadata[ep.name] = parse_pkg_metadata(ep.dist.key)
+            logger.info("Loaded plugin: {}".format(ep.name))
+        logger.info("Loaded {} plugins for entrypoint {}".format(
+            len(self._plugin_distinfo),
+            entrypoint_name
+        ))
         return len(self._plugin_distinfo)
 
     def get_metadata(self, name):

+ 3 - 0
flaskbb/user/__init__.py

@@ -0,0 +1,3 @@
+import logging
+
+logger = logging.getLogger(__name__)

+ 4 - 0
flaskbb/user/forms.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 from flask_login import current_user
 from flask_wtf import FlaskForm
 from wtforms import (StringField, PasswordField, TextAreaField, SelectField,
@@ -22,6 +23,9 @@ from flaskbb.utils.fields import BirthdayField
 from flaskbb.utils.helpers import check_image
 
 
+logger = logging.getLogger(__name__)
+
+
 class GeneralSettingsForm(FlaskForm):
     # The choices for those fields will be generated in the user view
     # because we cannot access the current_app outside of the context

+ 4 - 0
flaskbb/user/models.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 from werkzeug.security import generate_password_hash, check_password_hash
 from flask import url_for
 from flask_login import UserMixin, AnonymousUserMixin
@@ -22,6 +23,9 @@ from flaskbb.forum.models import (Post, Topic, Forum, topictracker, TopicsRead,
 from flaskbb.message.models import Conversation
 
 
+logger = logging.getLogger(__name__)
+
+
 groups_users = db.Table(
     'groups_users',
     db.Column('user_id', db.Integer, db.ForeignKey('users.id'),

+ 4 - 0
flaskbb/user/views.py

@@ -9,6 +9,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 from flask import Blueprint, flash, request
 from flask.views import MethodView
 from flask_babelplus import gettext as _
@@ -21,6 +22,9 @@ from flaskbb.utils.helpers import (get_available_languages,
                                    get_available_themes, register_view,
                                    render_template)
 
+logger = logging.getLogger(__name__)
+
+
 user = Blueprint("user", __name__)
 
 

+ 3 - 0
flaskbb/utils/__init__.py

@@ -0,0 +1,3 @@
+import logging
+
+logger = logging.getLogger(__name__)

+ 4 - 0
flaskbb/utils/database.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2015 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 import pytz
 from flask_login import current_user
 from flask_sqlalchemy import BaseQuery
@@ -15,6 +16,9 @@ from sqlalchemy.ext.declarative import declared_attr
 from flaskbb.extensions import db
 
 
+logger = logging.getLogger(__name__)
+
+
 def make_comparable(cls):
 
     def __eq__(self, other):

+ 5 - 0
flaskbb/utils/fields.py

@@ -11,6 +11,7 @@
     :license: BSD, see LICENSE for more details.
 """
 from datetime import datetime
+import logging
 try:
     import urllib2 as http
 except ImportError:
@@ -26,6 +27,10 @@ from wtforms.widgets.core import Select, HTMLString, html_params
 from flaskbb._compat import to_bytes, to_unicode
 from flaskbb.utils.settings import flaskbb_config
 
+
+logger = logging.getLogger(__name__)
+
+
 JSONEncoder = json.JSONEncoder
 
 RECAPTCHA_SCRIPT = u'https://www.google.com/recaptcha/api.js'

+ 4 - 0
flaskbb/utils/helpers.py

@@ -9,7 +9,9 @@
     :license: BSD, see LICENSE for more details.
 """
 import ast
+import glob
 import itertools
+import logging
 import operator
 import os
 import re
@@ -39,6 +41,8 @@ from PIL import ImageFile
 from pytz import UTC
 from werkzeug.local import LocalProxy
 
+logger = logging.getLogger(__name__)
+
 _punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+')
 
 

+ 4 - 0
flaskbb/utils/markup.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2016 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 import os
 import re
 
@@ -20,6 +21,9 @@ from pygments.formatters import HtmlFormatter
 from pygments.util import ClassNotFound
 
 
+logger = logging.getLogger(__name__)
+
+
 _re_emoji = re.compile(r':([a-z0-9\+\-_]+):', re.I)
 _re_user = re.compile(r'@(\w+)', re.I)
 

+ 7 - 4
flaskbb/utils/populate.py

@@ -9,14 +9,17 @@
     :license: BSD, see LICENSE for more details.
 """
 from __future__ import unicode_literals
+
 import collections
+import logging
 
+from flaskbb.extensions import alembic, db
+from flaskbb.forum.models import Category, Forum, Post, Topic
+from flaskbb.management.models import Setting, SettingsGroup
+from flaskbb.user.models import Group, User
 from sqlalchemy_utils.functions import create_database, database_exists
 
-from flaskbb.management.models import Setting, SettingsGroup
-from flaskbb.user.models import User, Group
-from flaskbb.forum.models import Post, Topic, Forum, Category
-from flaskbb.extensions import alembic, db
+logger = logging.getLogger(__name__)
 
 
 def delete_settings_from_fixture(fixture):

+ 4 - 0
flaskbb/utils/requirements.py

@@ -7,12 +7,16 @@
     :copyright: (c) 2015 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details
 """
+import logging
+
 from flask_allows import And, Or, Requirement
 
 from flaskbb.exceptions import FlaskBBError
 from flaskbb.forum.locals import current_forum, current_post, current_topic
 from flaskbb.forum.models import Forum, Post, Topic
 
+logger = logging.getLogger(__name__)
+
 
 class Has(Requirement):
     def __init__(self, permission):

+ 4 - 0
flaskbb/utils/search.py

@@ -9,6 +9,7 @@
     :copyright: (c) 2016 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 import whoosh
 from flask_whooshee import AbstractWhoosheer
 
@@ -17,6 +18,9 @@ from flaskbb.forum.models import Forum, Topic, Post
 from flaskbb.user.models import User
 
 
+logger = logging.getLogger(__name__)
+
+
 class PostWhoosheer(AbstractWhoosheer):
     models = [Post]
 

+ 4 - 0
flaskbb/utils/tokens.py

@@ -9,6 +9,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 from flask import current_app
 from itsdangerous import (TimedJSONWebSignatureSerializer, SignatureExpired,
                           BadSignature)
@@ -16,6 +17,9 @@ from itsdangerous import (TimedJSONWebSignatureSerializer, SignatureExpired,
 from flaskbb.user.models import User
 
 
+logger = logging.getLogger(__name__)
+
+
 def make_token(user, operation, expire=3600):
     """Generates a JSON Web Signature (JWS).
     See `RFC 7515 <https://tools.ietf.org/html/rfc7515>` if you want to know

+ 4 - 0
flaskbb/utils/translations.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2016 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+import logging
 import os
 import subprocess
 
@@ -17,6 +18,9 @@ from flask import current_app
 from flask_babelplus import Domain, get_locale
 
 
+logger = logging.getLogger(__name__)
+
+
 class FlaskBBDomain(Domain):
     def __init__(self, app):
         self.app = app