Skip to content

Commit

Permalink
🔧 Open up logging (#237)
Browse files Browse the repository at this point in the history
* 🔧 Open up logging

Create methods on logger for add/remove handlers and get all handlers. Expose these in global methods as well on the global instance of LOG.

Open up the get methods for file and stream handlers

Remove the file logger since it now can be added afterward instead of by default.

* ✅ MyPy correction to silent

'silent' apparently should be without quotes.
A warning is thrown on lint.

* ✅Add tests for logs
  • Loading branch information
keithrfung authored Oct 30, 2020
1 parent 95e122b commit 932022c
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 22 deletions.
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
ignore_missing_imports = True
follow_imports = "silent"
follow_imports = silent
show_column_numbers = True
mypy_path = "src:stubs"

Expand Down
82 changes: 61 additions & 21 deletions src/electionguard/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
import os.path
import sys
from typing import Any, Tuple
from typing import Any, List, Tuple
from logging.handlers import RotatingFileHandler

from .singleton import Singleton
Expand All @@ -21,8 +21,7 @@ def __init__(self) -> None:
super(ElectionGuardLog, self).__init__()

self.__logger = logging.getLogger("electionguard")
self.__logger.addHandler(self._get_file_handler())
self.__logger.addHandler(self._get_stream_handler())
self.__logger.addHandler(get_stream_handler())

@staticmethod
def __get_call_info() -> Tuple[str, str, int]:
Expand All @@ -45,30 +44,23 @@ def __formatted_message(self, message: str) -> str:
message = f"{os.path.basename(filename)}.{funcname}:#L{line}: {message}"
return message

def _get_stream_handler(self) -> logging.StreamHandler:
def add_handler(self, handler: logging.Handler) -> None:
"""
Get a Stream Handler, sends only warnings and errors to stdout.
Adds a logger handler
"""
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setLevel(logging.WARNING)
stream_handler.setFormatter(logging.Formatter(FORMAT))
return stream_handler
self.__logger.addHandler(handler)

def _get_file_handler(self) -> logging.FileHandler:
def remove_handler(self, handler: logging.Handler) -> None:
"""
Get a File System Handler, sends verbose logging to a file, `electionguard.log`.
When that file gets too large, the logs will rotate, creating files with names
like `electionguard.log.1`.
Removes a logger handler
"""
self.__logger.removeHandler(handler)

# TODO: add file compression, save a bunch of space.
# https://medium.com/@rahulraghu94/overriding-pythons-timedrotatingfilehandler-to-compress-your-log-files-iot-c766a4ace240
file_handler = RotatingFileHandler(
"electionguard.log", "a", maxBytes=10_000_000, backupCount=10
)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(logging.Formatter(FORMAT))
return file_handler
def handlers(self) -> List[logging.Handler]:
"""
Returns all logging handlers
"""
return self.__logger.handlers

def debug(self, message: str, *args: Any, **kwargs: Any) -> None:
"""
Expand Down Expand Up @@ -101,9 +93,57 @@ def critical(self, message: str, *args: Any, **kwargs: Any) -> None:
self.__logger.critical(self.__formatted_message(message), *args, **kwargs)


def get_stream_handler() -> logging.StreamHandler:
"""
Get a Stream Handler, sends only warnings and errors to stdout.
"""
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setLevel(logging.WARNING)
stream_handler.setFormatter(logging.Formatter(FORMAT))
return stream_handler


def get_file_handler() -> logging.FileHandler:
"""
Get a File System Handler, sends verbose logging to a file, `electionguard.log`.
When that file gets too large, the logs will rotate, creating files with names
like `electionguard.log.1`.
"""

# TODO: add file compression, save a bunch of space.
# https://medium.com/@rahulraghu94/overriding-pythons-timedrotatingfilehandler-to-compress-your-log-files-iot-c766a4ace240
file_handler = RotatingFileHandler(
"electionguard.log", "a", maxBytes=10_000_000, backupCount=10
)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(logging.Formatter(FORMAT))
return file_handler


LOG = ElectionGuardLog()


def log_add_handler(handler: logging.Handler) -> None:
"""
Adds a handler to the logger
"""
LOG.add_handler(handler)


def log_remove_handler(handler: logging.Handler) -> None:
"""
Removes a handler from the logger
"""
LOG.remove_handler(handler)


def log_handlers() -> List[logging.Handler]:
"""
Returns all logger handlers
"""
return LOG.handlers()


def log_debug(msg: str, *args: Any, **kwargs: Any) -> None:
"""
Logs a debug message to the console and the file log.
Expand Down
47 changes: 47 additions & 0 deletions tests/test_logs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from unittest import TestCase

from electionguard.logs import (
get_stream_handler,
log_add_handler,
log_remove_handler,
log_handlers,
log_debug,
log_error,
log_info,
log_warning,
)


class TestLogs(TestCase):
def test_log_methods(self):
# Arrange
message = "test log message"

# Act
log_debug(message)
log_error(message)
log_info(message)
log_warning(message)

def test_log_handlers(self):
# Arrange

# Act
handlers = log_handlers()

# Assert
self.assertEqual(len(handlers), 1)

# Act
log_remove_handler(handlers[0])
empty_handlers = log_handlers()

# Assert
self.assertEqual(len(empty_handlers), 0)

# Act
log_add_handler(get_stream_handler())
added_handlers = log_handlers()

# Assert
self.assertEqual(len(added_handlers), 1)

0 comments on commit 932022c

Please sign in to comment.