liblogging 0.1__py3-none-any.whl
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.
liblogging/__init__.py
ADDED
liblogging/logger.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
__author__ = "xi"
|
|
4
|
+
__all__ = [
|
|
5
|
+
"get_log_context",
|
|
6
|
+
"log_request",
|
|
7
|
+
"Logger",
|
|
8
|
+
"logger",
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
import functools
|
|
12
|
+
import inspect
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
import threading
|
|
18
|
+
from contextvars import ContextVar
|
|
19
|
+
from datetime import datetime
|
|
20
|
+
from logging import Formatter, Handler, NOTSET
|
|
21
|
+
from logging.handlers import RotatingFileHandler
|
|
22
|
+
from typing import Dict, List, Mapping, Optional
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
from tqdm import tqdm
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TqdmHandler(Handler):
|
|
29
|
+
|
|
30
|
+
def __init__(self, stream, level=NOTSET):
|
|
31
|
+
super().__init__(level)
|
|
32
|
+
self.stream = stream
|
|
33
|
+
|
|
34
|
+
def emit(self, record):
|
|
35
|
+
# noinspection PyBroadException
|
|
36
|
+
try:
|
|
37
|
+
msg = self.format(record)
|
|
38
|
+
tqdm.write(msg, file=self.stream)
|
|
39
|
+
self.flush()
|
|
40
|
+
except RecursionError:
|
|
41
|
+
raise
|
|
42
|
+
except Exception:
|
|
43
|
+
self.handleError(record)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
StreamHandler = TqdmHandler
|
|
47
|
+
except ImportError:
|
|
48
|
+
tqdm = None
|
|
49
|
+
from logging import StreamHandler
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ThreadCoroutineLocal(threading.local):
|
|
53
|
+
|
|
54
|
+
def __init__(self):
|
|
55
|
+
super().__init__()
|
|
56
|
+
self._co_local = ContextVar[Optional[Dict]]("_co_local", default=None)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def co_local(self):
|
|
60
|
+
co_local = self._co_local.get()
|
|
61
|
+
if co_local is None:
|
|
62
|
+
co_local = {}
|
|
63
|
+
self._co_local.set(co_local)
|
|
64
|
+
return co_local
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
thread_local = ThreadCoroutineLocal()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_log_context():
|
|
71
|
+
return thread_local.co_local
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def log_request(fields=("trace_id", "request_id")):
|
|
75
|
+
def decorator(fn):
|
|
76
|
+
if not inspect.iscoroutinefunction(fn):
|
|
77
|
+
@functools.wraps(fn)
|
|
78
|
+
def _wrapper(*args, **kwargs):
|
|
79
|
+
log_context = thread_local.co_local
|
|
80
|
+
log_context.clear()
|
|
81
|
+
for field, value in _find_log_items(fields, args, kwargs):
|
|
82
|
+
log_context[field] = value
|
|
83
|
+
return fn(*args, **kwargs)
|
|
84
|
+
else:
|
|
85
|
+
@functools.wraps(fn)
|
|
86
|
+
async def _wrapper(*args, **kwargs):
|
|
87
|
+
log_context = thread_local.co_local
|
|
88
|
+
log_context.clear()
|
|
89
|
+
for field, value in _find_log_items(fields, args, kwargs):
|
|
90
|
+
log_context[field] = value
|
|
91
|
+
return await fn(*args, **kwargs)
|
|
92
|
+
|
|
93
|
+
return _wrapper
|
|
94
|
+
|
|
95
|
+
return decorator
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _find_log_items(fields: List[str], args: tuple, kwargs: dict):
|
|
99
|
+
args = [*args, *kwargs.values()]
|
|
100
|
+
for field in fields:
|
|
101
|
+
if field in kwargs:
|
|
102
|
+
yield field, kwargs[field]
|
|
103
|
+
else:
|
|
104
|
+
for arg in args:
|
|
105
|
+
try:
|
|
106
|
+
yield field, getattr(arg, field)
|
|
107
|
+
break
|
|
108
|
+
except AttributeError:
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class JSONFormatter(Formatter):
|
|
113
|
+
|
|
114
|
+
def format(self, record):
|
|
115
|
+
context: dict = thread_local.co_local
|
|
116
|
+
|
|
117
|
+
create_time = datetime.strptime(self.formatTime(record), "%Y-%m-%d %H:%M:%S,%f")
|
|
118
|
+
message = record.getMessage()
|
|
119
|
+
extra_message = {}
|
|
120
|
+
if isinstance(message, Mapping) and "message" in message:
|
|
121
|
+
extra_message = {**message}
|
|
122
|
+
message = extra_message["message"]
|
|
123
|
+
del extra_message["message"]
|
|
124
|
+
|
|
125
|
+
log_data = {
|
|
126
|
+
# "uid": uid,
|
|
127
|
+
# "session_id": session_id,
|
|
128
|
+
# "turn": turn,
|
|
129
|
+
"time": create_time.strftime("%Y-%m-%d %H:%M:%S"),
|
|
130
|
+
"level": record.levelname,
|
|
131
|
+
"trace_id": context.get("trace_id", None),
|
|
132
|
+
"code": f"{record.filename}:{record.lineno}:{record.funcName}",
|
|
133
|
+
# "message_source": getattr(record, "message_source", "planning_service"), # 控制不同源
|
|
134
|
+
# "message_type": getattr(record, "message_type", "common"), # 控制不同log类型
|
|
135
|
+
# "data": getattr(record, "data", {}),
|
|
136
|
+
"message": message,
|
|
137
|
+
**extra_message
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
output_log = json.dumps(log_data, ensure_ascii=False)
|
|
141
|
+
return output_log
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class Logger(logging.Logger):
|
|
145
|
+
|
|
146
|
+
def __init__(
|
|
147
|
+
self,
|
|
148
|
+
name: str,
|
|
149
|
+
level: int = logging.INFO,
|
|
150
|
+
to_console: bool = True,
|
|
151
|
+
log_file: str = None,
|
|
152
|
+
max_size: int = 1024 * 1024 * 10,
|
|
153
|
+
backup_count: int = 3,
|
|
154
|
+
formatter: Formatter = JSONFormatter(),
|
|
155
|
+
):
|
|
156
|
+
super().__init__(name, level)
|
|
157
|
+
self.propagate = False
|
|
158
|
+
self.warning_once = functools.lru_cache(self.warning)
|
|
159
|
+
|
|
160
|
+
if log_file:
|
|
161
|
+
log_dir = os.path.dirname(log_file)
|
|
162
|
+
if log_dir and not os.path.exists(log_dir):
|
|
163
|
+
os.makedirs(log_dir)
|
|
164
|
+
file_handler = RotatingFileHandler(log_file, maxBytes=max_size, backupCount=backup_count)
|
|
165
|
+
file_handler.setLevel(level)
|
|
166
|
+
file_handler.setFormatter(formatter)
|
|
167
|
+
self.addHandler(file_handler)
|
|
168
|
+
|
|
169
|
+
if to_console:
|
|
170
|
+
console_handler = StreamHandler(stream=sys.stdout)
|
|
171
|
+
console_handler.setLevel(level)
|
|
172
|
+
console_handler.setFormatter(formatter)
|
|
173
|
+
self.addHandler(console_handler)
|
|
174
|
+
|
|
175
|
+
def turn_start(self):
|
|
176
|
+
turn_id = thread_local.co_local.get("turn_id", "?")
|
|
177
|
+
self._log(logging.INFO, f"TurnStart[{turn_id}]", ())
|
|
178
|
+
|
|
179
|
+
def turn_end(self):
|
|
180
|
+
turn_id = thread_local.co_local.get("turn_id", "?")
|
|
181
|
+
self._log(logging.INFO, f"TurnEnd[{turn_id}]", ())
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
logger = Logger("libentry.logger")
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: liblogging
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: Utilities for logging and sending logs.
|
|
5
|
+
Home-page: https://github.com/XoriieInpottn/liblogging
|
|
6
|
+
Author: xi
|
|
7
|
+
Author-email: gylv@mail.ustc.edu.cn
|
|
8
|
+
License: Apache-2.0 license
|
|
9
|
+
Platform: any
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# liblogger
|
|
14
|
+
|
|
15
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
liblogging/__init__.py,sha256=Glf_NNarPLzBbAGbQ6vu1_owbu3KhQWHDgGdp9MR2Yo,46
|
|
2
|
+
liblogging/logger.py,sha256=H69IEAuElYmaG_YNRDfAdYnDlb514wc7KjIbYsPsOhA,5457
|
|
3
|
+
liblogging-0.1.dist-info/METADATA,sha256=YoZcocw8IRi0GJhcGDI9xJ6sz2vKRmfyOrhYaP9q2b8,348
|
|
4
|
+
liblogging-0.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
5
|
+
liblogging-0.1.dist-info/top_level.txt,sha256=u2OA7KVQrL0NSCSl8YTLmyEN5eZjczZTN9JPx0IUCAw,11
|
|
6
|
+
liblogging-0.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
7
|
+
liblogging-0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
liblogging
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|