From 397ed12c9b0c133cf38093a1a579f70e70dc67db Mon Sep 17 00:00:00 2001 From: ZequnZ Date: Tue, 19 Nov 2024 17:57:34 +0200 Subject: [PATCH 1/3] feat: move tests folder outside --- .github/workflows/ci.yml | 2 +- Taskfile.yml | 4 ++-- {src/test => tests}/__init__.py | 0 {src/test => tests}/conftest.py | 0 {src/test => tests}/test_toy.py | 0 5 files changed, 3 insertions(+), 3 deletions(-) rename {src/test => tests}/__init__.py (100%) rename {src/test => tests}/conftest.py (100%) rename {src/test => tests}/test_toy.py (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73837b1..9f813c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: - uses: pre-commit/action@v3.0.1 - name: Run the automated tests(using pytest) - run: poetry run pytest ./src/test -sv --durations=0 + run: poetry run pytest ./tests -sv --durations=0 - uses: pyupio/safety-action@v1 if: runner.os == 'Linux' diff --git a/Taskfile.yml b/Taskfile.yml index cf2d90e..ba522cb 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,7 +1,7 @@ version: '3' vars: - TESTDIR: test + TESTDIR: tests tasks: linter-watch: @@ -28,7 +28,7 @@ tasks: desc: Run tests(unit, integration and more...) in TESTDIR(default=tests) folder dir: '{{ .USER_WORKING_DIR }}' cmds: - - poetry run pytest ./src/{{ .TESTDIR }} -sv --durations=0 + - poetry run pytest ./{{ .TESTDIR }} -sv --durations=0 precommit: desc: Run pre-commit diff --git a/src/test/__init__.py b/tests/__init__.py similarity index 100% rename from src/test/__init__.py rename to tests/__init__.py diff --git a/src/test/conftest.py b/tests/conftest.py similarity index 100% rename from src/test/conftest.py rename to tests/conftest.py diff --git a/src/test/test_toy.py b/tests/test_toy.py similarity index 100% rename from src/test/test_toy.py rename to tests/test_toy.py From 56bacde3f841d940d80ac3c234393fc9b7394aac Mon Sep 17 00:00:00 2001 From: ZequnZ Date: Tue, 19 Nov 2024 18:03:12 +0200 Subject: [PATCH 2/3] feat: add local log server --- docker-compose.yml | 9 ++++ local_log_server/Dockerfile-log-server | 8 +++ local_log_server/log_server.py | 63 +++++++++++++++++++++++ src/logger.py | 70 ++++++++++++++++++++++++++ src/test_logger.py | 8 +++ 5 files changed, 158 insertions(+) create mode 100644 local_log_server/Dockerfile-log-server create mode 100644 local_log_server/log_server.py create mode 100644 src/logger.py create mode 100644 src/test_logger.py diff --git a/docker-compose.yml b/docker-compose.yml index fd2e3cb..6218638 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,3 +25,12 @@ services: - ENVIRONMENT=local - SHELL=/bin/bash container_name: pyproj-template + depends_on: + - log_server + + log_server: + build: + context: ./local_log_server + dockerfile: Dockerfile-log-server + expose: + - "5237/udp" diff --git a/local_log_server/Dockerfile-log-server b/local_log_server/Dockerfile-log-server new file mode 100644 index 0000000..18359ea --- /dev/null +++ b/local_log_server/Dockerfile-log-server @@ -0,0 +1,8 @@ +FROM python:3.10.1-slim + +WORKDIR /app + +COPY log_server.py /app + +CMD ["--port", "5237", "--save"] +ENTRYPOINT ["python", "-u", "log_server.py"] diff --git a/local_log_server/log_server.py b/local_log_server/log_server.py new file mode 100644 index 0000000..954c59f --- /dev/null +++ b/local_log_server/log_server.py @@ -0,0 +1,63 @@ +""" +log_server.py + +This module spins up a local UDP log server that can print logs to the console and optionally save them to a text file. +""" + +import argparse +import os +import socket +import sys + + +def parse_arguments(args): + parser = argparse.ArgumentParser(description="Print Logs") + parser.add_argument( + "--save", + dest="save", + action="store_true", + help="Set flag to save logs to text file.", + ) + parser.add_argument( + "--no-save", + dest="save", + action="store_false", + help="Set flag not to save logs to text file.", + ) + parser.set_defaults(save=True) + parser.add_argument( + "--port", + default=5091, + help="Use flag to specify logger port (default 5091).", + type=int, + ) + + return parser.parse_args() + + +def spin_up_log_server(udp_ip, udp_port, save=False): + """Spin up a local udp log server.""" + try: + os.remove("logfile.txt") + except Exception: + pass + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Internet # UDP + sock.bind((udp_ip, udp_port)) + while True: + # buffer size is 1024 bytes + data = sock.recvfrom(1024)[0] + print(data.decode("utf-8")) + # write logs to file + if save: + with open("logfile.txt", "a") as log_file: + log_file.write(data.decode("utf-8") + "\n") + + +if __name__ == "__main__": + args = parse_arguments(sys.argv[1:]) + + UDP_PORT = int(args.port) + UDP_IP = "0.0.0.0" + + spin_up_log_server(UDP_IP, UDP_PORT, args.save) diff --git a/src/logger.py b/src/logger.py new file mode 100644 index 0000000..8af1139 --- /dev/null +++ b/src/logger.py @@ -0,0 +1,70 @@ +""" +logger.py + +This module defines loggers with different settings/configurations that can be chosen automatically based on the ENVIRONMENT variable. +""" + +import logging +import logging.config +import os +import sys + +# Define base log configuration +base_log_config: dict = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "simple_formatter": { + "format": "%(name)-8s %(asctime)s - %(levelname)s - %(message)s", + # Leaving datefmt string empty will result in the following timeformat: + # 2023-08-24 13:45:42,774 + # TODO: Explicit formatting would be better such as + # "datefmt": "%Y-%m-%d %H:%M:%S" + # However, with datefmt it's apparently not possible to add milliseconds to + # the time. Thus, using empty string for now. + "datefmt": "", + }, + "detailed_formatter": { + "format": " %(name)-8s[%(asctime)s %(filename)s -> %(funcName)s(): line:%(lineno)s] %(levelname)s: %(message)s" + }, + }, + # Define handlers than can be used in loggers + "handlers": { + "stream_handler": { + "class": "logging.StreamHandler", + "level": "INFO", + "formatter": "simple_formatter", + "stream": sys.stderr, + }, + "local_docker_handler": { + "class": "logging.handlers.SysLogHandler", + "level": "DEBUG", + "formatter": "detailed_formatter", + "address": ("log_server", 5237), + }, + }, + # Under "loggers", every dict is a configuration for a specific logger(key[logger name]-value[config]) + "loggers": { + "local_logger": { + "handlers": ["local_docker_handler"], + "level": "DEBUG", + "propagate": True, # If true, this logger would have its own handler and root’s handler + } + }, + # Root logger configuration + "root": { + "handlers": ["stream_handler"], + "level": "WARNING", + "propagate": False, + }, +} + +# Set configuration +logging.config.dictConfig(base_log_config) + + +# Get logger based on environment +if os.environ.get("ENVIRONMENT") == "local": + logger = logging.getLogger("local_logger") +else: + logger = logging.getLogger() diff --git a/src/test_logger.py b/src/test_logger.py new file mode 100644 index 0000000..31d7d14 --- /dev/null +++ b/src/test_logger.py @@ -0,0 +1,8 @@ +import logging + +from logger import logger + +root_logger = logging.getLogger() + +logger.info("test") +root_logger.warning("test") From f8ff74320aadc49adbe03d18d8fb6c38505f62b0 Mon Sep 17 00:00:00 2001 From: ZequnZ Date: Tue, 19 Nov 2024 18:03:39 +0200 Subject: [PATCH 3/3] docs: add session --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 4971a63..b689bb3 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,9 @@ A template for **Python project**, equipped with best practices, can be used whe ### 🔫 Security check: - Dependency vulnerability check:[safety](https://github.com/pyupio/safety), support CLI and in CI workflow +### Local log server +TBD + ## Guidebook ### Create a reproducible Python development environment with *pyenv* and *Poetry*