Initial version

This commit is contained in:
Marcel Schwarz 2024-01-25 15:08:31 +01:00
parent 732273e9b5
commit 6341b81ff8
11 changed files with 187 additions and 1 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
dist/
*.egg-info/

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.12" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/icaotix_python_logging.iml" filepath="$PROJECT_DIR$/.idea/icaotix_python_logging.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -1,2 +1,3 @@
# icaotix_python_logging
# icaotix python logging
This lib helps in setting up a json logger for stdout for basically all projects

18
pyproject.toml Normal file
View File

@ -0,0 +1,18 @@
[project]
name = "icaotix_python_logging"
version = "0.0.1"
authors = [
{ name = "Marcel Schwarz", email = "admin@icaotix.de" },
]
description = "A small package which helps to setup a logger"
readme = "README.md"
requires-python = ">=3.9"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
[project.urls]
Homepage = "https://git.icaotix.de/icaotix/icaotix_python_logging"
Issues = "https://git.icaotix.de/icaotix/icaotix_python_logging/issues"

View File

View File

@ -0,0 +1,122 @@
# Logging
import atexit
import datetime
import datetime as dt
import json
import logging
import logging.config
import logging.handlers
import queue
import sys
from typing import override
LOG_RECORD_BUILTIN_ATTRS = {
"args",
"asctime",
"created",
"exc_info",
"exc_text",
"filename",
"funcName",
"levelname",
"levelno",
"lineno",
"module",
"msecs",
"message",
"msg",
"name",
"pathname",
"process",
"processName",
"relativeCreated",
"stack_info",
"thread",
"threadName",
"taskName",
}
class JSONFormatter(logging.Formatter):
def __init__(
self,
*,
fmt_keys: dict[str, str] | None = None,
):
super().__init__()
self.fmt_keys = fmt_keys if fmt_keys is not None else {}
@override
def format(self, record: logging.LogRecord) -> str:
message = self._prepare_log_dict(record)
return json.dumps(message, default=str)
def _prepare_log_dict(self, record: logging.LogRecord):
always_fields = {
"message": record.getMessage(),
"timestamp": dt.datetime.fromtimestamp(
record.created, tz=dt.timezone.utc
).isoformat(),
}
if record.exc_info is not None:
always_fields["exc_info"] = self.formatException(record.exc_info)
if record.stack_info is not None:
always_fields["stack_info"] = self.formatStack(record.stack_info)
message = {
key: msg_val
if (msg_val := always_fields.pop(val, None)) is not None
else getattr(record, val)
for key, val in self.fmt_keys.items()
}
message.update(always_fields)
for key, val in record.__dict__.items():
if key not in LOG_RECORD_BUILTIN_ATTRS:
if message.get("extra") is None:
message["extra"] = dict()
message["extra"][key] = val
return message
def get_logger(name: str) -> logging.Logger:
json_formatter = JSONFormatter(fmt_keys={
"level": "levelname",
"message": "message",
"timestamp": "timestamp",
"logger": "name",
"pathname": "pathname",
"module": "module",
"function": "funcName",
"process_id": "process",
"line": "lineno",
"thread_name": "threadName"
})
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(json_formatter)
queue_handler_queue = queue.Queue()
queue_handler = logging.handlers.QueueHandler(queue_handler_queue)
queue_handler.listener = logging.handlers.QueueListener(queue_handler_queue, stdout_handler,
respect_handler_level=True)
queue_handler.listener.start()
atexit.register(queue_handler.listener.stop)
logger = logging.getLogger(name) # __name__ is a common choice
logger.setLevel(logging.DEBUG)
logger.handlers = [queue_handler]
return logger
def main():
log = get_logger(__name__)
log.info("Was geht\n los da rein", extra={"asd": 1})
log.debug("Was geht\n los da rein",
extra={"asd": 1, "laaa": "", "da": datetime.datetime.now(tz=datetime.UTC), "set": {12, 45}})
if __name__ == '__main__':
main()