diff --git a/README.md b/README.md index f9ba61c..aa1319f 100644 --- a/README.md +++ b/README.md @@ -282,6 +282,10 @@ optional arguments: worker will execute) ``` +### Logging + +The package logs to the `toosimpleq` logger. Please refer to the Django docs to see how to configure this. If no handler is defined, a default handler will be added that outputs the logs to the console. + ## Contrib apps ### django_toosimple_q.contrib.mail diff --git a/django_toosimple_q/logging.py b/django_toosimple_q/logging.py index 6b4fe7e..c7d008a 100644 --- a/django_toosimple_q/logging.py +++ b/django_toosimple_q/logging.py @@ -2,16 +2,7 @@ from .registry import schedules_registry, tasks_registry -formatter = logging.Formatter( - "[%(asctime)s][%(levelname)s][toosimpleq] %(message)s", "%Y-%m-%d %H:%M:%S" -) - -handler = logging.StreamHandler() -handler.setFormatter(formatter) - logger = logging.getLogger("toosimpleq") -logger.setLevel(logging.INFO) -logger.addHandler(handler) def show_registry(): diff --git a/django_toosimple_q/management/commands/worker.py b/django_toosimple_q/management/commands/worker.py index 060d166..aedd05e 100644 --- a/django_toosimple_q/management/commands/worker.py +++ b/django_toosimple_q/management/commands/worker.py @@ -64,6 +64,23 @@ def add_arguments(self, parser): ) def handle(self, *args, **options): + if not logger.hasHandlers(): + # log to console in case no loggers are configured + default_formatter = logging.Formatter( + "[%(asctime)s][%(levelname)s][toosimpleq] %(message)s", + "%Y-%m-%d %H:%M:%S", + ) + default_handler = logging.StreamHandler() + default_handler.setFormatter(default_formatter) + logger.addHandler(default_handler) + + try: + self._handle(*args, **options) + except Exception as e: + logger.exception(e) + raise e + + def _handle(self, *args, **options): # Handle interuption signals signal.signal(signal.SIGINT, self.handle_signal) signal.signal(signal.SIGTERM, self.handle_signal) @@ -137,7 +154,7 @@ def handle(self, *args, **options): except Exception as e: exc = e - logger.critical(f"Crashed unhandled exception: {e}") + logger.exception(e) self.worker_status.exit_code = WorkerStatus.ExitCodes.CRASHED.value self.worker_status.exit_log = format_exc() diff --git a/django_toosimple_q/tests/tests_logging.py b/django_toosimple_q/tests/tests_logging.py new file mode 100644 index 0000000..9058879 --- /dev/null +++ b/django_toosimple_q/tests/tests_logging.py @@ -0,0 +1,46 @@ +import io +from contextlib import redirect_stderr, redirect_stdout + +from django.conf import settings +from django.core import management +from django.test import TestCase, override_settings +from django.utils.log import configure_logging +from freezegun import freeze_time + + +class LoggingTestCase(TestCase): + # def setUp(self): + # logger.handlers = [] + + @freeze_time("2020-01-01") + def test_default_logging(self): + """With no specific config, log to console""" + configure_logging(settings.LOGGING_CONFIG, settings.LOGGING) + buf = io.StringIO() + with redirect_stdout(buf), redirect_stderr(buf): + management.call_command("worker", "--once", "--queue", "nop") + self.assertIn( + "[2020-01-01 00:00:00][INFO][toosimpleq] Exiting loop because --once was passed", + buf.getvalue(), + ) + + @freeze_time("2020-01-01") + @override_settings( + LOGGING={ + "version": 1, + "disable_existing_loggers": False, + "handlers": {"file": {"class": "logging.NullHandler"}}, + "root": {"handlers": ["file"]}, + } + ) + def test_logging_output(self): + """With explicit config, handler isn't added""" + configure_logging(settings.LOGGING_CONFIG, settings.LOGGING) + + buf = io.StringIO() + with redirect_stdout(buf), redirect_stderr(buf): + management.call_command("worker", "--once", "--queue", "nop") + self.assertEqual( + "", + buf.getvalue(), + )