From f0a78590e4c8d1f71e49a3a9e388893312259e96 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Thu, 9 Mar 2023 23:46:31 +0300 Subject: [PATCH 01/16] init --- .github/workflows/build_and_publish.yml | 25 +++++++ README.md | 2 + logger/__init__.py | 4 ++ logger/formatter.py | 69 ++++++++++++++++++ logger/middleware.py | 96 +++++++++++++++++++++++++ logger/models.py | 44 ++++++++++++ logger/writer.py | 41 +++++++++++ requirements.txt | 3 + setup.py | 18 +++++ 9 files changed, 302 insertions(+) create mode 100644 .github/workflows/build_and_publish.yml create mode 100644 README.md create mode 100644 logger/__init__.py create mode 100644 logger/formatter.py create mode 100644 logger/middleware.py create mode 100644 logger/models.py create mode 100644 logger/writer.py create mode 100644 requirements.txt create mode 100644 setup.py diff --git a/.github/workflows/build_and_publish.yml b/.github/workflows/build_and_publish.yml new file mode 100644 index 0000000..a236b7c --- /dev/null +++ b/.github/workflows/build_and_publish.yml @@ -0,0 +1,25 @@ +name: Publish Python 🐍 distributions 📦 + +on: + release: + types: [published] + +jobs: + build-and-publish: + name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Set up Python 3.11 + uses: actions/setup-python@v3 + with: + python-version: "3.11" + - name: Install dependencies + run: | + python -m ensurepip + pip install build --user + python -m build --wheel --sdist --outdir dist/ + - name: Publish a Python distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/README.md b/README.md new file mode 100644 index 0000000..ffb656e --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# auth-lib +Библиотека для логирования сервисов Твой ФФ! diff --git a/logger/__init__.py b/logger/__init__.py new file mode 100644 index 0000000..9d8735e --- /dev/null +++ b/logger/__init__.py @@ -0,0 +1,4 @@ +from logger.writer import create_log_config +from logger.middleware import LoggingMiddleware + +__all__ = ["create_log_config", "LoggingMiddleware"] \ No newline at end of file diff --git a/logger/formatter.py b/logger/formatter.py new file mode 100644 index 0000000..9c18483 --- /dev/null +++ b/logger/formatter.py @@ -0,0 +1,69 @@ +import datetime +import json +import logging +import traceback + +from logger.models import BaseJsonLogSchema + + +class JSONLogFormatter(logging.Formatter): + """ + Кастомизированный класс-форматер для логов в формате json + """ + + def format(self, record: logging.LogRecord, *args, **kwargs) -> str: + """ + Преобразование объект журнала в json + + :param record: объект журнала + :return: строка журнала в JSON формате + """ + log_object: dict = self._format_log_object(record) + return json.dumps(log_object, ensure_ascii=False) + + @staticmethod + def _format_log_object(record: logging.LogRecord) -> dict: + """ + Перевод записи объекта журнала + в json формат с необходимым перечнем полей + + :param record: объект журнала + :return: Словарь с объектами журнала + """ + now = ( + datetime.datetime.fromtimestamp(record.created) + .astimezone() + .replace(microsecond=0) + .isoformat() + ) + message = record.getMessage() + duration_ms = record.duration if hasattr(record, "duration") else record.msecs + # Инициализация тела журнала + json_log_fields = BaseJsonLogSchema( + thread=record.process, + timestamp=now, + level=record.levelno, + level_name=record.levelname, + message=message, + source=record.name, + duration_ms=duration_ms, + ) + + if hasattr(record, "props"): + json_log_fields.props = record.props + + if record.exc_info: + json_log_fields.exceptions = traceback.format_exception(*record.exc_info) + + elif record.exc_text: + json_log_fields.exceptions = record.exc_text + # Преобразование Pydantic объекта в словарь + json_log_object = json_log_fields.dict( + exclude_unset=True, + by_alias=True, + ) + # Соединение дополнительных полей логирования + if hasattr(record, "request_json_fields"): + json_log_object.update(record.request_json_fields) + + return json_log_object diff --git a/logger/middleware.py b/logger/middleware.py new file mode 100644 index 0000000..63bc508 --- /dev/null +++ b/logger/middleware.py @@ -0,0 +1,96 @@ +import http +import math +import time + +from starlette.middleware.base import RequestResponseEndpoint, BaseHTTPMiddleware +from starlette.requests import Request +from starlette.responses import Response +from starlette.types import Message, ASGIApp +import logging + +from logger.models import RequestJsonLogSchema + + +class LoggingMiddleware(BaseHTTPMiddleware): + + EMPTY_VALUE = "None" + logger: logging.Logger + port: str + """ + Middleware для обработки запросов и ответов с целью журналирования + """ + + def __init__(self, app: ASGIApp, port: str, logger: logging.Logger): + super().__init__(app) + self.port = port + self.logger = logger + + @staticmethod + async def set_body(request: Request, body: bytes) -> None: + async def receive() -> Message: + return {"type": "http.request", "body": body} + + request._receive = receive + + async def dispatch( + self, request: Request, call_next: RequestResponseEndpoint, *args, **kwargs + ): + start_time = time.time() + exception_object = None + + server: tuple = request.get("server", ("localhost", self.port)) + request_headers: dict = dict(request.headers.items()) + try: + response = await call_next(request) + except Exception as ex: + response_body = bytes(http.HTTPStatus.INTERNAL_SERVER_ERROR.phrase.encode()) + response = Response( + content=response_body, + status_code=http.HTTPStatus.INTERNAL_SERVER_ERROR.real, + ) + exception_object = ex + response_headers = {} + else: + response_headers = dict(response.headers.items()) + response_body = response.body + response = Response( + content=response_body, + status_code=response.status_code, + headers=dict(response.headers), + media_type=response.media_type, + ) + duration_ms: int = math.ceil((time.time() - start_time) * 1000) + request_json_fields = RequestJsonLogSchema( + level_name = "INFO" if 1<= response.status_code <= 499 else "ERROR", + request_uri=str(request.url), + request_referer=request_headers.get( + "referer", LoggingMiddleware.EMPTY_VALUE + ), + request_method=request.method, + request_path=request.url.path, + request_host=f"{server[0]}:{server[1]}", + request_size=int(request_headers.get("content-length", 0)), + request_content_type=request_headers.get( + "content-type", LoggingMiddleware.EMPTY_VALUE + ), + request_direction="in", + remote_ip=request.client[0], + remote_port=request.client[1], + response_status_code=response.status_code, + response_size=int(response_headers.get("content-length", 0)), + duration_ms=duration_ms, + ).dict() + message = ( + f'{"Ошибка" if exception_object else "Ответ"} ' + f"с кодом {response.status_code} " + f'на запрос {request.method} "{str(request.url)}"' + ) + self.logger.info( + message, + extra={ + "request_json_fields": request_json_fields, + "to_mask": True, + }, + exc_info=exception_object, + ) + return response diff --git a/logger/models.py b/logger/models.py new file mode 100644 index 0000000..a604768 --- /dev/null +++ b/logger/models.py @@ -0,0 +1,44 @@ +from pydantic import BaseModel, Field + + +class BaseJsonLogSchema(BaseModel): + """ + Схема основного тела лога в формате JSON + """ + + thread: int | str + level: int + level_name: str + message: str + source: str + timestamp: str = Field(..., alias="@timestamp") + duration_ms: int + exceptions: list[str] | str = None + trace_id: str | None = None + span_id: str | None = None + parent_id: str| None = None + + class Config: + allow_population_by_field_name = True + + +class RequestJsonLogSchema(BaseModel): + """ + Схема части запросов-ответов лога в формате JSON + """ + + level_name: str + request_uri: str + request_referer: str + request_protocol: str + request_method: str + request_path: str + request_host: str + request_size: int + request_content_type: str + request_direction: str + remote_ip: str + remote_port: str + response_status_code: int + response_size: int + duration_ms: int diff --git a/logger/writer.py b/logger/writer.py new file mode 100644 index 0000000..787a807 --- /dev/null +++ b/logger/writer.py @@ -0,0 +1,41 @@ +from typing import Any + + +def create_log_config(project_name: str, *, log_level: str) -> dict[str, Any]: + return { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "json": { + "()": "logging.logger.formatter.JSONLogFormatter", + }, + }, + "handlers": { + "json": { + "formatter": "json", + "class": "asynclog.AsyncLogDispatcher", + "func": "logging.logger.writer.write_log", + }, + }, + "loggers": { + project_name: { + "handlers": ["json"], + "level": log_level.upper(), + "propagate": False, + }, + "uvicorn": { + "handlers": ["json"], + "level": log_level.upper(), + "propagate": False, + }, + "uvicorn.access": { + "handlers": ["json"], + "level": log_level.upper(), + "propagate": False, + }, + }, + } + + +def write_log(message): + print(message) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ec7e9a2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +pydantic +starlette +asynclog \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..011a45f --- /dev/null +++ b/setup.py @@ -0,0 +1,18 @@ +from setuptools import setup, find_packages + +with open("README.md", "r") as readme_file: + readme = readme_file.read() + +setup( + name="logging_profcomff", + version="2023.02.23", + author="Semyon Grigoriev", + long_description=readme, + long_description_content_type="text/markdown", + url="https://github.com/profcomff/logging/tree/init", + packages=find_packages(), + install_requires=["pydantic", "starlette", "setuptools", "asynclog"], + classifiers=[ + "Programming Language :: Python :: 3.11", + ], +) From 9591d6248fc3b39af3a15d5c1ddd4a152429d24d Mon Sep 17 00:00:00 2001 From: semen603089 Date: Fri, 10 Mar 2023 02:15:48 +0300 Subject: [PATCH 02/16] fix --- logger/models.py | 1 - logger/writer.py | 10 ++++------ requirements.txt | 3 +-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/logger/models.py b/logger/models.py index a604768..806198f 100644 --- a/logger/models.py +++ b/logger/models.py @@ -30,7 +30,6 @@ class RequestJsonLogSchema(BaseModel): level_name: str request_uri: str request_referer: str - request_protocol: str request_method: str request_path: str request_host: str diff --git a/logger/writer.py b/logger/writer.py index 787a807..ece193d 100644 --- a/logger/writer.py +++ b/logger/writer.py @@ -1,7 +1,7 @@ from typing import Any -def create_log_config(project_name: str, *, log_level: str) -> dict[str, Any]: +def create_log_config(*, log_level: str) -> dict[str, Any]: return { "version": 1, "disable_existing_loggers": False, @@ -13,12 +13,12 @@ def create_log_config(project_name: str, *, log_level: str) -> dict[str, Any]: "handlers": { "json": { "formatter": "json", - "class": "asynclog.AsyncLogDispatcher", - "func": "logging.logger.writer.write_log", + 'class': 'logging.StreamHandler', + 'stream': 'ext://sys.stdout' }, }, "loggers": { - project_name: { + "": { "handlers": ["json"], "level": log_level.upper(), "propagate": False, @@ -37,5 +37,3 @@ def create_log_config(project_name: str, *, log_level: str) -> dict[str, Any]: } -def write_log(message): - print(message) diff --git a/requirements.txt b/requirements.txt index ec7e9a2..60046fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ pydantic -starlette -asynclog \ No newline at end of file +starlette \ No newline at end of file From 656145876b1880c559f8a653e245119c266854bf Mon Sep 17 00:00:00 2001 From: semen603089 Date: Fri, 10 Mar 2023 02:16:39 +0300 Subject: [PATCH 03/16] fix --- logger/middleware.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/logger/middleware.py b/logger/middleware.py index 63bc508..3c2a90f 100644 --- a/logger/middleware.py +++ b/logger/middleware.py @@ -52,7 +52,9 @@ async def dispatch( response_headers = {} else: response_headers = dict(response.headers.items()) - response_body = response.body + response_body = b'' + async for chunk in response.body_iterator: + response_body += chunk response = Response( content=response_body, status_code=response.status_code, From cca3b4b966cfc477d49286204d4419c04a3b7e82 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Fri, 10 Mar 2023 02:18:29 +0300 Subject: [PATCH 04/16] fix --- logger/writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger/writer.py b/logger/writer.py index ece193d..e3421be 100644 --- a/logger/writer.py +++ b/logger/writer.py @@ -7,7 +7,7 @@ def create_log_config(*, log_level: str) -> dict[str, Any]: "disable_existing_loggers": False, "formatters": { "json": { - "()": "logging.logger.formatter.JSONLogFormatter", + "()": "logger.formatter.JSONLogFormatter", }, }, "handlers": { From 395e05c85f976f108bfb05582423854fe7c60843 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Fri, 10 Mar 2023 02:20:55 +0300 Subject: [PATCH 05/16] fix --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 011a45f..bdbe0d2 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="logging_profcomff", - version="2023.02.23", + version="2023.03.10", author="Semyon Grigoriev", long_description=readme, long_description_content_type="text/markdown", From 2539087209cd8d3b88b62ea73a122db37c33ae06 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Fri, 10 Mar 2023 02:24:28 +0300 Subject: [PATCH 06/16] fix --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index bdbe0d2..cfd0af8 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ long_description_content_type="text/markdown", url="https://github.com/profcomff/logging/tree/init", packages=find_packages(), - install_requires=["pydantic", "starlette", "setuptools", "asynclog"], + install_requires=["pydantic", "starlette", "setuptools"], classifiers=[ "Programming Language :: Python :: 3.11", ], From dfd599d924e222e3e15a3b7dd5710f3bd89879c5 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 11 Mar 2023 17:39:14 +0300 Subject: [PATCH 07/16] fix --- gunicorn_logging_examples/logging_prod.conf | 38 ++++++++ gunicorn_logging_examples/logging_test.conf | 38 ++++++++ logger/__init__.py | 4 - logger/formatter.py | 32 +++---- logger/middleware.py | 98 --------------------- logger/models.py | 43 --------- logger/writer.py | 39 -------- 7 files changed, 89 insertions(+), 203 deletions(-) create mode 100644 gunicorn_logging_examples/logging_prod.conf create mode 100644 gunicorn_logging_examples/logging_test.conf delete mode 100644 logger/middleware.py delete mode 100644 logger/models.py delete mode 100644 logger/writer.py diff --git a/gunicorn_logging_examples/logging_prod.conf b/gunicorn_logging_examples/logging_prod.conf new file mode 100644 index 0000000..0030579 --- /dev/null +++ b/gunicorn_logging_examples/logging_prod.conf @@ -0,0 +1,38 @@ +[loggers] +keys=root,gunicorn.error,gunicorn.access + +[handlers] +keys=all + +[formatters] +keys=json + +[logger_root] +level=INFO +handlers=all + +[logger_gunicorn.error] +level=INFO +handlers=all +propagate=0 +qualname=gunicorn.error +formatter=json + +[logger_gunicorn.access] +level=INFO +handlers=all +propagate=0 +qualname=gunicorn.access +formatter=json + + +[handler_all] +class=StreamHandler +formatter=json +level=INFO +args=(sys.stdout,) + +[formatter_json] +class=logger.formatter.JSONLogFormatter + + diff --git a/gunicorn_logging_examples/logging_test.conf b/gunicorn_logging_examples/logging_test.conf new file mode 100644 index 0000000..d8970af --- /dev/null +++ b/gunicorn_logging_examples/logging_test.conf @@ -0,0 +1,38 @@ +[loggers] +keys=root,gunicorn.error,gunicorn.access + +[handlers] +keys=all + +[formatters] +keys=json + +[logger_root] +level=DEBUG +handlers=all + +[logger_gunicorn.error] +level=DEBUG +handlers=all +propagate=0 +qualname=gunicorn.error +formatter=json + +[logger_gunicorn.access] +level=DEBUG +handlers=all +propagate=0 +qualname=gunicorn.access +formatter=json + + +[handler_all] +class=StreamHandler +formatter=json +level=DEBUG +args=(sys.stdout,) + +[formatter_json] +class=logger.formatter.JSONLogFormatter + + diff --git a/logger/__init__.py b/logger/__init__.py index 9d8735e..e69de29 100644 --- a/logger/__init__.py +++ b/logger/__init__.py @@ -1,4 +0,0 @@ -from logger.writer import create_log_config -from logger.middleware import LoggingMiddleware - -__all__ = ["create_log_config", "LoggingMiddleware"] \ No newline at end of file diff --git a/logger/formatter.py b/logger/formatter.py index 9c18483..bda739a 100644 --- a/logger/formatter.py +++ b/logger/formatter.py @@ -3,8 +3,6 @@ import logging import traceback -from logger.models import BaseJsonLogSchema - class JSONLogFormatter(logging.Formatter): """ @@ -39,15 +37,16 @@ def _format_log_object(record: logging.LogRecord) -> dict: message = record.getMessage() duration_ms = record.duration if hasattr(record, "duration") else record.msecs # Инициализация тела журнала - json_log_fields = BaseJsonLogSchema( - thread=record.process, - timestamp=now, - level=record.levelno, - level_name=record.levelname, - message=message, - source=record.name, - duration_ms=duration_ms, - ) + json_log_fields = dict() + json_log_fields["thread"] = record.process + json_log_fields["timestamp"] = now + json_log_fields["level"] = record.levelno + json_log_fields["level_name"] = record.levelname + json_log_fields["message"] = message + json_log_fields["source"] = record.name + json_log_fields["duration_ms"] = duration_ms + json_log_fields["func"] = record.funcName + json_log_fields["file"] = record.filename if hasattr(record, "props"): json_log_fields.props = record.props @@ -57,13 +56,8 @@ def _format_log_object(record: logging.LogRecord) -> dict: elif record.exc_text: json_log_fields.exceptions = record.exc_text - # Преобразование Pydantic объекта в словарь - json_log_object = json_log_fields.dict( - exclude_unset=True, - by_alias=True, - ) - # Соединение дополнительных полей логирования + if hasattr(record, "request_json_fields"): - json_log_object.update(record.request_json_fields) + json_log_fields.update(record.request_json_fields) - return json_log_object + return json_log_fields diff --git a/logger/middleware.py b/logger/middleware.py deleted file mode 100644 index 3c2a90f..0000000 --- a/logger/middleware.py +++ /dev/null @@ -1,98 +0,0 @@ -import http -import math -import time - -from starlette.middleware.base import RequestResponseEndpoint, BaseHTTPMiddleware -from starlette.requests import Request -from starlette.responses import Response -from starlette.types import Message, ASGIApp -import logging - -from logger.models import RequestJsonLogSchema - - -class LoggingMiddleware(BaseHTTPMiddleware): - - EMPTY_VALUE = "None" - logger: logging.Logger - port: str - """ - Middleware для обработки запросов и ответов с целью журналирования - """ - - def __init__(self, app: ASGIApp, port: str, logger: logging.Logger): - super().__init__(app) - self.port = port - self.logger = logger - - @staticmethod - async def set_body(request: Request, body: bytes) -> None: - async def receive() -> Message: - return {"type": "http.request", "body": body} - - request._receive = receive - - async def dispatch( - self, request: Request, call_next: RequestResponseEndpoint, *args, **kwargs - ): - start_time = time.time() - exception_object = None - - server: tuple = request.get("server", ("localhost", self.port)) - request_headers: dict = dict(request.headers.items()) - try: - response = await call_next(request) - except Exception as ex: - response_body = bytes(http.HTTPStatus.INTERNAL_SERVER_ERROR.phrase.encode()) - response = Response( - content=response_body, - status_code=http.HTTPStatus.INTERNAL_SERVER_ERROR.real, - ) - exception_object = ex - response_headers = {} - else: - response_headers = dict(response.headers.items()) - response_body = b'' - async for chunk in response.body_iterator: - response_body += chunk - response = Response( - content=response_body, - status_code=response.status_code, - headers=dict(response.headers), - media_type=response.media_type, - ) - duration_ms: int = math.ceil((time.time() - start_time) * 1000) - request_json_fields = RequestJsonLogSchema( - level_name = "INFO" if 1<= response.status_code <= 499 else "ERROR", - request_uri=str(request.url), - request_referer=request_headers.get( - "referer", LoggingMiddleware.EMPTY_VALUE - ), - request_method=request.method, - request_path=request.url.path, - request_host=f"{server[0]}:{server[1]}", - request_size=int(request_headers.get("content-length", 0)), - request_content_type=request_headers.get( - "content-type", LoggingMiddleware.EMPTY_VALUE - ), - request_direction="in", - remote_ip=request.client[0], - remote_port=request.client[1], - response_status_code=response.status_code, - response_size=int(response_headers.get("content-length", 0)), - duration_ms=duration_ms, - ).dict() - message = ( - f'{"Ошибка" if exception_object else "Ответ"} ' - f"с кодом {response.status_code} " - f'на запрос {request.method} "{str(request.url)}"' - ) - self.logger.info( - message, - extra={ - "request_json_fields": request_json_fields, - "to_mask": True, - }, - exc_info=exception_object, - ) - return response diff --git a/logger/models.py b/logger/models.py deleted file mode 100644 index 806198f..0000000 --- a/logger/models.py +++ /dev/null @@ -1,43 +0,0 @@ -from pydantic import BaseModel, Field - - -class BaseJsonLogSchema(BaseModel): - """ - Схема основного тела лога в формате JSON - """ - - thread: int | str - level: int - level_name: str - message: str - source: str - timestamp: str = Field(..., alias="@timestamp") - duration_ms: int - exceptions: list[str] | str = None - trace_id: str | None = None - span_id: str | None = None - parent_id: str| None = None - - class Config: - allow_population_by_field_name = True - - -class RequestJsonLogSchema(BaseModel): - """ - Схема части запросов-ответов лога в формате JSON - """ - - level_name: str - request_uri: str - request_referer: str - request_method: str - request_path: str - request_host: str - request_size: int - request_content_type: str - request_direction: str - remote_ip: str - remote_port: str - response_status_code: int - response_size: int - duration_ms: int diff --git a/logger/writer.py b/logger/writer.py deleted file mode 100644 index e3421be..0000000 --- a/logger/writer.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing import Any - - -def create_log_config(*, log_level: str) -> dict[str, Any]: - return { - "version": 1, - "disable_existing_loggers": False, - "formatters": { - "json": { - "()": "logger.formatter.JSONLogFormatter", - }, - }, - "handlers": { - "json": { - "formatter": "json", - 'class': 'logging.StreamHandler', - 'stream': 'ext://sys.stdout' - }, - }, - "loggers": { - "": { - "handlers": ["json"], - "level": log_level.upper(), - "propagate": False, - }, - "uvicorn": { - "handlers": ["json"], - "level": log_level.upper(), - "propagate": False, - }, - "uvicorn.access": { - "handlers": ["json"], - "level": log_level.upper(), - "propagate": False, - }, - }, - } - - From d1ee03e0b75f70013a9e6756ce21f9ff5a6a1d15 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 11 Mar 2023 17:41:28 +0300 Subject: [PATCH 08/16] fix --- logger/formatter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logger/formatter.py b/logger/formatter.py index bda739a..6c5877d 100644 --- a/logger/formatter.py +++ b/logger/formatter.py @@ -52,10 +52,10 @@ def _format_log_object(record: logging.LogRecord) -> dict: json_log_fields.props = record.props if record.exc_info: - json_log_fields.exceptions = traceback.format_exception(*record.exc_info) + json_log_fields["exceptions"] = traceback.format_exception(*record.exc_info) elif record.exc_text: - json_log_fields.exceptions = record.exc_text + json_log_fields["exceptions"] = record.exc_text if hasattr(record, "request_json_fields"): json_log_fields.update(record.request_json_fields) From 341e1eef1275ecbaeb01a1bd224f042823e89b37 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 11 Mar 2023 17:46:28 +0300 Subject: [PATCH 09/16] fix --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index cfd0af8..948bcfe 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ long_description_content_type="text/markdown", url="https://github.com/profcomff/logging/tree/init", packages=find_packages(), - install_requires=["pydantic", "starlette", "setuptools"], + install_requires=["setuptools"], classifiers=[ "Programming Language :: Python :: 3.11", ], From 4efc09213a280fc7b77972111553df04ba22afca Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 11 Mar 2023 17:56:19 +0300 Subject: [PATCH 10/16] fix --- requirements.txt | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 60046fa..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pydantic -starlette \ No newline at end of file From b7dbfcc5639096ba76fad8ab12ff52d94c3aa33d Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 11 Mar 2023 18:03:51 +0300 Subject: [PATCH 11/16] fix --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 948bcfe..ae98e45 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ author="Semyon Grigoriev", long_description=readme, long_description_content_type="text/markdown", - url="https://github.com/profcomff/logging/tree/init", + url="https://github.com/profcomff/logging-lib", packages=find_packages(), install_requires=["setuptools"], classifiers=[ From 222c10ad5da31ec048f804913c7a170e94476fcb Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 11 Mar 2023 20:41:00 +0300 Subject: [PATCH 12/16] fix --- gunicorn_logging_examples/logging_prod.conf | 3 --- gunicorn_logging_examples/logging_test.conf | 3 --- logger/formatter.py | 6 +++++- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/gunicorn_logging_examples/logging_prod.conf b/gunicorn_logging_examples/logging_prod.conf index 0030579..971f309 100644 --- a/gunicorn_logging_examples/logging_prod.conf +++ b/gunicorn_logging_examples/logging_prod.conf @@ -25,7 +25,6 @@ propagate=0 qualname=gunicorn.access formatter=json - [handler_all] class=StreamHandler formatter=json @@ -34,5 +33,3 @@ args=(sys.stdout,) [formatter_json] class=logger.formatter.JSONLogFormatter - - diff --git a/gunicorn_logging_examples/logging_test.conf b/gunicorn_logging_examples/logging_test.conf index d8970af..8904c93 100644 --- a/gunicorn_logging_examples/logging_test.conf +++ b/gunicorn_logging_examples/logging_test.conf @@ -25,7 +25,6 @@ propagate=0 qualname=gunicorn.access formatter=json - [handler_all] class=StreamHandler formatter=json @@ -34,5 +33,3 @@ args=(sys.stdout,) [formatter_json] class=logger.formatter.JSONLogFormatter - - diff --git a/logger/formatter.py b/logger/formatter.py index 6c5877d..1bbdd5e 100644 --- a/logger/formatter.py +++ b/logger/formatter.py @@ -47,13 +47,17 @@ def _format_log_object(record: logging.LogRecord) -> dict: json_log_fields["duration_ms"] = duration_ms json_log_fields["func"] = record.funcName json_log_fields["file"] = record.filename + empty_record = logging.LogRecord( + str(), int(), str(), int(), object(), exc_info=None, args=(object,) + ) + keys = set(dir(record)) - set(dir(empty_record)) + json_log_fields.update({key: getattr(record, key) for key in keys}) if hasattr(record, "props"): json_log_fields.props = record.props if record.exc_info: json_log_fields["exceptions"] = traceback.format_exception(*record.exc_info) - elif record.exc_text: json_log_fields["exceptions"] = record.exc_text From 0170889fc90cf9f3d4c78c15a2b889129f45dc5b Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 11 Mar 2023 20:55:57 +0300 Subject: [PATCH 13/16] readme --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ffb656e..a9441cf 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,12 @@ -# auth-lib +# logging-lib Библиотека для логирования сервисов Твой ФФ! + +## Как подключить +1. В requirements.txt добавьте logging-profcomff +2. Скопируйте из /gunicorn_logging_examples обе конфигурации +3. Вставьте их в корень проекта +4. Добаввьте в Dockerfile ARG conf_file +4. В Dockerfile добавьте GUNICORN_CMD_ARGS в качестве env переменной +5. Пропишите туда "--log-config $conf_file" +6. В Actions в запуск добавьте(прод) --build-args: docker build --build-arg conf_file=logging_prod.conf +7. В Actions в запуск добавьте(тест) --build-args: docker build --build-arg conf_file=logging_test.conf From 86572d4bdb58e9fa60cca9ed41b28926236c002e Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 11 Mar 2023 20:58:12 +0300 Subject: [PATCH 14/16] readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a9441cf..de71203 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ 2. Скопируйте из /gunicorn_logging_examples обе конфигурации 3. Вставьте их в корень проекта 4. Добаввьте в Dockerfile ARG conf_file -4. В Dockerfile добавьте GUNICORN_CMD_ARGS в качестве env переменной -5. Пропишите туда "--log-config $conf_file" -6. В Actions в запуск добавьте(прод) --build-args: docker build --build-arg conf_file=logging_prod.conf -7. В Actions в запуск добавьте(тест) --build-args: docker build --build-arg conf_file=logging_test.conf +5. В Dockerfile добавьте GUNICORN_CMD_ARGS в качестве env переменной +6. Пропишите туда "--log-config $conf_file" +7. В Actions в запуск добавьте(прод) --build-args: docker build --build-arg conf_file=logging_prod.conf +8. В Actions в запуск добавьте(тест) --build-args: docker build --build-arg conf_file=logging_test.conf From ec226474062755eafa86d28e85d1ef1682aa0ece Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 11 Mar 2023 21:27:17 +0300 Subject: [PATCH 15/16] review --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index de71203..0038b45 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ 1. В requirements.txt добавьте logging-profcomff 2. Скопируйте из /gunicorn_logging_examples обе конфигурации 3. Вставьте их в корень проекта -4. Добаввьте в Dockerfile ARG conf_file +4. Добаввьте в Dockerfile ARG CONF_FILE 5. В Dockerfile добавьте GUNICORN_CMD_ARGS в качестве env переменной -6. Пропишите туда "--log-config $conf_file" -7. В Actions в запуск добавьте(прод) --build-args: docker build --build-arg conf_file=logging_prod.conf -8. В Actions в запуск добавьте(тест) --build-args: docker build --build-arg conf_file=logging_test.conf +6. Пропишите туда "--log-config $CONF_FILE" +7. В Actions в запуск добавьте(прод) --build-args: docker build --build-arg CONF_FILE=logging_prod.conf +8. В Actions в запуск добавьте(тест) --build-args: docker build --build-arg CONF_FILE=logging_test.conf From 2afdc0c2c58cf8f270e6673704ef15f5d3a91f34 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 11 Mar 2023 21:33:06 +0300 Subject: [PATCH 16/16] review --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ae98e45..f2e7d23 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="logging_profcomff", - version="2023.03.10", + version="2023.03.11", author="Semyon Grigoriev", long_description=readme, long_description_content_type="text/markdown",