izihawa-loglib 1.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.1
2
+ Name: izihawa_loglib
3
+ Version: 1.1.1
4
+ Summary: Izihawa log utilities
5
+ Author: Pasha Podolsky
6
+ Author-email: ppodolsky@me.com
7
+ Requires-Python: >=3.8.1,<3.13
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Dist: confuse (>=2.0.1,<3.0.0)
14
+ Requires-Dist: izihawa-types (>=0.1.4,<0.2.0)
15
+ Requires-Dist: izihawa-utils (>=1.2.1,<2.0.0)
16
+ Requires-Dist: orjson (>=3.9.10,<4.0.0)
17
+ Description-Content-Type: text/markdown
18
+
19
+ # izihawa_netlib
20
+
@@ -0,0 +1 @@
1
+ # izihawa_netlib
@@ -0,0 +1,34 @@
1
+ import logging
2
+ import logging.config
3
+ import sys
4
+
5
+ from izihawa_utils.exceptions import BaseError
6
+ from izihawa_utils.file import mkdir_p
7
+
8
+ from .handlers import QueueHandler
9
+
10
+
11
+ def configure_logging(config: dict, make_path: bool = False, default_level=logging.INFO):
12
+ if config.get("application", {}).get("debug", False) or "logging" not in config:
13
+ logging.basicConfig(stream=sys.stdout, level=default_level)
14
+ else:
15
+ if make_path:
16
+ mkdir_p(config["log_path"])
17
+ logging.config.dictConfig(config["logging"])
18
+
19
+
20
+ def error_log(e, level=logging.ERROR, **fields):
21
+ level = getattr(e, "level", level)
22
+ if isinstance(e, BaseError):
23
+ e = e.as_internal_dict()
24
+ e.update(fields)
25
+ elif fields:
26
+ e = {"error": repr(e), **fields}
27
+ logging.getLogger("error").log(msg=str(e), level=level)
28
+
29
+
30
+ __all__ = [
31
+ "QueueHandler",
32
+ "configure_logging",
33
+ "error_log",
34
+ ]
@@ -0,0 +1,100 @@
1
+ import dataclasses
2
+ import datetime
3
+ import logging
4
+ import os
5
+ import pprint
6
+ import time
7
+ import traceback
8
+ import typing
9
+
10
+ import orjson as json
11
+ from izihawa_utils.exceptions import BaseError
12
+
13
+ DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
14
+
15
+
16
+ class BaseFormatter(logging.Formatter):
17
+ def _prepare(self, record):
18
+ if isinstance(record.msg, BaseError):
19
+ return record.msg.as_internal_dict()
20
+ elif isinstance(record.msg, typing.Dict) or dataclasses.is_dataclass(
21
+ record.msg
22
+ ):
23
+ return record.msg
24
+ else:
25
+ return dict(message=super().format(record))
26
+
27
+ def format(self, record):
28
+ log_record = self._prepare(record)
29
+ return json.dumps(log_record).decode()
30
+
31
+
32
+ class DefaultHttpFormatter(BaseFormatter):
33
+ def _prepare(self, record):
34
+ log_record = super()._prepare(record)
35
+
36
+ timestamp = time.time()
37
+ formatted_datetime = datetime.datetime.fromtimestamp(timestamp).strftime(
38
+ DATETIME_FORMAT
39
+ )
40
+ user_ip = getattr(record, "user_ip", None)
41
+ request_id = getattr(record, "request_id", None)
42
+ method = getattr(record, "method", None)
43
+ path = getattr(record, "path", None)
44
+
45
+ log_record.update(
46
+ unixtime=int(timestamp),
47
+ timestamp=int(timestamp * 1_000_000),
48
+ datetime=formatted_datetime,
49
+ process=os.getpid(),
50
+ )
51
+
52
+ if user_ip:
53
+ log_record["user_ip"] = user_ip
54
+ if request_id:
55
+ log_record["request_id"] = request_id
56
+ if method:
57
+ log_record["method"] = method
58
+ if path:
59
+ log_record["path"] = path
60
+
61
+ return log_record
62
+
63
+ def format(self, record):
64
+ log_record = self._prepare(record)
65
+ return json.dumps(log_record).decode()
66
+
67
+
68
+ class DefaultFormatter(BaseFormatter):
69
+ def _prepare(self, record):
70
+ log_record = super()._prepare(record)
71
+
72
+ timestamp = time.time()
73
+ formatted_datetime = datetime.datetime.fromtimestamp(timestamp).strftime(
74
+ DATETIME_FORMAT
75
+ )
76
+
77
+ log_record.update(
78
+ unixtime=int(timestamp),
79
+ timestamp=int(timestamp * 1_000_000),
80
+ datetime=formatted_datetime,
81
+ process=os.getpid(),
82
+ )
83
+ return log_record
84
+
85
+ def format(self, record):
86
+ log_record = self._prepare(record)
87
+ return json.dumps(log_record).decode()
88
+
89
+
90
+ class TracebackFormatter(DefaultFormatter):
91
+ def format(self, record):
92
+ log_record = self._prepare(record)
93
+ value = pprint.pformat(log_record, indent=2)
94
+ if traceback.sys.exc_info()[0] is not None:
95
+ value += "\n" + traceback.format_exc()
96
+ return value
97
+
98
+
99
+ default_formatter = DefaultFormatter()
100
+ default_traceback_formatter = TracebackFormatter()
@@ -0,0 +1,44 @@
1
+ import logging.handlers
2
+ import os
3
+ import queue
4
+
5
+ from izihawa_types.var import varstr
6
+
7
+
8
+ class QueueHandler(logging.handlers.QueueHandler):
9
+ def __init__(self, *handlers):
10
+ self._queue = queue.Queue(-1)
11
+ self._listener = logging.handlers.QueueListener(
12
+ self._queue, *handlers, respect_handler_level=True
13
+ )
14
+ self.setLevel("INFO")
15
+
16
+ super().__init__(self._queue)
17
+ self._listener.start()
18
+
19
+ def stop(self):
20
+ self._listener.stop()
21
+
22
+ def prepare(self, record):
23
+ return record
24
+
25
+
26
+ class BaseFileHandler(logging.handlers.WatchedFileHandler):
27
+ def _open(self):
28
+ file = super()._open()
29
+ os.chmod(self.baseFilename, 0o644)
30
+ return file
31
+
32
+
33
+ class BaseBinaryFileHandler(BaseFileHandler):
34
+ def __init__(self, *args, **kwargs):
35
+ super().__init__(*args, **kwargs, mode="ab+")
36
+
37
+ def emit(self, record):
38
+ try:
39
+ self.stream.write(varstr(record.msg))
40
+ self.flush()
41
+ except RecursionError:
42
+ raise
43
+ except Exception:
44
+ self.handleError(record)
@@ -0,0 +1,27 @@
1
+ [tool.poetry]
2
+ name = "izihawa_loglib"
3
+ version = "1.1.1"
4
+ description = "Izihawa log utilities"
5
+ authors = ["Pasha Podolsky <ppodolsky@me.com>"]
6
+ readme = "README.md"
7
+ packages = [{include = "izihawa_loglib"}]
8
+
9
+
10
+ [tool.poetry.dependencies]
11
+ python = "^3.8.1,<3.13"
12
+ confuse = "^2.0.1"
13
+ izihawa-types = "^0.1.4"
14
+ izihawa-utils = "^1.2.1"
15
+ orjson = "^3.9.10"
16
+
17
+
18
+ [tool.poetry.group.dev.dependencies]
19
+ mypy = "~1.7.0"
20
+ pylint = "~3.0.0"
21
+ pyproject-flake8 = "~6.1.0"
22
+ twine = "~4.0.2"
23
+
24
+
25
+ [build-system]
26
+ requires = ["poetry-core"]
27
+ build-backend = "poetry.core.masonry.api"