Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reworking logging [Not final]. #384

Merged
merged 5 commits into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions datashuttle/utils/ds_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,22 @@
from datashuttle.utils import utils


def get_logger_name():
return "datashuttle"


def get_logger():
return logging.getLogger(get_logger_name())


def logging_is_active():
logger_exists = get_logger_name() in logging.root.manager.loggerDict
if logger_exists:
if get_logger().handlers != []:
JoeZiminski marked this conversation as resolved.
Show resolved Hide resolved
return True
return False


def start(
path_to_log: Path,
command_name: str,
Expand All @@ -38,8 +54,10 @@ def start(
file_log_level="DEBUG",
write_git=True,
log_to_console=False,
logger_name=get_logger_name(),
)
logging.info(f"Starting logging for command {command_name}")
logger = get_logger()
logger.info(f"Starting logging for command {command_name}")


def get_logging_filename(command_name: str) -> str:
Expand Down Expand Up @@ -94,7 +112,8 @@ def close_log_filehandler() -> None:
"""
Remove handlers from all loggers.
"""
logger = logging.getLogger()
logger = get_logger()
logger.debug("Finished logging.")
handlers = logger.handlers[:]
for handler in handlers:
logger.removeHandler(handler)
Expand Down
26 changes: 13 additions & 13 deletions datashuttle/utils/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import logging
import re
import traceback
import warnings
Expand All @@ -24,8 +23,9 @@ def log(message: str) -> None:
Log the message to the main initialised
logger.
"""
logger = logging.getLogger("datashuttle")
logger.debug(message)
if ds_logger.logging_is_active():
logger = ds_logger.get_logger()
logger.debug(message)


def log_and_message(message: str, use_rich: bool = False) -> None:
Expand All @@ -41,13 +41,21 @@ def log_and_raise_error(message: str, exception: Any) -> None:
"""
Log the message before raising the same message as an error.
"""
if "datashuttle" in logging.root.manager.loggerDict.keys():
logger = logging.getLogger("datashuttle")
if ds_logger.logging_is_active():
logger = ds_logger.get_logger()
logger.error(f"\n\n{' '.join(traceback.format_stack(limit=5))}")
logger.error(message)
raise_error(message, exception)


def warn(message: str, log: bool) -> None:
""" """
if log and ds_logger.logging_is_active():
logger = ds_logger.get_logger()
logger.warning(message)
warnings.warn(message)


def raise_error(message: str, exception) -> None:
"""
Centralized way to raise an error. The logger is closed
Expand All @@ -58,14 +66,6 @@ def raise_error(message: str, exception) -> None:
raise exception(message)


def warn(message: str, log: bool) -> None:
""" """
if log:
logger = logging.getLogger("datashuttle")
logger.warning(message)
warnings.warn(message)


def print_message_to_user(
message: Union[str, list], use_rich: bool = False
) -> None:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies = [
"PyYAML",
"requests",
"rich",
"fancylog",
"fancylog>=0.4.2",
"simplejson",
"pyperclip",
"textual",
Expand Down
2 changes: 1 addition & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ def set_datashuttle_loggers(disable):
of test_logging.py and turned back off during
tear-down.
"""
for name in ["datashuttle", "rich"]:
for name in [ds_logger.get_logger_name(), "rich"]:
logger = logging.getLogger(name)
logger.disabled = disable

Expand Down
73 changes: 73 additions & 0 deletions tests/tests_integration/test_logging.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import glob
import logging
import os
import re
from pathlib import Path
Expand All @@ -8,13 +9,85 @@

from datashuttle import DataShuttle
from datashuttle.configs.canonical_tags import tags
from datashuttle.utils import ds_logger
from datashuttle.utils.custom_exceptions import (
ConfigError,
NeuroBlueprintError,
)


class TestLogging:

@pytest.fixture(scope="function")
def teardown_logger(self):
"""
Ensure the logger is deleted at the end of each test.
"""
yield
if "datashuttle" in logging.root.manager.loggerDict:
logging.root.manager.loggerDict.pop("datashuttle")

# -------------------------------------------------------------------------
# Basic Functionality Tests
# -------------------------------------------------------------------------

def test_logger_name(self):
"""
Check the canonical logger name.
"""
assert ds_logger.get_logger_name() == "datashuttle"

def test_start_logging(self, tmp_path, teardown_logger):
"""
Test that the central `start` logging function
starts the named logger with the expected handlers.
"""
assert ds_logger.logging_is_active() is False

ds_logger.start(tmp_path, "test-command", variables=[])

# test logger exists and is as expected
assert "datashuttle" in logging.root.manager.loggerDict
assert ds_logger.logging_is_active() is True

logger = logging.getLogger("datashuttle")
assert logger.propagate is False
assert len(logger.handlers) == 1
assert isinstance(logger.handlers[0], logging.FileHandler)

def test_shutdown_logger(self, tmp_path, teardown_logger):
"""
Check the log handler remover indeed removes the handles.
"""
assert ds_logger.logging_is_active() is False

ds_logger.start(tmp_path, "test-command", variables=[])

logger = logging.getLogger("datashuttle")

ds_logger.close_log_filehandler()

assert len(logger.handlers) == 0
assert ds_logger.logging_is_active() is False

def test_logging_an_error(self, project, teardown_logger):
"""
Check that errors are caught and logged properly.
"""
try:
project.create_folders("rawdata", "sob-001")
except:
pass

log = test_utils.read_log_file(project.cfg.logging_path)

assert "ERROR" in log
assert "Problem with name:" in log

# -------------------------------------------------------------------------
# Functional Tests
# -------------------------------------------------------------------------

@pytest.fixture(scope="function")
def clean_project_name(self):
"""
Expand Down