Initial version
This commit is contained in:
parent
732273e9b5
commit
6341b81ff8
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
dist/
|
||||||
|
*.egg-info/
|
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal 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
|
8
.idea/icaotix_python_logging.iml
generated
Normal file
8
.idea/icaotix_python_logging.iml
generated
Normal 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>
|
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal 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
generated
Normal file
7
.idea/misc.xml
generated
Normal 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
generated
Normal file
8
.idea/modules.xml
generated
Normal 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
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -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
18
pyproject.toml
Normal 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"
|
0
src/icaotix_python_logging/__init__.py
Normal file
0
src/icaotix_python_logging/__init__.py
Normal file
122
src/icaotix_python_logging/logger.py
Normal file
122
src/icaotix_python_logging/logger.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user