ominfra 0.0.0.dev429__py3-none-any.whl → 0.0.0.dev431__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.
- ominfra/scripts/journald2aws.py +991 -690
- ominfra/scripts/manage.py +1030 -728
- ominfra/scripts/supervisor.py +1030 -728
- {ominfra-0.0.0.dev429.dist-info → ominfra-0.0.0.dev431.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev429.dist-info → ominfra-0.0.0.dev431.dist-info}/RECORD +9 -9
- {ominfra-0.0.0.dev429.dist-info → ominfra-0.0.0.dev431.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev429.dist-info → ominfra-0.0.0.dev431.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev429.dist-info → ominfra-0.0.0.dev431.dist-info}/licenses/LICENSE +0 -0
- {ominfra-0.0.0.dev429.dist-info → ominfra-0.0.0.dev431.dist-info}/top_level.txt +0 -0
ominfra/scripts/supervisor.py
CHANGED
@@ -154,6 +154,13 @@ HttpHeaders = http.client.HTTPMessage # ta.TypeAlias
|
|
154
154
|
# ../../omlish/lite/maybes.py
|
155
155
|
U = ta.TypeVar('U')
|
156
156
|
|
157
|
+
# ../../omlish/logs/infos.py
|
158
|
+
LoggingMsgFn = ta.Callable[[], ta.Union[str, tuple]] # ta.TypeAlias
|
159
|
+
LoggingExcInfoTuple = ta.Tuple[ta.Type[BaseException], BaseException, ta.Optional[types.TracebackType]] # ta.TypeAlias
|
160
|
+
LoggingExcInfo = ta.Union[BaseException, LoggingExcInfoTuple] # ta.TypeAlias
|
161
|
+
LoggingExcInfoArg = ta.Union[LoggingExcInfo, bool, None] # ta.TypeAlias
|
162
|
+
LoggingContextInfo = ta.Any # ta.TypeAlias
|
163
|
+
|
157
164
|
# ../../omlish/http/handlers.py
|
158
165
|
HttpHandler = ta.Callable[['HttpHandlerRequest'], 'HttpHandlerResponse'] # ta.TypeAlias
|
159
166
|
HttpHandlerResponseData = ta.Union[bytes, 'HttpHandlerResponseStreamedData'] # ta.TypeAlias # noqa
|
@@ -165,16 +172,11 @@ InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
|
|
165
172
|
InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
|
166
173
|
|
167
174
|
# ../../omlish/logs/contexts.py
|
168
|
-
|
169
|
-
LoggingExcInfo = ta.Union[BaseException, LoggingExcInfoTuple] # ta.TypeAlias
|
170
|
-
LoggingExcInfoArg = ta.Union[LoggingExcInfo, bool, None] # ta.TypeAlias
|
175
|
+
LoggingContextInfoT = ta.TypeVar('LoggingContextInfoT', bound=LoggingContextInfo)
|
171
176
|
|
172
177
|
# ../../omlish/http/coro/server/server.py
|
173
178
|
CoroHttpServerFactory = ta.Callable[[SocketAddress], 'CoroHttpServer']
|
174
179
|
|
175
|
-
# ../../omlish/logs/base.py
|
176
|
-
LoggingMsgFn = ta.Callable[[], ta.Union[str, tuple]] # ta.TypeAlias
|
177
|
-
|
178
180
|
|
179
181
|
########################################
|
180
182
|
# ../errors.py
|
@@ -2125,8 +2127,6 @@ class AttrOps(ta.Generic[T]):
|
|
2125
2127
|
self._eq = _eq
|
2126
2128
|
return _eq
|
2127
2129
|
|
2128
|
-
#
|
2129
|
-
|
2130
2130
|
@property
|
2131
2131
|
def hash_eq(self) -> ta.Tuple[
|
2132
2132
|
ta.Callable[[T], int],
|
@@ -2134,6 +2134,8 @@ class AttrOps(ta.Generic[T]):
|
|
2134
2134
|
]:
|
2135
2135
|
return (self.hash, self.eq)
|
2136
2136
|
|
2137
|
+
#
|
2138
|
+
|
2137
2139
|
@property
|
2138
2140
|
def repr_hash_eq(self) -> ta.Tuple[
|
2139
2141
|
ta.Callable[[T], str],
|
@@ -2144,20 +2146,25 @@ class AttrOps(ta.Generic[T]):
|
|
2144
2146
|
|
2145
2147
|
#
|
2146
2148
|
|
2149
|
+
class NOT_SET: # noqa
|
2150
|
+
def __new__(cls, *args, **kwargs): # noqa
|
2151
|
+
raise TypeError
|
2152
|
+
|
2147
2153
|
def install(
|
2148
2154
|
self,
|
2149
2155
|
locals_dct: ta.MutableMapping[str, ta.Any],
|
2150
2156
|
*,
|
2151
|
-
|
2152
|
-
|
2153
|
-
|
2154
|
-
eq: bool = False,
|
2157
|
+
repr: ta.Union[bool, ta.Type[NOT_SET]] = NOT_SET, # noqa
|
2158
|
+
hash: ta.Union[bool, ta.Type[NOT_SET]] = NOT_SET, # noqa
|
2159
|
+
eq: ta.Union[bool, ta.Type[NOT_SET]] = NOT_SET,
|
2155
2160
|
) -> 'AttrOps[T]':
|
2156
|
-
if repr
|
2161
|
+
if all(a is self.NOT_SET for a in (repr, hash, eq)):
|
2162
|
+
repr = hash = eq = True # noqa
|
2163
|
+
if repr:
|
2157
2164
|
locals_dct.update(__repr__=self.repr)
|
2158
|
-
if hash
|
2165
|
+
if hash:
|
2159
2166
|
locals_dct.update(__hash__=self.hash)
|
2160
|
-
if eq
|
2167
|
+
if eq:
|
2161
2168
|
locals_dct.update(__eq__=self.eq)
|
2162
2169
|
return self
|
2163
2170
|
|
@@ -3069,124 +3076,6 @@ def typing_annotations_attr() -> str:
|
|
3069
3076
|
return _TYPING_ANNOTATIONS_ATTR
|
3070
3077
|
|
3071
3078
|
|
3072
|
-
########################################
|
3073
|
-
# ../../../omlish/logs/infos.py
|
3074
|
-
|
3075
|
-
|
3076
|
-
##
|
3077
|
-
|
3078
|
-
|
3079
|
-
def logging_context_info(cls):
|
3080
|
-
return cls
|
3081
|
-
|
3082
|
-
|
3083
|
-
##
|
3084
|
-
|
3085
|
-
|
3086
|
-
@logging_context_info
|
3087
|
-
@ta.final
|
3088
|
-
class LoggingSourceFileInfo(ta.NamedTuple):
|
3089
|
-
file_name: str
|
3090
|
-
module: str
|
3091
|
-
|
3092
|
-
@classmethod
|
3093
|
-
def build(cls, file_path: ta.Optional[str]) -> ta.Optional['LoggingSourceFileInfo']:
|
3094
|
-
if file_path is None:
|
3095
|
-
return None
|
3096
|
-
|
3097
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L331-L336 # noqa
|
3098
|
-
try:
|
3099
|
-
file_name = os.path.basename(file_path)
|
3100
|
-
module = os.path.splitext(file_name)[0]
|
3101
|
-
except (TypeError, ValueError, AttributeError):
|
3102
|
-
return None
|
3103
|
-
|
3104
|
-
return cls(
|
3105
|
-
file_name,
|
3106
|
-
module,
|
3107
|
-
)
|
3108
|
-
|
3109
|
-
|
3110
|
-
##
|
3111
|
-
|
3112
|
-
|
3113
|
-
@logging_context_info
|
3114
|
-
@ta.final
|
3115
|
-
class LoggingThreadInfo(ta.NamedTuple):
|
3116
|
-
ident: int
|
3117
|
-
native_id: ta.Optional[int]
|
3118
|
-
name: str
|
3119
|
-
|
3120
|
-
@classmethod
|
3121
|
-
def build(cls) -> 'LoggingThreadInfo':
|
3122
|
-
return cls(
|
3123
|
-
threading.get_ident(),
|
3124
|
-
threading.get_native_id() if hasattr(threading, 'get_native_id') else None,
|
3125
|
-
threading.current_thread().name,
|
3126
|
-
)
|
3127
|
-
|
3128
|
-
|
3129
|
-
##
|
3130
|
-
|
3131
|
-
|
3132
|
-
@logging_context_info
|
3133
|
-
@ta.final
|
3134
|
-
class LoggingProcessInfo(ta.NamedTuple):
|
3135
|
-
pid: int
|
3136
|
-
|
3137
|
-
@classmethod
|
3138
|
-
def build(cls) -> 'LoggingProcessInfo':
|
3139
|
-
return cls(
|
3140
|
-
os.getpid(),
|
3141
|
-
)
|
3142
|
-
|
3143
|
-
|
3144
|
-
##
|
3145
|
-
|
3146
|
-
|
3147
|
-
@logging_context_info
|
3148
|
-
@ta.final
|
3149
|
-
class LoggingMultiprocessingInfo(ta.NamedTuple):
|
3150
|
-
process_name: str
|
3151
|
-
|
3152
|
-
@classmethod
|
3153
|
-
def build(cls) -> ta.Optional['LoggingMultiprocessingInfo']:
|
3154
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L355-L364 # noqa
|
3155
|
-
if (mp := sys.modules.get('multiprocessing')) is None:
|
3156
|
-
return None
|
3157
|
-
|
3158
|
-
return cls(
|
3159
|
-
mp.current_process().name,
|
3160
|
-
)
|
3161
|
-
|
3162
|
-
|
3163
|
-
##
|
3164
|
-
|
3165
|
-
|
3166
|
-
@logging_context_info
|
3167
|
-
@ta.final
|
3168
|
-
class LoggingAsyncioTaskInfo(ta.NamedTuple):
|
3169
|
-
name: str
|
3170
|
-
|
3171
|
-
@classmethod
|
3172
|
-
def build(cls) -> ta.Optional['LoggingAsyncioTaskInfo']:
|
3173
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L372-L377 # noqa
|
3174
|
-
if (asyncio := sys.modules.get('asyncio')) is None:
|
3175
|
-
return None
|
3176
|
-
|
3177
|
-
try:
|
3178
|
-
task = asyncio.current_task()
|
3179
|
-
except Exception: # noqa
|
3180
|
-
return None
|
3181
|
-
|
3182
|
-
if task is None:
|
3183
|
-
return None
|
3184
|
-
|
3185
|
-
return cls(
|
3186
|
-
task.get_name(), # Always non-None
|
3187
|
-
)
|
3188
|
-
|
3189
|
-
|
3190
3079
|
########################################
|
3191
3080
|
# ../../../omlish/logs/levels.py
|
3192
3081
|
|
@@ -3206,36 +3095,66 @@ class NamedLogLevel(int):
|
|
3206
3095
|
|
3207
3096
|
#
|
3208
3097
|
|
3209
|
-
|
3210
|
-
|
3211
|
-
|
3098
|
+
_CACHE: ta.ClassVar[ta.MutableMapping[int, 'NamedLogLevel']] = {}
|
3099
|
+
|
3100
|
+
@ta.overload
|
3101
|
+
def __new__(cls, name: str, offset: int = 0, /) -> 'NamedLogLevel':
|
3102
|
+
...
|
3103
|
+
|
3104
|
+
@ta.overload
|
3105
|
+
def __new__(cls, i: int, /) -> 'NamedLogLevel':
|
3106
|
+
...
|
3212
3107
|
|
3213
|
-
|
3108
|
+
def __new__(cls, x, offset=0, /):
|
3109
|
+
if isinstance(x, str):
|
3110
|
+
return cls(cls._INTS_BY_NAME[x.upper()] + offset)
|
3111
|
+
elif not offset and (c := cls._CACHE.get(x)) is not None:
|
3112
|
+
return c
|
3113
|
+
else:
|
3114
|
+
return super().__new__(cls, x + offset)
|
3115
|
+
|
3116
|
+
#
|
3117
|
+
|
3118
|
+
_name_and_offset: ta.Tuple[str, int]
|
3214
3119
|
|
3215
3120
|
@property
|
3216
|
-
def
|
3121
|
+
def name_and_offset(self) -> ta.Tuple[str, int]:
|
3217
3122
|
try:
|
3218
|
-
return self.
|
3123
|
+
return self._name_and_offset
|
3219
3124
|
except AttributeError:
|
3220
3125
|
pass
|
3221
3126
|
|
3222
|
-
if (n := self.
|
3127
|
+
if (n := self._NAMES_BY_INT.get(self)) is not None:
|
3128
|
+
t = (n, 0)
|
3129
|
+
else:
|
3223
3130
|
for n, i in self._NAME_INT_PAIRS: # noqa
|
3224
3131
|
if self >= i:
|
3132
|
+
t = (n, (self - i))
|
3225
3133
|
break
|
3226
3134
|
else:
|
3227
|
-
|
3135
|
+
t = ('NOTSET', int(self))
|
3136
|
+
|
3137
|
+
self._name_and_offset = t
|
3138
|
+
return t
|
3139
|
+
|
3140
|
+
@property
|
3141
|
+
def exact_name(self) -> ta.Optional[str]:
|
3142
|
+
n, o = self.name_and_offset
|
3143
|
+
return n if not o else None
|
3228
3144
|
|
3229
|
-
|
3145
|
+
@property
|
3146
|
+
def effective_name(self) -> str:
|
3147
|
+
n, _ = self.name_and_offset
|
3230
3148
|
return n
|
3231
3149
|
|
3232
3150
|
#
|
3233
3151
|
|
3234
|
-
def __repr__(self) -> str:
|
3235
|
-
return f'{self.__class__.__name__}({int(self)})'
|
3236
|
-
|
3237
3152
|
def __str__(self) -> str:
|
3238
|
-
return self.exact_name or f'{self.effective_name
|
3153
|
+
return self.exact_name or f'{self.effective_name}{int(self):+}'
|
3154
|
+
|
3155
|
+
def __repr__(self) -> str:
|
3156
|
+
n, o = self.name_and_offset
|
3157
|
+
return f'{self.__class__.__name__}({n!r}{f", {int(o)}" if o else ""})'
|
3239
3158
|
|
3240
3159
|
#
|
3241
3160
|
|
@@ -3255,6 +3174,9 @@ NamedLogLevel.DEBUG = NamedLogLevel(logging.DEBUG)
|
|
3255
3174
|
NamedLogLevel.NOTSET = NamedLogLevel(logging.NOTSET)
|
3256
3175
|
|
3257
3176
|
|
3177
|
+
NamedLogLevel._CACHE.update({i: NamedLogLevel(i) for i in NamedLogLevel._NAMES_BY_INT}) # noqa
|
3178
|
+
|
3179
|
+
|
3258
3180
|
########################################
|
3259
3181
|
# ../../../omlish/logs/std/filters.py
|
3260
3182
|
|
@@ -6172,223 +6094,338 @@ def check_lite_runtime_version() -> None:
|
|
6172
6094
|
|
6173
6095
|
|
6174
6096
|
########################################
|
6175
|
-
# ../../../omlish/logs/
|
6097
|
+
# ../../../omlish/logs/infos.py
|
6098
|
+
"""
|
6099
|
+
TODO:
|
6100
|
+
- remove redundant info fields only present for std adaptation (Level.name, ...)
|
6101
|
+
"""
|
6176
6102
|
|
6177
6103
|
|
6178
6104
|
##
|
6179
6105
|
|
6180
6106
|
|
6181
|
-
|
6182
|
-
|
6183
|
-
class LoggingCaller(ta.NamedTuple):
|
6184
|
-
file_path: str
|
6185
|
-
line_no: int
|
6186
|
-
name: str
|
6187
|
-
stack_info: ta.Optional[str]
|
6107
|
+
def logging_context_info(cls):
|
6108
|
+
return cls
|
6188
6109
|
|
6189
|
-
@classmethod
|
6190
|
-
def is_internal_frame(cls, frame: types.FrameType) -> bool:
|
6191
|
-
file_path = os.path.normcase(frame.f_code.co_filename)
|
6192
6110
|
|
6193
|
-
|
6194
|
-
|
6195
|
-
|
6196
|
-
|
6197
|
-
return True
|
6111
|
+
@ta.final
|
6112
|
+
class LoggingContextInfos:
|
6113
|
+
def __new__(cls, *args, **kwargs): # noqa
|
6114
|
+
raise TypeError
|
6198
6115
|
|
6199
|
-
|
6116
|
+
#
|
6200
6117
|
|
6201
|
-
@
|
6202
|
-
|
6203
|
-
|
6118
|
+
@logging_context_info
|
6119
|
+
@ta.final
|
6120
|
+
class Name(ta.NamedTuple):
|
6121
|
+
name: str
|
6204
6122
|
|
6205
|
-
|
6206
|
-
|
6207
|
-
|
6208
|
-
|
6209
|
-
|
6123
|
+
@logging_context_info
|
6124
|
+
@ta.final
|
6125
|
+
class Level(ta.NamedTuple):
|
6126
|
+
level: NamedLogLevel
|
6127
|
+
name: str
|
6210
6128
|
|
6211
|
-
|
6129
|
+
@classmethod
|
6130
|
+
def build(cls, level: int) -> 'LoggingContextInfos.Level':
|
6131
|
+
nl: NamedLogLevel = level if level.__class__ is NamedLogLevel else NamedLogLevel(level) # type: ignore[assignment] # noqa
|
6132
|
+
return cls(
|
6133
|
+
level=nl,
|
6134
|
+
name=logging.getLevelName(nl),
|
6135
|
+
)
|
6212
6136
|
|
6213
|
-
|
6137
|
+
@logging_context_info
|
6138
|
+
@ta.final
|
6139
|
+
class Msg(ta.NamedTuple):
|
6140
|
+
msg: str
|
6141
|
+
args: ta.Union[tuple, ta.Mapping[ta.Any, ta.Any], None]
|
6214
6142
|
|
6215
|
-
|
6216
|
-
|
6217
|
-
|
6218
|
-
|
6219
|
-
|
6220
|
-
|
6221
|
-
|
6222
|
-
|
6223
|
-
|
6143
|
+
@classmethod
|
6144
|
+
def build(
|
6145
|
+
cls,
|
6146
|
+
msg: ta.Union[str, tuple, LoggingMsgFn],
|
6147
|
+
*args: ta.Any,
|
6148
|
+
) -> 'LoggingContextInfos.Msg':
|
6149
|
+
s: str
|
6150
|
+
a: ta.Any
|
6151
|
+
|
6152
|
+
if callable(msg):
|
6153
|
+
if args:
|
6154
|
+
raise TypeError(f'Must not provide both a message function and args: {msg=} {args=}')
|
6155
|
+
x = msg()
|
6156
|
+
if isinstance(x, str):
|
6157
|
+
s, a = x, ()
|
6158
|
+
elif isinstance(x, tuple):
|
6159
|
+
if x:
|
6160
|
+
s, a = x[0], x[1:]
|
6161
|
+
else:
|
6162
|
+
s, a = '', ()
|
6163
|
+
else:
|
6164
|
+
raise TypeError(x)
|
6224
6165
|
|
6225
|
-
|
6226
|
-
|
6227
|
-
|
6228
|
-
|
6229
|
-
|
6230
|
-
|
6231
|
-
|
6232
|
-
if sinfo[-1] == '\n':
|
6233
|
-
sinfo = sinfo[:-1]
|
6166
|
+
elif isinstance(msg, tuple):
|
6167
|
+
if args:
|
6168
|
+
raise TypeError(f'Must not provide both a tuple message and args: {msg=} {args=}')
|
6169
|
+
if msg:
|
6170
|
+
s, a = msg[0], msg[1:]
|
6171
|
+
else:
|
6172
|
+
s, a = '', ()
|
6234
6173
|
|
6235
|
-
|
6236
|
-
|
6237
|
-
f.f_lineno or 0,
|
6238
|
-
f.f_code.co_name,
|
6239
|
-
sinfo,
|
6240
|
-
)
|
6174
|
+
elif isinstance(msg, str):
|
6175
|
+
s, a = msg, args
|
6241
6176
|
|
6177
|
+
else:
|
6178
|
+
raise TypeError(msg)
|
6242
6179
|
|
6243
|
-
|
6244
|
-
|
6180
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L307 # noqa
|
6181
|
+
if a and len(a) == 1 and isinstance(a[0], collections.abc.Mapping) and a[0]:
|
6182
|
+
a = a[0]
|
6245
6183
|
|
6184
|
+
return cls(
|
6185
|
+
msg=s,
|
6186
|
+
args=a,
|
6187
|
+
)
|
6246
6188
|
|
6247
|
-
|
6189
|
+
@logging_context_info
|
6190
|
+
@ta.final
|
6191
|
+
class Extra(ta.NamedTuple):
|
6192
|
+
extra: ta.Mapping[ta.Any, ta.Any]
|
6248
6193
|
|
6194
|
+
@logging_context_info
|
6195
|
+
@ta.final
|
6196
|
+
class Time(ta.NamedTuple):
|
6197
|
+
ns: int
|
6198
|
+
secs: float
|
6199
|
+
msecs: float
|
6200
|
+
relative_secs: float
|
6249
6201
|
|
6250
|
-
|
6251
|
-
|
6202
|
+
@classmethod
|
6203
|
+
def get_std_start_ns(cls) -> int:
|
6204
|
+
x: ta.Any = logging._startTime # type: ignore[attr-defined] # noqa
|
6252
6205
|
|
6253
|
-
|
6206
|
+
# Before 3.13.0b1 this will be `time.time()`, a float of seconds. After that, it will be `time.time_ns()`,
|
6207
|
+
# an int.
|
6208
|
+
#
|
6209
|
+
# See:
|
6210
|
+
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
6211
|
+
#
|
6212
|
+
if isinstance(x, float):
|
6213
|
+
return int(x * 1e9)
|
6214
|
+
else:
|
6215
|
+
return x
|
6254
6216
|
|
6255
|
-
|
6217
|
+
@classmethod
|
6218
|
+
def build(
|
6219
|
+
cls,
|
6220
|
+
ns: int,
|
6221
|
+
*,
|
6222
|
+
start_ns: ta.Optional[int] = None,
|
6223
|
+
) -> 'LoggingContextInfos.Time':
|
6224
|
+
# https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
6225
|
+
secs = ns / 1e9 # ns to float seconds
|
6226
|
+
|
6227
|
+
# Get the number of whole milliseconds (0-999) in the fractional part of seconds.
|
6228
|
+
# Eg: 1_677_903_920_999_998_503 ns --> 999_998_503 ns--> 999 ms
|
6229
|
+
# Convert to float by adding 0.0 for historical reasons. See gh-89047
|
6230
|
+
msecs = (ns % 1_000_000_000) // 1_000_000 + 0.0
|
6231
|
+
|
6232
|
+
# https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
6233
|
+
if msecs == 999.0 and int(secs) != ns // 1_000_000_000:
|
6234
|
+
# ns -> sec conversion can round up, e.g:
|
6235
|
+
# 1_677_903_920_999_999_900 ns --> 1_677_903_921.0 sec
|
6236
|
+
msecs = 0.0
|
6237
|
+
|
6238
|
+
if start_ns is None:
|
6239
|
+
start_ns = cls.get_std_start_ns()
|
6240
|
+
relative_secs = (ns - start_ns) / 1e6
|
6241
|
+
|
6242
|
+
return cls(
|
6243
|
+
ns=ns,
|
6244
|
+
secs=secs,
|
6245
|
+
msecs=msecs,
|
6246
|
+
relative_secs=relative_secs,
|
6247
|
+
)
|
6256
6248
|
|
6257
|
-
|
6249
|
+
@logging_context_info
|
6250
|
+
@ta.final
|
6251
|
+
class Exc(ta.NamedTuple):
|
6252
|
+
info: LoggingExcInfo
|
6253
|
+
info_tuple: LoggingExcInfoTuple
|
6258
6254
|
|
6259
|
-
|
6255
|
+
@classmethod
|
6256
|
+
def build(
|
6257
|
+
cls,
|
6258
|
+
arg: LoggingExcInfoArg = False,
|
6259
|
+
) -> ta.Optional['LoggingContextInfos.Exc']:
|
6260
|
+
if arg is True:
|
6261
|
+
sys_exc_info = sys.exc_info()
|
6262
|
+
if sys_exc_info[0] is not None:
|
6263
|
+
arg = sys_exc_info
|
6264
|
+
else:
|
6265
|
+
arg = None
|
6266
|
+
elif arg is False:
|
6267
|
+
arg = None
|
6268
|
+
if arg is None:
|
6269
|
+
return None
|
6260
6270
|
|
6261
|
-
|
6271
|
+
info: LoggingExcInfo = arg
|
6272
|
+
if isinstance(info, BaseException):
|
6273
|
+
info_tuple: LoggingExcInfoTuple = (type(info), info, info.__traceback__) # noqa
|
6274
|
+
else:
|
6275
|
+
info_tuple = info
|
6262
6276
|
|
6263
|
-
|
6277
|
+
return cls(
|
6278
|
+
info=info,
|
6279
|
+
info_tuple=info_tuple,
|
6280
|
+
)
|
6264
6281
|
|
6265
|
-
|
6282
|
+
@logging_context_info
|
6283
|
+
@ta.final
|
6284
|
+
class Caller(ta.NamedTuple):
|
6285
|
+
file_path: str
|
6286
|
+
line_no: int
|
6287
|
+
func_name: str
|
6288
|
+
stack_info: ta.Optional[str]
|
6266
6289
|
|
6267
|
-
|
6290
|
+
@classmethod
|
6291
|
+
def is_internal_frame(cls, frame: types.FrameType) -> bool:
|
6292
|
+
file_path = os.path.normcase(frame.f_code.co_filename)
|
6268
6293
|
|
6269
|
-
|
6294
|
+
# Yes, really.
|
6295
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L204 # noqa
|
6296
|
+
# https://github.com/python/cpython/commit/5ca6d7469be53960843df39bb900e9c3359f127f
|
6297
|
+
if 'importlib' in file_path and '_bootstrap' in file_path:
|
6298
|
+
return True
|
6270
6299
|
|
6271
|
-
|
6300
|
+
return False
|
6272
6301
|
|
6302
|
+
@classmethod
|
6303
|
+
def find_frame(cls, stack_offset: int = 0) -> ta.Optional[types.FrameType]:
|
6304
|
+
f: ta.Optional[types.FrameType] = sys._getframe(2 + stack_offset) # noqa
|
6273
6305
|
|
6274
|
-
|
6275
|
-
#
|
6276
|
-
|
6277
|
-
|
6278
|
-
|
6279
|
-
"""
|
6306
|
+
while f is not None:
|
6307
|
+
# NOTE: We don't check __file__ like stdlib since we may be running amalgamated - we rely on careful,
|
6308
|
+
# manual stack_offset management.
|
6309
|
+
if hasattr(f, 'f_code'):
|
6310
|
+
return f
|
6280
6311
|
|
6312
|
+
f = f.f_back
|
6281
6313
|
|
6282
|
-
|
6314
|
+
return None
|
6283
6315
|
|
6316
|
+
@classmethod
|
6317
|
+
def build(
|
6318
|
+
cls,
|
6319
|
+
stack_offset: int = 0,
|
6320
|
+
*,
|
6321
|
+
stack_info: bool = False,
|
6322
|
+
) -> ta.Optional['LoggingContextInfos.Caller']:
|
6323
|
+
if (f := cls.find_frame(stack_offset + 1)) is None:
|
6324
|
+
return None
|
6284
6325
|
|
6285
|
-
|
6286
|
-
|
6287
|
-
|
6288
|
-
|
6289
|
-
|
6290
|
-
|
6291
|
-
|
6292
|
-
|
6293
|
-
|
6294
|
-
|
6295
|
-
|
6296
|
-
|
6297
|
-
|
6298
|
-
|
6299
|
-
|
6300
|
-
|
6301
|
-
'msecs': False,
|
6302
|
-
'relativeCreated': False,
|
6303
|
-
'thread': False,
|
6304
|
-
'threadName': False,
|
6305
|
-
'processName': False,
|
6306
|
-
'process': False,
|
6307
|
-
}
|
6326
|
+
# https://github.com/python/cpython/blob/08e9794517063c8cd92c48714071b1d3c60b71bd/Lib/logging/__init__.py#L1616-L1623 # noqa
|
6327
|
+
sinfo = None
|
6328
|
+
if stack_info:
|
6329
|
+
sio = io.StringIO()
|
6330
|
+
traceback.print_stack(f, file=sio)
|
6331
|
+
sinfo = sio.getvalue()
|
6332
|
+
sio.close()
|
6333
|
+
if sinfo[-1] == '\n':
|
6334
|
+
sinfo = sinfo[:-1]
|
6335
|
+
|
6336
|
+
return cls(
|
6337
|
+
file_path=f.f_code.co_filename,
|
6338
|
+
line_no=f.f_lineno or 0,
|
6339
|
+
func_name=f.f_code.co_name,
|
6340
|
+
stack_info=sinfo,
|
6341
|
+
)
|
6308
6342
|
|
6309
|
-
|
6310
|
-
|
6311
|
-
|
6312
|
-
|
6313
|
-
|
6314
|
-
) -> None:
|
6315
|
-
super().__init__(*args, **kwargs)
|
6343
|
+
@logging_context_info
|
6344
|
+
@ta.final
|
6345
|
+
class SourceFile(ta.NamedTuple):
|
6346
|
+
file_name: str
|
6347
|
+
module: str
|
6316
6348
|
|
6317
|
-
|
6318
|
-
|
6319
|
-
|
6349
|
+
@classmethod
|
6350
|
+
def build(cls, caller_file_path: ta.Optional[str]) -> ta.Optional['LoggingContextInfos.SourceFile']:
|
6351
|
+
if caller_file_path is None:
|
6352
|
+
return None
|
6320
6353
|
|
6321
|
-
|
6322
|
-
|
6323
|
-
|
6324
|
-
|
6325
|
-
|
6326
|
-
|
6327
|
-
}
|
6328
|
-
return self._json_dumps(dct)
|
6354
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L331-L336 # noqa
|
6355
|
+
try:
|
6356
|
+
file_name = os.path.basename(caller_file_path)
|
6357
|
+
module = os.path.splitext(file_name)[0]
|
6358
|
+
except (TypeError, ValueError, AttributeError):
|
6359
|
+
return None
|
6329
6360
|
|
6361
|
+
return cls(
|
6362
|
+
file_name=file_name,
|
6363
|
+
module=module,
|
6364
|
+
)
|
6330
6365
|
|
6331
|
-
|
6332
|
-
|
6366
|
+
@logging_context_info
|
6367
|
+
@ta.final
|
6368
|
+
class Thread(ta.NamedTuple):
|
6369
|
+
ident: int
|
6370
|
+
native_id: ta.Optional[int]
|
6371
|
+
name: str
|
6333
6372
|
|
6373
|
+
@classmethod
|
6374
|
+
def build(cls) -> 'LoggingContextInfos.Thread':
|
6375
|
+
return cls(
|
6376
|
+
ident=threading.get_ident(),
|
6377
|
+
native_id=threading.get_native_id() if hasattr(threading, 'get_native_id') else None,
|
6378
|
+
name=threading.current_thread().name,
|
6379
|
+
)
|
6334
6380
|
|
6335
|
-
|
6381
|
+
@logging_context_info
|
6382
|
+
@ta.final
|
6383
|
+
class Process(ta.NamedTuple):
|
6384
|
+
pid: int
|
6336
6385
|
|
6386
|
+
@classmethod
|
6387
|
+
def build(cls) -> 'LoggingContextInfos.Process':
|
6388
|
+
return cls(
|
6389
|
+
pid=os.getpid(),
|
6390
|
+
)
|
6337
6391
|
|
6338
|
-
@logging_context_info
|
6339
|
-
@ta.final
|
6340
|
-
class
|
6341
|
-
|
6392
|
+
@logging_context_info
|
6393
|
+
@ta.final
|
6394
|
+
class Multiprocessing(ta.NamedTuple):
|
6395
|
+
process_name: str
|
6342
6396
|
|
6343
|
-
|
6344
|
-
|
6345
|
-
|
6397
|
+
@classmethod
|
6398
|
+
def build(cls) -> ta.Optional['LoggingContextInfos.Multiprocessing']:
|
6399
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L355-L364 # noqa
|
6400
|
+
if (mp := sys.modules.get('multiprocessing')) is None:
|
6401
|
+
return None
|
6346
6402
|
|
6347
|
-
|
6348
|
-
|
6349
|
-
|
6403
|
+
return cls(
|
6404
|
+
process_name=mp.current_process().name,
|
6405
|
+
)
|
6350
6406
|
|
6351
|
-
|
6352
|
-
|
6353
|
-
|
6354
|
-
|
6355
|
-
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
6356
|
-
#
|
6357
|
-
if isinstance(x, float):
|
6358
|
-
return int(x * 1e9)
|
6359
|
-
else:
|
6360
|
-
return x
|
6407
|
+
@logging_context_info
|
6408
|
+
@ta.final
|
6409
|
+
class AsyncioTask(ta.NamedTuple):
|
6410
|
+
name: str
|
6361
6411
|
|
6362
|
-
|
6363
|
-
|
6364
|
-
|
6365
|
-
|
6366
|
-
|
6367
|
-
start_time_ns: ta.Optional[int] = None,
|
6368
|
-
) -> 'LoggingTimeFields':
|
6369
|
-
# https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
6370
|
-
created = time_ns / 1e9 # ns to float seconds
|
6371
|
-
|
6372
|
-
# Get the number of whole milliseconds (0-999) in the fractional part of seconds.
|
6373
|
-
# Eg: 1_677_903_920_999_998_503 ns --> 999_998_503 ns--> 999 ms
|
6374
|
-
# Convert to float by adding 0.0 for historical reasons. See gh-89047
|
6375
|
-
msecs = (time_ns % 1_000_000_000) // 1_000_000 + 0.0
|
6376
|
-
|
6377
|
-
# https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
6378
|
-
if msecs == 999.0 and int(created) != time_ns // 1_000_000_000:
|
6379
|
-
# ns -> sec conversion can round up, e.g:
|
6380
|
-
# 1_677_903_920_999_999_900 ns --> 1_677_903_921.0 sec
|
6381
|
-
msecs = 0.0
|
6382
|
-
|
6383
|
-
if start_time_ns is None:
|
6384
|
-
start_time_ns = cls.get_std_start_time_ns()
|
6385
|
-
relative_created = (time_ns - start_time_ns) / 1e6
|
6412
|
+
@classmethod
|
6413
|
+
def build(cls) -> ta.Optional['LoggingContextInfos.AsyncioTask']:
|
6414
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L372-L377 # noqa
|
6415
|
+
if (asyncio := sys.modules.get('asyncio')) is None:
|
6416
|
+
return None
|
6386
6417
|
|
6387
|
-
|
6388
|
-
|
6389
|
-
|
6390
|
-
|
6391
|
-
|
6418
|
+
try:
|
6419
|
+
task = asyncio.current_task()
|
6420
|
+
except Exception: # noqa
|
6421
|
+
return None
|
6422
|
+
|
6423
|
+
if task is None:
|
6424
|
+
return None
|
6425
|
+
|
6426
|
+
return cls(
|
6427
|
+
name=task.get_name(), # Always non-None
|
6428
|
+
)
|
6392
6429
|
|
6393
6430
|
|
6394
6431
|
##
|
@@ -6399,12 +6436,12 @@ class UnexpectedLoggingStartTimeWarning(LoggingSetupWarning):
|
|
6399
6436
|
|
6400
6437
|
|
6401
6438
|
def _check_logging_start_time() -> None:
|
6402
|
-
if (x :=
|
6439
|
+
if (x := LoggingContextInfos.Time.get_std_start_ns()) < (t := time.time()):
|
6403
6440
|
import warnings # noqa
|
6404
6441
|
|
6405
6442
|
warnings.warn(
|
6406
6443
|
f'Unexpected logging start time detected: '
|
6407
|
-
f'
|
6444
|
+
f'get_std_start_ns={x}, '
|
6408
6445
|
f'time.time()={t}',
|
6409
6446
|
UnexpectedLoggingStartTimeWarning,
|
6410
6447
|
)
|
@@ -6413,6 +6450,95 @@ def _check_logging_start_time() -> None:
|
|
6413
6450
|
_check_logging_start_time()
|
6414
6451
|
|
6415
6452
|
|
6453
|
+
########################################
|
6454
|
+
# ../../../omlish/logs/protocols.py
|
6455
|
+
|
6456
|
+
|
6457
|
+
##
|
6458
|
+
|
6459
|
+
|
6460
|
+
@ta.runtime_checkable
|
6461
|
+
class LoggerLike(ta.Protocol):
|
6462
|
+
"""Satisfied by both our Logger and stdlib logging.Logger."""
|
6463
|
+
|
6464
|
+
def isEnabledFor(self, level: LogLevel) -> bool: ... # noqa
|
6465
|
+
|
6466
|
+
def getEffectiveLevel(self) -> LogLevel: ... # noqa
|
6467
|
+
|
6468
|
+
#
|
6469
|
+
|
6470
|
+
def log(self, level: LogLevel, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
6471
|
+
|
6472
|
+
def debug(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
6473
|
+
|
6474
|
+
def info(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
6475
|
+
|
6476
|
+
def warning(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
6477
|
+
|
6478
|
+
def error(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
6479
|
+
|
6480
|
+
def exception(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
6481
|
+
|
6482
|
+
def critical(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
6483
|
+
|
6484
|
+
|
6485
|
+
########################################
|
6486
|
+
# ../../../omlish/logs/std/json.py
|
6487
|
+
"""
|
6488
|
+
TODO:
|
6489
|
+
- translate json keys
|
6490
|
+
"""
|
6491
|
+
|
6492
|
+
|
6493
|
+
##
|
6494
|
+
|
6495
|
+
|
6496
|
+
class JsonLoggingFormatter(logging.Formatter):
|
6497
|
+
KEYS: ta.Mapping[str, bool] = {
|
6498
|
+
'name': False,
|
6499
|
+
'msg': False,
|
6500
|
+
'args': False,
|
6501
|
+
'levelname': False,
|
6502
|
+
'levelno': False,
|
6503
|
+
'pathname': False,
|
6504
|
+
'filename': False,
|
6505
|
+
'module': False,
|
6506
|
+
'exc_info': True,
|
6507
|
+
'exc_text': True,
|
6508
|
+
'stack_info': True,
|
6509
|
+
'lineno': False,
|
6510
|
+
'funcName': False,
|
6511
|
+
'created': False,
|
6512
|
+
'msecs': False,
|
6513
|
+
'relativeCreated': False,
|
6514
|
+
'thread': False,
|
6515
|
+
'threadName': False,
|
6516
|
+
'processName': False,
|
6517
|
+
'process': False,
|
6518
|
+
}
|
6519
|
+
|
6520
|
+
def __init__(
|
6521
|
+
self,
|
6522
|
+
*args: ta.Any,
|
6523
|
+
json_dumps: ta.Optional[ta.Callable[[ta.Any], str]] = None,
|
6524
|
+
**kwargs: ta.Any,
|
6525
|
+
) -> None:
|
6526
|
+
super().__init__(*args, **kwargs)
|
6527
|
+
|
6528
|
+
if json_dumps is None:
|
6529
|
+
json_dumps = json_dumps_compact
|
6530
|
+
self._json_dumps = json_dumps
|
6531
|
+
|
6532
|
+
def format(self, record: logging.LogRecord) -> str:
|
6533
|
+
dct = {
|
6534
|
+
k: v
|
6535
|
+
for k, o in self.KEYS.items()
|
6536
|
+
for v in [getattr(record, k)]
|
6537
|
+
if not (o and v is None)
|
6538
|
+
}
|
6539
|
+
return self._json_dumps(dct)
|
6540
|
+
|
6541
|
+
|
6416
6542
|
########################################
|
6417
6543
|
# ../../../omlish/os/journald.py
|
6418
6544
|
|
@@ -8451,68 +8577,36 @@ inj = InjectionApi()
|
|
8451
8577
|
|
8452
8578
|
|
8453
8579
|
class LoggingContext(Abstract):
|
8454
|
-
@property
|
8455
8580
|
@abc.abstractmethod
|
8456
|
-
def
|
8581
|
+
def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
8457
8582
|
raise NotImplementedError
|
8458
8583
|
|
8459
|
-
|
8460
|
-
|
8461
|
-
|
8462
|
-
@abc.abstractmethod
|
8463
|
-
def time_ns(self) -> int:
|
8464
|
-
raise NotImplementedError
|
8465
|
-
|
8466
|
-
@property
|
8467
|
-
@abc.abstractmethod
|
8468
|
-
def times(self) -> LoggingTimeFields:
|
8469
|
-
raise NotImplementedError
|
8470
|
-
|
8471
|
-
#
|
8584
|
+
@ta.final
|
8585
|
+
def __getitem__(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
8586
|
+
return self.get_info(ty)
|
8472
8587
|
|
8473
|
-
@
|
8474
|
-
|
8475
|
-
|
8476
|
-
|
8588
|
+
@ta.final
|
8589
|
+
def must_get_info(self, ty: ta.Type[LoggingContextInfoT]) -> LoggingContextInfoT:
|
8590
|
+
if (info := self.get_info(ty)) is None:
|
8591
|
+
raise TypeError(f'LoggingContextInfo absent: {ty}')
|
8592
|
+
return info
|
8477
8593
|
|
8478
|
-
|
8479
|
-
@abc.abstractmethod
|
8480
|
-
def exc_info_tuple(self) -> ta.Optional[LoggingExcInfoTuple]:
|
8481
|
-
raise NotImplementedError
|
8594
|
+
##
|
8482
8595
|
|
8483
|
-
#
|
8484
8596
|
|
8597
|
+
class CaptureLoggingContext(LoggingContext, Abstract):
|
8485
8598
|
@abc.abstractmethod
|
8486
|
-
def
|
8487
|
-
|
8599
|
+
def set_basic(
|
8600
|
+
self,
|
8601
|
+
name: str,
|
8488
8602
|
|
8489
|
-
|
8490
|
-
|
8603
|
+
msg: ta.Union[str, tuple, LoggingMsgFn],
|
8604
|
+
args: tuple,
|
8605
|
+
) -> 'CaptureLoggingContext':
|
8491
8606
|
raise NotImplementedError
|
8492
8607
|
|
8493
8608
|
#
|
8494
8609
|
|
8495
|
-
@abc.abstractmethod
|
8496
|
-
def thread(self) -> ta.Optional[LoggingThreadInfo]:
|
8497
|
-
raise NotImplementedError
|
8498
|
-
|
8499
|
-
@abc.abstractmethod
|
8500
|
-
def process(self) -> ta.Optional[LoggingProcessInfo]:
|
8501
|
-
raise NotImplementedError
|
8502
|
-
|
8503
|
-
@abc.abstractmethod
|
8504
|
-
def multiprocessing(self) -> ta.Optional[LoggingMultiprocessingInfo]:
|
8505
|
-
raise NotImplementedError
|
8506
|
-
|
8507
|
-
@abc.abstractmethod
|
8508
|
-
def asyncio_task(self) -> ta.Optional[LoggingAsyncioTaskInfo]:
|
8509
|
-
raise NotImplementedError
|
8510
|
-
|
8511
|
-
|
8512
|
-
##
|
8513
|
-
|
8514
|
-
|
8515
|
-
class CaptureLoggingContext(LoggingContext, Abstract):
|
8516
8610
|
class AlreadyCapturedError(Exception):
|
8517
8611
|
pass
|
8518
8612
|
|
@@ -8543,80 +8637,50 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
|
|
8543
8637
|
|
8544
8638
|
exc_info: LoggingExcInfoArg = False,
|
8545
8639
|
|
8546
|
-
caller: ta.Union[
|
8640
|
+
caller: ta.Union[LoggingContextInfos.Caller, ta.Type[NOT_SET], None] = NOT_SET,
|
8547
8641
|
stack_offset: int = 0,
|
8548
8642
|
stack_info: bool = False,
|
8549
8643
|
) -> None:
|
8550
|
-
|
8551
|
-
|
8552
|
-
#
|
8644
|
+
# TODO: Name, Msg, Extra
|
8553
8645
|
|
8554
8646
|
if time_ns is None:
|
8555
8647
|
time_ns = time.time_ns()
|
8556
|
-
self._time_ns: int = time_ns
|
8557
|
-
|
8558
|
-
#
|
8559
|
-
|
8560
|
-
if exc_info is True:
|
8561
|
-
sys_exc_info = sys.exc_info()
|
8562
|
-
if sys_exc_info[0] is not None:
|
8563
|
-
exc_info = sys_exc_info
|
8564
|
-
else:
|
8565
|
-
exc_info = None
|
8566
|
-
elif exc_info is False:
|
8567
|
-
exc_info = None
|
8568
|
-
|
8569
|
-
if exc_info is not None:
|
8570
|
-
self._exc_info: ta.Optional[LoggingExcInfo] = exc_info
|
8571
|
-
if isinstance(exc_info, BaseException):
|
8572
|
-
self._exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = (type(exc_info), exc_info, exc_info.__traceback__) # noqa
|
8573
|
-
else:
|
8574
|
-
self._exc_info_tuple = exc_info
|
8575
8648
|
|
8576
|
-
|
8649
|
+
self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {}
|
8650
|
+
self._set_info(
|
8651
|
+
LoggingContextInfos.Level.build(level),
|
8652
|
+
LoggingContextInfos.Time.build(time_ns),
|
8653
|
+
LoggingContextInfos.Exc.build(exc_info),
|
8654
|
+
)
|
8577
8655
|
|
8578
8656
|
if caller is not CaptureLoggingContextImpl.NOT_SET:
|
8579
|
-
self.
|
8657
|
+
self._infos[LoggingContextInfos.Caller] = caller
|
8580
8658
|
else:
|
8581
8659
|
self._stack_offset = stack_offset
|
8582
8660
|
self._stack_info = stack_info
|
8583
8661
|
|
8584
|
-
|
8585
|
-
|
8586
|
-
|
8587
|
-
|
8588
|
-
return self
|
8589
|
-
|
8590
|
-
#
|
8591
|
-
|
8592
|
-
@property
|
8593
|
-
def time_ns(self) -> int:
|
8594
|
-
return self._time_ns
|
8595
|
-
|
8596
|
-
_times: LoggingTimeFields
|
8597
|
-
|
8598
|
-
@property
|
8599
|
-
def times(self) -> LoggingTimeFields:
|
8600
|
-
try:
|
8601
|
-
return self._times
|
8602
|
-
except AttributeError:
|
8603
|
-
pass
|
8604
|
-
|
8605
|
-
times = self._times = LoggingTimeFields.build(self.time_ns)
|
8606
|
-
return times
|
8662
|
+
def _set_info(self, *infos: ta.Optional[LoggingContextInfo]) -> 'CaptureLoggingContextImpl':
|
8663
|
+
for info in infos:
|
8664
|
+
if info is not None:
|
8665
|
+
self._infos[type(info)] = info
|
8666
|
+
return self
|
8607
8667
|
|
8608
|
-
|
8668
|
+
def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
8669
|
+
return self._infos.get(ty)
|
8609
8670
|
|
8610
|
-
|
8611
|
-
_exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = None
|
8671
|
+
##
|
8612
8672
|
|
8613
|
-
|
8614
|
-
|
8615
|
-
|
8673
|
+
def set_basic(
|
8674
|
+
self,
|
8675
|
+
name: str,
|
8616
8676
|
|
8617
|
-
|
8618
|
-
|
8619
|
-
|
8677
|
+
msg: ta.Union[str, tuple, LoggingMsgFn],
|
8678
|
+
args: tuple,
|
8679
|
+
) -> 'CaptureLoggingContextImpl':
|
8680
|
+
return self._set_info(
|
8681
|
+
LoggingContextInfos.Name(name),
|
8682
|
+
LoggingContextInfos.Msg.build(msg, *args),
|
8683
|
+
)
|
8620
8684
|
|
8621
8685
|
##
|
8622
8686
|
|
@@ -8630,74 +8694,28 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
|
|
8630
8694
|
|
8631
8695
|
_has_captured: bool = False
|
8632
8696
|
|
8633
|
-
_caller: ta.Optional[LoggingCaller]
|
8634
|
-
_source_file: ta.Optional[LoggingSourceFileInfo]
|
8635
|
-
|
8636
|
-
_thread: ta.Optional[LoggingThreadInfo]
|
8637
|
-
_process: ta.Optional[LoggingProcessInfo]
|
8638
|
-
_multiprocessing: ta.Optional[LoggingMultiprocessingInfo]
|
8639
|
-
_asyncio_task: ta.Optional[LoggingAsyncioTaskInfo]
|
8640
|
-
|
8641
8697
|
def capture(self) -> None:
|
8642
8698
|
if self._has_captured:
|
8643
8699
|
raise CaptureLoggingContextImpl.AlreadyCapturedError
|
8644
8700
|
self._has_captured = True
|
8645
8701
|
|
8646
|
-
if not
|
8647
|
-
self.
|
8702
|
+
if LoggingContextInfos.Caller not in self._infos:
|
8703
|
+
self._set_info(LoggingContextInfos.Caller.build(
|
8648
8704
|
self._stack_offset + 1,
|
8649
8705
|
stack_info=self._stack_info,
|
8650
|
-
)
|
8651
|
-
|
8652
|
-
if (caller := self._caller) is not None:
|
8653
|
-
self._source_file = LoggingSourceFileInfo.build(caller.file_path)
|
8654
|
-
else:
|
8655
|
-
self._source_file = None
|
8656
|
-
|
8657
|
-
self._thread = LoggingThreadInfo.build()
|
8658
|
-
self._process = LoggingProcessInfo.build()
|
8659
|
-
self._multiprocessing = LoggingMultiprocessingInfo.build()
|
8660
|
-
self._asyncio_task = LoggingAsyncioTaskInfo.build()
|
8661
|
-
|
8662
|
-
#
|
8663
|
-
|
8664
|
-
def caller(self) -> ta.Optional[LoggingCaller]:
|
8665
|
-
try:
|
8666
|
-
return self._caller
|
8667
|
-
except AttributeError:
|
8668
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
8669
|
-
|
8670
|
-
def source_file(self) -> ta.Optional[LoggingSourceFileInfo]:
|
8671
|
-
try:
|
8672
|
-
return self._source_file
|
8673
|
-
except AttributeError:
|
8674
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
8675
|
-
|
8676
|
-
#
|
8677
|
-
|
8678
|
-
def thread(self) -> ta.Optional[LoggingThreadInfo]:
|
8679
|
-
try:
|
8680
|
-
return self._thread
|
8681
|
-
except AttributeError:
|
8682
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
8683
|
-
|
8684
|
-
def process(self) -> ta.Optional[LoggingProcessInfo]:
|
8685
|
-
try:
|
8686
|
-
return self._process
|
8687
|
-
except AttributeError:
|
8688
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
8706
|
+
))
|
8689
8707
|
|
8690
|
-
|
8691
|
-
|
8692
|
-
|
8693
|
-
|
8694
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
8708
|
+
if (caller := self[LoggingContextInfos.Caller]) is not None:
|
8709
|
+
self._set_info(LoggingContextInfos.SourceFile.build(
|
8710
|
+
caller.file_path,
|
8711
|
+
))
|
8695
8712
|
|
8696
|
-
|
8697
|
-
|
8698
|
-
|
8699
|
-
|
8700
|
-
|
8713
|
+
self._set_info(
|
8714
|
+
LoggingContextInfos.Thread.build(),
|
8715
|
+
LoggingContextInfos.Process.build(),
|
8716
|
+
LoggingContextInfos.Multiprocessing.build(),
|
8717
|
+
LoggingContextInfos.AsyncioTask.build(),
|
8718
|
+
)
|
8701
8719
|
|
8702
8720
|
|
8703
8721
|
########################################
|
@@ -9578,7 +9596,6 @@ class CoroHttpServer:
|
|
9578
9596
|
|
9579
9597
|
|
9580
9598
|
class AnyLogger(Abstract, ta.Generic[T]):
|
9581
|
-
@ta.final
|
9582
9599
|
def is_enabled_for(self, level: LogLevel) -> bool:
|
9583
9600
|
return level >= self.get_effective_level()
|
9584
9601
|
|
@@ -9724,36 +9741,6 @@ class AnyLogger(Abstract, ta.Generic[T]):
|
|
9724
9741
|
|
9725
9742
|
##
|
9726
9743
|
|
9727
|
-
@classmethod
|
9728
|
-
def _prepare_msg_args(cls, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> ta.Tuple[str, tuple]:
|
9729
|
-
if callable(msg):
|
9730
|
-
if args:
|
9731
|
-
raise TypeError(f'Must not provide both a message function and args: {msg=} {args=}')
|
9732
|
-
x = msg()
|
9733
|
-
if isinstance(x, str):
|
9734
|
-
return x, ()
|
9735
|
-
elif isinstance(x, tuple):
|
9736
|
-
if x:
|
9737
|
-
return x[0], x[1:]
|
9738
|
-
else:
|
9739
|
-
return '', ()
|
9740
|
-
else:
|
9741
|
-
raise TypeError(x)
|
9742
|
-
|
9743
|
-
elif isinstance(msg, tuple):
|
9744
|
-
if args:
|
9745
|
-
raise TypeError(f'Must not provide both a tuple message and args: {msg=} {args=}')
|
9746
|
-
if msg:
|
9747
|
-
return msg[0], msg[1:]
|
9748
|
-
else:
|
9749
|
-
return '', ()
|
9750
|
-
|
9751
|
-
elif isinstance(msg, str):
|
9752
|
-
return msg, args
|
9753
|
-
|
9754
|
-
else:
|
9755
|
-
raise TypeError(msg)
|
9756
|
-
|
9757
9744
|
@abc.abstractmethod
|
9758
9745
|
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> T: # noqa
|
9759
9746
|
raise NotImplementedError
|
@@ -9777,7 +9764,7 @@ class AsyncLogger(AnyLogger[ta.Awaitable[None]], Abstract):
|
|
9777
9764
|
class AnyNopLogger(AnyLogger[T], Abstract):
|
9778
9765
|
@ta.final
|
9779
9766
|
def get_effective_level(self) -> LogLevel:
|
9780
|
-
return 999
|
9767
|
+
return -999
|
9781
9768
|
|
9782
9769
|
|
9783
9770
|
@ta.final
|
@@ -9794,137 +9781,543 @@ class AsyncNopLogger(AnyNopLogger[ta.Awaitable[None]], AsyncLogger):
|
|
9794
9781
|
|
9795
9782
|
########################################
|
9796
9783
|
# ../../../omlish/logs/std/records.py
|
9784
|
+
"""
|
9785
|
+
TODO:
|
9786
|
+
- TypedDict?
|
9787
|
+
"""
|
9797
9788
|
|
9798
9789
|
|
9799
9790
|
##
|
9800
9791
|
|
9801
9792
|
|
9802
|
-
|
9803
|
-
#
|
9804
|
-
#
|
9805
|
-
#
|
9806
|
-
#
|
9807
|
-
# - https://github.com/python/cpython/blob/
|
9808
|
-
#
|
9809
|
-
#
|
9810
|
-
# - name: str
|
9811
|
-
# - level: int
|
9812
|
-
# - pathname: str - Confusingly referred to as `fn` before the LogRecord ctor. May be empty or "(unknown file)".
|
9813
|
-
# - lineno: int - May be 0.
|
9814
|
-
# - msg: str
|
9815
|
-
# - args: tuple | dict | 1-tuple[dict]
|
9816
|
-
# - exc_info: LoggingExcInfoTuple | None
|
9817
|
-
# - func: str | None = None -> funcName
|
9818
|
-
# - sinfo: str | None = None -> stack_info
|
9819
|
-
#
|
9820
|
-
KNOWN_STD_LOGGING_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
|
9821
|
-
# Name of the logger used to log the call. Unmodified by ctor.
|
9822
|
-
name=str,
|
9793
|
+
class LoggingContextInfoRecordAdapters:
|
9794
|
+
# Ref:
|
9795
|
+
# - https://docs.python.org/3/library/logging.html#logrecord-attributes
|
9796
|
+
#
|
9797
|
+
# LogRecord:
|
9798
|
+
# - https://github.com/python/cpython/blob/39b2f82717a69dde7212bc39b673b0f55c99e6a3/Lib/logging/__init__.py#L276 (3.8) # noqa
|
9799
|
+
# - https://github.com/python/cpython/blob/f070f54c5f4a42c7c61d1d5d3b8f3b7203b4a0fb/Lib/logging/__init__.py#L286 (~3.14) # noqa
|
9800
|
+
#
|
9823
9801
|
|
9824
|
-
|
9825
|
-
|
9826
|
-
msg=str,
|
9802
|
+
def __new__(cls, *args, **kwargs): # noqa
|
9803
|
+
raise TypeError
|
9827
9804
|
|
9828
|
-
|
9829
|
-
|
9830
|
-
|
9831
|
-
|
9805
|
+
class Adapter(Abstract, ta.Generic[T]):
|
9806
|
+
@property
|
9807
|
+
@abc.abstractmethod
|
9808
|
+
def info_cls(self) -> ta.Type[LoggingContextInfo]:
|
9809
|
+
raise NotImplementedError
|
9832
9810
|
|
9833
|
-
|
9811
|
+
#
|
9834
9812
|
|
9835
|
-
|
9836
|
-
|
9837
|
-
|
9813
|
+
@ta.final
|
9814
|
+
class NOT_SET: # noqa
|
9815
|
+
def __new__(cls, *args, **kwargs): # noqa
|
9816
|
+
raise TypeError
|
9838
9817
|
|
9839
|
-
|
9840
|
-
|
9818
|
+
class RecordAttr(ta.NamedTuple):
|
9819
|
+
name: str
|
9820
|
+
type: ta.Any
|
9821
|
+
default: ta.Any
|
9841
9822
|
|
9842
|
-
|
9823
|
+
# @abc.abstractmethod
|
9824
|
+
record_attrs: ta.ClassVar[ta.Mapping[str, RecordAttr]]
|
9843
9825
|
|
9844
|
-
|
9845
|
-
|
9846
|
-
|
9826
|
+
@property
|
9827
|
+
@abc.abstractmethod
|
9828
|
+
def _record_attrs(self) -> ta.Union[
|
9829
|
+
ta.Mapping[str, ta.Any],
|
9830
|
+
ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]],
|
9831
|
+
]:
|
9832
|
+
raise NotImplementedError
|
9847
9833
|
|
9848
|
-
|
9849
|
-
filename=str,
|
9834
|
+
#
|
9850
9835
|
|
9851
|
-
|
9852
|
-
|
9853
|
-
|
9836
|
+
@abc.abstractmethod
|
9837
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
9838
|
+
raise NotImplementedError
|
9854
9839
|
|
9855
|
-
|
9840
|
+
#
|
9856
9841
|
|
9857
|
-
|
9858
|
-
|
9842
|
+
@abc.abstractmethod
|
9843
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[T]:
|
9844
|
+
raise NotImplementedError
|
9859
9845
|
|
9860
|
-
|
9861
|
-
exc_text=ta.Optional[str],
|
9846
|
+
#
|
9862
9847
|
|
9863
|
-
|
9848
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
9849
|
+
super().__init_subclass__(**kwargs)
|
9850
|
+
|
9851
|
+
if Abstract in cls.__bases__:
|
9852
|
+
return
|
9864
9853
|
|
9865
|
-
|
9866
|
-
|
9867
|
-
|
9868
|
-
|
9869
|
-
|
9854
|
+
if 'record_attrs' in cls.__dict__:
|
9855
|
+
raise TypeError(cls)
|
9856
|
+
if not isinstance(ra := cls.__dict__['_record_attrs'], collections.abc.Mapping):
|
9857
|
+
raise TypeError(ra)
|
9858
|
+
|
9859
|
+
rd: ta.Dict[str, LoggingContextInfoRecordAdapters.Adapter.RecordAttr] = {}
|
9860
|
+
for n, v in ra.items():
|
9861
|
+
if not n or not isinstance(n, str) or n in rd:
|
9862
|
+
raise AttributeError(n)
|
9863
|
+
if isinstance(v, tuple):
|
9864
|
+
t, d = v
|
9865
|
+
else:
|
9866
|
+
t, d = v, cls.NOT_SET
|
9867
|
+
rd[n] = cls.RecordAttr(
|
9868
|
+
name=n,
|
9869
|
+
type=t,
|
9870
|
+
default=d,
|
9871
|
+
)
|
9872
|
+
cls.record_attrs = rd
|
9870
9873
|
|
9871
|
-
|
9872
|
-
|
9873
|
-
|
9874
|
+
class RequiredAdapter(Adapter[T], Abstract):
|
9875
|
+
@property
|
9876
|
+
@abc.abstractmethod
|
9877
|
+
def _record_attrs(self) -> ta.Mapping[str, ta.Any]:
|
9878
|
+
raise NotImplementedError
|
9874
9879
|
|
9875
|
-
|
9876
|
-
# "(unknown function)" by Logger.findCaller / Logger._log.
|
9877
|
-
funcName=str,
|
9880
|
+
#
|
9878
9881
|
|
9879
|
-
|
9882
|
+
@ta.final
|
9883
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
9884
|
+
if (info := ctx.get_info(self.info_cls)) is not None:
|
9885
|
+
return self._info_to_record(info)
|
9886
|
+
else:
|
9887
|
+
raise TypeError # FIXME: fallback?
|
9880
9888
|
|
9881
|
-
|
9882
|
-
|
9883
|
-
|
9884
|
-
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
9885
|
-
# - https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
9886
|
-
#
|
9887
|
-
created=float,
|
9889
|
+
@abc.abstractmethod
|
9890
|
+
def _info_to_record(self, info: T) -> ta.Mapping[str, ta.Any]:
|
9891
|
+
raise NotImplementedError
|
9888
9892
|
|
9889
|
-
|
9890
|
-
msecs=float,
|
9893
|
+
#
|
9891
9894
|
|
9892
|
-
|
9893
|
-
|
9895
|
+
@abc.abstractmethod
|
9896
|
+
def record_to_info(self, rec: logging.LogRecord) -> T:
|
9897
|
+
raise NotImplementedError
|
9894
9898
|
|
9895
|
-
|
9899
|
+
#
|
9896
9900
|
|
9897
|
-
|
9898
|
-
|
9901
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
9902
|
+
super().__init_subclass__(**kwargs)
|
9899
9903
|
|
9900
|
-
|
9901
|
-
|
9904
|
+
if any(a.default is not cls.NOT_SET for a in cls.record_attrs.values()):
|
9905
|
+
raise TypeError(cls.record_attrs)
|
9902
9906
|
|
9903
|
-
|
9907
|
+
class OptionalAdapter(Adapter[T], Abstract, ta.Generic[T]):
|
9908
|
+
@property
|
9909
|
+
@abc.abstractmethod
|
9910
|
+
def _record_attrs(self) -> ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]:
|
9911
|
+
raise NotImplementedError
|
9912
|
+
|
9913
|
+
record_defaults: ta.ClassVar[ta.Mapping[str, ta.Any]]
|
9914
|
+
|
9915
|
+
#
|
9916
|
+
|
9917
|
+
@ta.final
|
9918
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
9919
|
+
if (info := ctx.get_info(self.info_cls)) is not None:
|
9920
|
+
return self._info_to_record(info)
|
9921
|
+
else:
|
9922
|
+
return self.record_defaults
|
9923
|
+
|
9924
|
+
@abc.abstractmethod
|
9925
|
+
def _info_to_record(self, info: T) -> ta.Mapping[str, ta.Any]:
|
9926
|
+
raise NotImplementedError
|
9927
|
+
|
9928
|
+
#
|
9929
|
+
|
9930
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
9931
|
+
super().__init_subclass__(**kwargs)
|
9932
|
+
|
9933
|
+
dd: ta.Dict[str, ta.Any] = {a.name: a.default for a in cls.record_attrs.values()}
|
9934
|
+
if any(d is cls.NOT_SET for d in dd.values()):
|
9935
|
+
raise TypeError(cls.record_attrs)
|
9936
|
+
cls.record_defaults = dd
|
9904
9937
|
|
9905
|
-
# Process name if available. Set to None if `logging.logMultiprocessing` is not truthy. Otherwise, set to
|
9906
|
-
# 'MainProcess', then `sys.modules.get('multiprocessing').current_process().name` if that works, otherwise remains
|
9907
|
-
# as 'MainProcess'.
|
9908
9938
|
#
|
9909
|
-
|
9939
|
+
|
9940
|
+
class Name(RequiredAdapter[LoggingContextInfos.Name]):
|
9941
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Name]] = LoggingContextInfos.Name
|
9942
|
+
|
9943
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
9944
|
+
# Name of the logger used to log the call. Unmodified by ctor.
|
9945
|
+
name=str,
|
9946
|
+
)
|
9947
|
+
|
9948
|
+
def _info_to_record(self, info: LoggingContextInfos.Name) -> ta.Mapping[str, ta.Any]:
|
9949
|
+
return dict(
|
9950
|
+
name=info.name,
|
9951
|
+
)
|
9952
|
+
|
9953
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Name:
|
9954
|
+
return LoggingContextInfos.Name(
|
9955
|
+
name=rec.name,
|
9956
|
+
)
|
9957
|
+
|
9958
|
+
class Level(RequiredAdapter[LoggingContextInfos.Level]):
|
9959
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Level]] = LoggingContextInfos.Level
|
9960
|
+
|
9961
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
9962
|
+
# Text logging level for the message ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). Set to
|
9963
|
+
# `getLevelName(level)`.
|
9964
|
+
levelname=str,
|
9965
|
+
|
9966
|
+
# Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL). Unmodified by ctor.
|
9967
|
+
levelno=int,
|
9968
|
+
)
|
9969
|
+
|
9970
|
+
def _info_to_record(self, info: LoggingContextInfos.Level) -> ta.Mapping[str, ta.Any]:
|
9971
|
+
return dict(
|
9972
|
+
levelname=info.name,
|
9973
|
+
levelno=int(info.level),
|
9974
|
+
)
|
9975
|
+
|
9976
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Level:
|
9977
|
+
return LoggingContextInfos.Level.build(rec.levelno)
|
9978
|
+
|
9979
|
+
class Msg(RequiredAdapter[LoggingContextInfos.Msg]):
|
9980
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Msg]] = LoggingContextInfos.Msg
|
9981
|
+
|
9982
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
9983
|
+
# The format string passed in the original logging call. Merged with args to produce message, or an
|
9984
|
+
# arbitrary object (see Using arbitrary objects as messages). Unmodified by ctor.
|
9985
|
+
msg=str,
|
9986
|
+
|
9987
|
+
# The tuple of arguments merged into msg to produce message, or a dict whose values are used for the merge
|
9988
|
+
# (when there is only one argument, and it is a dictionary). Ctor will transform a 1-tuple containing a
|
9989
|
+
# Mapping into just the mapping, but is otherwise unmodified.
|
9990
|
+
args=ta.Union[tuple, dict, None],
|
9991
|
+
)
|
9992
|
+
|
9993
|
+
def _info_to_record(self, info: LoggingContextInfos.Msg) -> ta.Mapping[str, ta.Any]:
|
9994
|
+
return dict(
|
9995
|
+
msg=info.msg,
|
9996
|
+
args=info.args,
|
9997
|
+
)
|
9998
|
+
|
9999
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Msg:
|
10000
|
+
return LoggingContextInfos.Msg(
|
10001
|
+
msg=rec.msg,
|
10002
|
+
args=rec.args,
|
10003
|
+
)
|
10004
|
+
|
10005
|
+
# FIXME: handled specially - all unknown attrs on LogRecord
|
10006
|
+
# class Extra(Adapter[LoggingContextInfos.Extra]):
|
10007
|
+
# _record_attrs: ta.ClassVar[ta.Mapping[str, ta.Union[ta.Any, ta.Tuple[ta.Any, ta.Any]]]] = dict()
|
9910
10008
|
#
|
9911
|
-
#
|
9912
|
-
#
|
10009
|
+
# def info_to_record(self, info: ta.Optional[LoggingContextInfos.Extra]) -> ta.Mapping[str, ta.Any]:
|
10010
|
+
# # FIXME:
|
10011
|
+
# # if extra is not None:
|
10012
|
+
# # for key in extra:
|
10013
|
+
# # if (key in ["message", "asctime"]) or (key in rv.__dict__):
|
10014
|
+
# # raise KeyError("Attempt to overwrite %r in LogRecord" % key)
|
10015
|
+
# # rv.__dict__[key] = extra[key]
|
10016
|
+
# return dict()
|
9913
10017
|
#
|
9914
|
-
|
10018
|
+
# def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Extra]:
|
10019
|
+
# return None
|
9915
10020
|
|
9916
|
-
|
9917
|
-
|
9918
|
-
process=ta.Optional[int],
|
10021
|
+
class Time(RequiredAdapter[LoggingContextInfos.Time]):
|
10022
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Time]] = LoggingContextInfos.Time
|
9919
10023
|
|
9920
|
-
|
10024
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
10025
|
+
# Time when the LogRecord was created. Set to `time.time_ns() / 1e9` for >=3.13.0b1, otherwise simply
|
10026
|
+
# `time.time()`.
|
10027
|
+
#
|
10028
|
+
# See:
|
10029
|
+
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
10030
|
+
# - https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
10031
|
+
#
|
10032
|
+
created=float,
|
9921
10033
|
|
9922
|
-
|
9923
|
-
|
9924
|
-
|
9925
|
-
|
10034
|
+
# Millisecond portion of the time when the LogRecord was created.
|
10035
|
+
msecs=float,
|
10036
|
+
|
10037
|
+
# Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.
|
10038
|
+
relativeCreated=float,
|
10039
|
+
)
|
10040
|
+
|
10041
|
+
def _info_to_record(self, info: LoggingContextInfos.Time) -> ta.Mapping[str, ta.Any]:
|
10042
|
+
return dict(
|
10043
|
+
created=info.secs,
|
10044
|
+
msecs=info.msecs,
|
10045
|
+
relativeCreated=info.relative_secs,
|
10046
|
+
)
|
10047
|
+
|
10048
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Time:
|
10049
|
+
return LoggingContextInfos.Time.build(
|
10050
|
+
int(rec.created * 1e9),
|
10051
|
+
)
|
10052
|
+
|
10053
|
+
class Exc(OptionalAdapter[LoggingContextInfos.Exc]):
|
10054
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Exc]] = LoggingContextInfos.Exc
|
10055
|
+
|
10056
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
10057
|
+
# Exception tuple (à la sys.exc_info) or, if no exception has occurred, None. Unmodified by ctor.
|
10058
|
+
exc_info=(ta.Optional[LoggingExcInfoTuple], None),
|
10059
|
+
|
10060
|
+
# Used to cache the traceback text. Simply set to None by ctor, later set by Formatter.format.
|
10061
|
+
exc_text=(ta.Optional[str], None),
|
10062
|
+
)
|
10063
|
+
|
10064
|
+
def _info_to_record(self, info: LoggingContextInfos.Exc) -> ta.Mapping[str, ta.Any]:
|
10065
|
+
return dict(
|
10066
|
+
exc_info=info.info_tuple,
|
10067
|
+
exc_text=None,
|
10068
|
+
)
|
10069
|
+
|
10070
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Exc]:
|
10071
|
+
# FIXME:
|
10072
|
+
# error: Argument 1 to "build" of "Exc" has incompatible type
|
10073
|
+
# "tuple[type[BaseException], BaseException, TracebackType | None] | tuple[None, None, None] | None"; expected # noqa
|
10074
|
+
# "BaseException | tuple[type[BaseException], BaseException, TracebackType | None] | bool | None" [arg-type] # noqa
|
10075
|
+
return LoggingContextInfos.Exc.build(rec.exc_info) # type: ignore[arg-type]
|
10076
|
+
|
10077
|
+
class Caller(OptionalAdapter[LoggingContextInfos.Caller]):
|
10078
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Caller]] = LoggingContextInfos.Caller
|
10079
|
+
|
10080
|
+
_UNKNOWN_PATH_NAME: ta.ClassVar[str] = '(unknown file)'
|
10081
|
+
_UNKNOWN_FUNC_NAME: ta.ClassVar[str] = '(unknown function)'
|
10082
|
+
|
10083
|
+
_STACK_INFO_PREFIX: ta.ClassVar[str] = 'Stack (most recent call last):\n'
|
10084
|
+
|
10085
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
10086
|
+
# Full pathname of the source file where the logging call was issued (if available). Unmodified by ctor. May
|
10087
|
+
# default to "(unknown file)" by Logger.findCaller / Logger._log.
|
10088
|
+
pathname=(str, _UNKNOWN_PATH_NAME),
|
10089
|
+
|
10090
|
+
# Source line number where the logging call was issued (if available). Unmodified by ctor. May default to 0
|
10091
|
+
# y Logger.findCaller / Logger._log.
|
10092
|
+
lineno=(int, 0),
|
10093
|
+
|
10094
|
+
# Name of function containing the logging call. Set by ctor to `func` arg, unmodified. May default to
|
10095
|
+
# "(unknown function)" by Logger.findCaller / Logger._log.
|
10096
|
+
funcName=(str, _UNKNOWN_FUNC_NAME),
|
10097
|
+
|
10098
|
+
# Stack frame information (where available) from the bottom of the stack in the current thread, up to and
|
10099
|
+
# including the stack frame of the logging call which resulted in the creation of this record. Set by ctor
|
10100
|
+
# to `sinfo` arg, unmodified. Mostly set, if requested, by `Logger.findCaller`, to
|
10101
|
+
# `traceback.print_stack(f)`, but prepended with the literal "Stack (most recent call last):\n", and
|
10102
|
+
# stripped of exactly one trailing `\n` if present.
|
10103
|
+
stack_info=(ta.Optional[str], None),
|
10104
|
+
)
|
10105
|
+
|
10106
|
+
def _info_to_record(self, caller: LoggingContextInfos.Caller) -> ta.Mapping[str, ta.Any]:
|
10107
|
+
if (sinfo := caller.stack_info) is not None:
|
10108
|
+
stack_info: ta.Optional[str] = '\n'.join([
|
10109
|
+
self._STACK_INFO_PREFIX,
|
10110
|
+
sinfo[1:] if sinfo.endswith('\n') else sinfo,
|
10111
|
+
])
|
10112
|
+
else:
|
10113
|
+
stack_info = None
|
10114
|
+
|
10115
|
+
return dict(
|
10116
|
+
pathname=caller.file_path,
|
10117
|
+
|
10118
|
+
lineno=caller.line_no,
|
10119
|
+
funcName=caller.func_name,
|
10120
|
+
|
10121
|
+
stack_info=stack_info,
|
10122
|
+
)
|
10123
|
+
|
10124
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Caller]:
|
10125
|
+
# FIXME: piecemeal?
|
10126
|
+
# FIXME: strip _STACK_INFO_PREFIX
|
10127
|
+
raise NotImplementedError
|
10128
|
+
|
10129
|
+
class SourceFile(Adapter[LoggingContextInfos.SourceFile]):
|
10130
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.SourceFile]] = LoggingContextInfos.SourceFile
|
10131
|
+
|
10132
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
10133
|
+
# Filename portion of pathname. Set to `os.path.basename(pathname)` if successful, otherwise defaults to
|
10134
|
+
# pathname.
|
10135
|
+
filename=str,
|
10136
|
+
|
10137
|
+
# Module (name portion of filename). Set to `os.path.splitext(filename)[0]`, otherwise defaults to
|
10138
|
+
# "Unknown module".
|
10139
|
+
module=str,
|
10140
|
+
)
|
10141
|
+
|
10142
|
+
_UNKNOWN_MODULE: ta.ClassVar[str] = 'Unknown module'
|
10143
|
+
|
10144
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
10145
|
+
if (info := ctx.get_info(LoggingContextInfos.SourceFile)) is not None:
|
10146
|
+
return dict(
|
10147
|
+
filename=info.file_name,
|
10148
|
+
module=info.module,
|
10149
|
+
)
|
10150
|
+
|
10151
|
+
if (caller := ctx.get_info(LoggingContextInfos.Caller)) is not None:
|
10152
|
+
return dict(
|
10153
|
+
filename=caller.file_path,
|
10154
|
+
module=self._UNKNOWN_MODULE,
|
10155
|
+
)
|
10156
|
+
|
10157
|
+
return dict(
|
10158
|
+
filename=LoggingContextInfoRecordAdapters.Caller._UNKNOWN_PATH_NAME, # noqa
|
10159
|
+
module=self._UNKNOWN_MODULE,
|
10160
|
+
)
|
10161
|
+
|
10162
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.SourceFile]:
|
10163
|
+
if not (
|
10164
|
+
rec.module is None or
|
10165
|
+
rec.module == self._UNKNOWN_MODULE
|
10166
|
+
):
|
10167
|
+
return LoggingContextInfos.SourceFile(
|
10168
|
+
file_name=rec.filename,
|
10169
|
+
module=rec.module, # FIXME: piecemeal?
|
10170
|
+
)
|
10171
|
+
|
10172
|
+
return None
|
10173
|
+
|
10174
|
+
class Thread(OptionalAdapter[LoggingContextInfos.Thread]):
|
10175
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Thread]] = LoggingContextInfos.Thread
|
10176
|
+
|
10177
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
10178
|
+
# Thread ID if available, and `logging.logThreads` is truthy.
|
10179
|
+
thread=(ta.Optional[int], None),
|
10180
|
+
|
10181
|
+
# Thread name if available, and `logging.logThreads` is truthy.
|
10182
|
+
threadName=(ta.Optional[str], None),
|
10183
|
+
)
|
10184
|
+
|
10185
|
+
def _info_to_record(self, info: LoggingContextInfos.Thread) -> ta.Mapping[str, ta.Any]:
|
10186
|
+
if logging.logThreads:
|
10187
|
+
return dict(
|
10188
|
+
thread=info.ident,
|
10189
|
+
threadName=info.name,
|
10190
|
+
)
|
10191
|
+
|
10192
|
+
return self.record_defaults
|
10193
|
+
|
10194
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Thread]:
|
10195
|
+
if (
|
10196
|
+
(ident := rec.thread) is not None and
|
10197
|
+
(name := rec.threadName) is not None
|
10198
|
+
):
|
10199
|
+
return LoggingContextInfos.Thread(
|
10200
|
+
ident=ident,
|
10201
|
+
native_id=None,
|
10202
|
+
name=name,
|
10203
|
+
)
|
10204
|
+
|
10205
|
+
return None
|
10206
|
+
|
10207
|
+
class Process(OptionalAdapter[LoggingContextInfos.Process]):
|
10208
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Process]] = LoggingContextInfos.Process
|
10209
|
+
|
10210
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
10211
|
+
# Process ID if available - that is, if `hasattr(os, 'getpid')` - and `logging.logProcesses` is truthy,
|
10212
|
+
# otherwise None.
|
10213
|
+
process=(ta.Optional[int], None),
|
10214
|
+
)
|
10215
|
+
|
10216
|
+
def _info_to_record(self, info: LoggingContextInfos.Process) -> ta.Mapping[str, ta.Any]:
|
10217
|
+
if logging.logProcesses:
|
10218
|
+
return dict(
|
10219
|
+
process=info.pid,
|
10220
|
+
)
|
10221
|
+
|
10222
|
+
return self.record_defaults
|
10223
|
+
|
10224
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Process]:
|
10225
|
+
if (
|
10226
|
+
(pid := rec.process) is not None
|
10227
|
+
):
|
10228
|
+
return LoggingContextInfos.Process(
|
10229
|
+
pid=pid,
|
10230
|
+
)
|
10231
|
+
|
10232
|
+
return None
|
10233
|
+
|
10234
|
+
class Multiprocessing(OptionalAdapter[LoggingContextInfos.Multiprocessing]):
|
10235
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Multiprocessing]] = LoggingContextInfos.Multiprocessing
|
10236
|
+
|
10237
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
10238
|
+
# Process name if available. Set to None if `logging.logMultiprocessing` is not truthy. Otherwise, set to
|
10239
|
+
# 'MainProcess', then `sys.modules.get('multiprocessing').current_process().name` if that works, otherwise
|
10240
|
+
# remains as 'MainProcess'.
|
10241
|
+
#
|
10242
|
+
# As noted by stdlib:
|
10243
|
+
#
|
10244
|
+
# Errors may occur if multiprocessing has not finished loading yet - e.g. if a custom import hook causes
|
10245
|
+
# third-party code to run when multiprocessing calls import. See issue 8200 for an example
|
10246
|
+
#
|
10247
|
+
processName=(ta.Optional[str], None),
|
10248
|
+
)
|
10249
|
+
|
10250
|
+
def _info_to_record(self, info: LoggingContextInfos.Multiprocessing) -> ta.Mapping[str, ta.Any]:
|
10251
|
+
if logging.logMultiprocessing:
|
10252
|
+
return dict(
|
10253
|
+
processName=info.process_name,
|
10254
|
+
)
|
10255
|
+
|
10256
|
+
return self.record_defaults
|
10257
|
+
|
10258
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Multiprocessing]:
|
10259
|
+
if (
|
10260
|
+
(process_name := rec.processName) is not None
|
10261
|
+
):
|
10262
|
+
return LoggingContextInfos.Multiprocessing(
|
10263
|
+
process_name=process_name,
|
10264
|
+
)
|
10265
|
+
|
10266
|
+
return None
|
10267
|
+
|
10268
|
+
class AsyncioTask(OptionalAdapter[LoggingContextInfos.AsyncioTask]):
|
10269
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.AsyncioTask]] = LoggingContextInfos.AsyncioTask
|
10270
|
+
|
10271
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Union[ta.Any, ta.Tuple[ta.Any, ta.Any]]]] = dict(
|
10272
|
+
# Absent <3.12, otherwise asyncio.Task name if available, and `logging.logAsyncioTasks` is truthy. Set to
|
10273
|
+
# `sys.modules.get('asyncio').current_task().get_name()`, otherwise None.
|
10274
|
+
taskName=(ta.Optional[str], None),
|
10275
|
+
)
|
10276
|
+
|
10277
|
+
def _info_to_record(self, info: LoggingContextInfos.AsyncioTask) -> ta.Mapping[str, ta.Any]:
|
10278
|
+
if getattr(logging, 'logAsyncioTasks', None): # Absent <3.12
|
10279
|
+
return dict(
|
10280
|
+
taskName=info.name,
|
10281
|
+
)
|
10282
|
+
|
10283
|
+
return self.record_defaults
|
9926
10284
|
|
9927
|
-
|
10285
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.AsyncioTask]:
|
10286
|
+
if (
|
10287
|
+
(name := getattr(rec, 'taskName', None)) is not None
|
10288
|
+
):
|
10289
|
+
return LoggingContextInfos.AsyncioTask(
|
10290
|
+
name=name,
|
10291
|
+
)
|
10292
|
+
|
10293
|
+
return None
|
10294
|
+
|
10295
|
+
|
10296
|
+
_LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_: ta.Sequence[LoggingContextInfoRecordAdapters.Adapter] = [ # noqa
|
10297
|
+
LoggingContextInfoRecordAdapters.Name(),
|
10298
|
+
LoggingContextInfoRecordAdapters.Level(),
|
10299
|
+
LoggingContextInfoRecordAdapters.Msg(),
|
10300
|
+
LoggingContextInfoRecordAdapters.Time(),
|
10301
|
+
LoggingContextInfoRecordAdapters.Exc(),
|
10302
|
+
LoggingContextInfoRecordAdapters.Caller(),
|
10303
|
+
LoggingContextInfoRecordAdapters.SourceFile(),
|
10304
|
+
LoggingContextInfoRecordAdapters.Thread(),
|
10305
|
+
LoggingContextInfoRecordAdapters.Process(),
|
10306
|
+
LoggingContextInfoRecordAdapters.Multiprocessing(),
|
10307
|
+
LoggingContextInfoRecordAdapters.AsyncioTask(),
|
10308
|
+
]
|
10309
|
+
|
10310
|
+
_LOGGING_CONTEXT_INFO_RECORD_ADAPTERS: ta.Mapping[ta.Type[LoggingContextInfo], LoggingContextInfoRecordAdapters.Adapter] = { # noqa
|
10311
|
+
ad.info_cls: ad for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_
|
10312
|
+
}
|
10313
|
+
|
10314
|
+
|
10315
|
+
##
|
10316
|
+
|
10317
|
+
|
10318
|
+
KNOWN_STD_LOGGING_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(
|
10319
|
+
a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs
|
10320
|
+
)
|
9928
10321
|
|
9929
10322
|
|
9930
10323
|
# Formatter:
|
@@ -9948,14 +10341,17 @@ KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
|
|
9948
10341
|
KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
|
9949
10342
|
|
9950
10343
|
|
9951
|
-
##
|
9952
|
-
|
9953
|
-
|
9954
10344
|
class UnknownStdLoggingRecordAttrsWarning(LoggingSetupWarning):
|
9955
10345
|
pass
|
9956
10346
|
|
9957
10347
|
|
9958
10348
|
def _check_std_logging_record_attrs() -> None:
|
10349
|
+
if (
|
10350
|
+
len([a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs]) !=
|
10351
|
+
len(KNOWN_STD_LOGGING_RECORD_ATTR_SET)
|
10352
|
+
):
|
10353
|
+
raise RuntimeError('Duplicate LoggingContextInfoRecordAdapter record attrs')
|
10354
|
+
|
9959
10355
|
rec_dct = dict(logging.makeLogRecord({}).__dict__)
|
9960
10356
|
|
9961
10357
|
if (unk_rec_fields := frozenset(rec_dct) - KNOWN_STD_LOGGING_RECORD_ATTR_SET):
|
@@ -9974,116 +10370,20 @@ _check_std_logging_record_attrs()
|
|
9974
10370
|
|
9975
10371
|
|
9976
10372
|
class LoggingContextLogRecord(logging.LogRecord):
|
9977
|
-
|
9978
|
-
|
9979
|
-
|
9980
|
-
|
9981
|
-
|
9982
|
-
|
9983
|
-
|
9984
|
-
|
9985
|
-
|
9986
|
-
|
9987
|
-
|
9988
|
-
|
9989
|
-
|
9990
|
-
|
9991
|
-
# msg,
|
9992
|
-
# args,
|
9993
|
-
# exc_info,
|
9994
|
-
# func=None,
|
9995
|
-
# sinfo=None,
|
9996
|
-
# **kwargs,
|
9997
|
-
*,
|
9998
|
-
name: str,
|
9999
|
-
msg: str,
|
10000
|
-
args: ta.Union[tuple, dict],
|
10001
|
-
|
10002
|
-
_logging_context: LoggingContext,
|
10003
|
-
) -> None:
|
10004
|
-
ctx = _logging_context
|
10005
|
-
|
10006
|
-
self.name: str = name
|
10007
|
-
|
10008
|
-
self.msg: str = msg
|
10009
|
-
|
10010
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L307
|
10011
|
-
if args and len(args) == 1 and isinstance(args[0], collections.abc.Mapping) and args[0]:
|
10012
|
-
args = args[0] # type: ignore[assignment]
|
10013
|
-
self.args: ta.Union[tuple, dict] = args
|
10014
|
-
|
10015
|
-
self.levelname: str = logging.getLevelName(ctx.level)
|
10016
|
-
self.levelno: int = ctx.level
|
10017
|
-
|
10018
|
-
if (caller := ctx.caller()) is not None:
|
10019
|
-
self.pathname: str = caller.file_path
|
10020
|
-
else:
|
10021
|
-
self.pathname = self._UNKNOWN_PATH_NAME
|
10022
|
-
|
10023
|
-
if (src_file := ctx.source_file()) is not None:
|
10024
|
-
self.filename: str = src_file.file_name
|
10025
|
-
self.module: str = src_file.module
|
10026
|
-
else:
|
10027
|
-
self.filename = self.pathname
|
10028
|
-
self.module = self._UNKNOWN_MODULE
|
10029
|
-
|
10030
|
-
self.exc_info: ta.Optional[LoggingExcInfoTuple] = ctx.exc_info_tuple
|
10031
|
-
self.exc_text: ta.Optional[str] = None
|
10032
|
-
|
10033
|
-
# If ctx.build_caller() was never called, we simply don't have a stack trace.
|
10034
|
-
if caller is not None:
|
10035
|
-
if (sinfo := caller.stack_info) is not None:
|
10036
|
-
self.stack_info: ta.Optional[str] = '\n'.join([
|
10037
|
-
self._STACK_INFO_PREFIX,
|
10038
|
-
sinfo[1:] if sinfo.endswith('\n') else sinfo,
|
10039
|
-
])
|
10040
|
-
else:
|
10041
|
-
self.stack_info = None
|
10042
|
-
|
10043
|
-
self.lineno: int = caller.line_no
|
10044
|
-
self.funcName: str = caller.name
|
10045
|
-
|
10046
|
-
else:
|
10047
|
-
self.stack_info = None
|
10048
|
-
|
10049
|
-
self.lineno = 0
|
10050
|
-
self.funcName = self._UNKNOWN_FUNC_NAME
|
10051
|
-
|
10052
|
-
times = ctx.times
|
10053
|
-
self.created: float = times.created
|
10054
|
-
self.msecs: float = times.msecs
|
10055
|
-
self.relativeCreated: float = times.relative_created
|
10056
|
-
|
10057
|
-
if logging.logThreads:
|
10058
|
-
thread = check.not_none(ctx.thread())
|
10059
|
-
self.thread: ta.Optional[int] = thread.ident
|
10060
|
-
self.threadName: ta.Optional[str] = thread.name
|
10061
|
-
else:
|
10062
|
-
self.thread = None
|
10063
|
-
self.threadName = None
|
10064
|
-
|
10065
|
-
if logging.logProcesses:
|
10066
|
-
process = check.not_none(ctx.process())
|
10067
|
-
self.process: ta.Optional[int] = process.pid
|
10068
|
-
else:
|
10069
|
-
self.process = None
|
10070
|
-
|
10071
|
-
if logging.logMultiprocessing:
|
10072
|
-
if (mp := ctx.multiprocessing()) is not None:
|
10073
|
-
self.processName: ta.Optional[str] = mp.process_name
|
10074
|
-
else:
|
10075
|
-
self.processName = None
|
10076
|
-
else:
|
10077
|
-
self.processName = None
|
10078
|
-
|
10079
|
-
# Absent <3.12
|
10080
|
-
if getattr(logging, 'logAsyncioTasks', None):
|
10081
|
-
if (at := ctx.asyncio_task()) is not None:
|
10082
|
-
self.taskName: ta.Optional[str] = at.name
|
10083
|
-
else:
|
10084
|
-
self.taskName = None
|
10085
|
-
else:
|
10086
|
-
self.taskName = None
|
10373
|
+
# LogRecord.__init__ args:
|
10374
|
+
# - name: str
|
10375
|
+
# - level: int
|
10376
|
+
# - pathname: str - Confusingly referred to as `fn` before the LogRecord ctor. May be empty or "(unknown file)".
|
10377
|
+
# - lineno: int - May be 0.
|
10378
|
+
# - msg: str
|
10379
|
+
# - args: tuple | dict | 1-tuple[dict]
|
10380
|
+
# - exc_info: LoggingExcInfoTuple | None
|
10381
|
+
# - func: str | None = None -> funcName
|
10382
|
+
# - sinfo: str | None = None -> stack_info
|
10383
|
+
|
10384
|
+
def __init__(self, *, _logging_context: LoggingContext) -> None: # noqa
|
10385
|
+
for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_:
|
10386
|
+
self.__dict__.update(ad.context_to_record(_logging_context))
|
10087
10387
|
|
10088
10388
|
|
10089
10389
|
########################################
|
@@ -10360,7 +10660,7 @@ class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
|
|
10360
10660
|
|
10361
10661
|
|
10362
10662
|
########################################
|
10363
|
-
# ../../../omlish/logs/std/
|
10663
|
+
# ../../../omlish/logs/std/loggers.py
|
10364
10664
|
|
10365
10665
|
|
10366
10666
|
##
|
@@ -10376,25 +10676,27 @@ class StdLogger(Logger):
|
|
10376
10676
|
def std(self) -> logging.Logger:
|
10377
10677
|
return self._std
|
10378
10678
|
|
10679
|
+
def is_enabled_for(self, level: LogLevel) -> bool:
|
10680
|
+
return self._std.isEnabledFor(level)
|
10681
|
+
|
10379
10682
|
def get_effective_level(self) -> LogLevel:
|
10380
10683
|
return self._std.getEffectiveLevel()
|
10381
10684
|
|
10382
10685
|
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> None:
|
10383
|
-
if not self.is_enabled_for(ctx.level):
|
10686
|
+
if not self.is_enabled_for(ctx.must_get_info(LoggingContextInfos.Level).level):
|
10384
10687
|
return
|
10385
10688
|
|
10386
|
-
ctx.
|
10387
|
-
|
10388
|
-
ms, args = self._prepare_msg_args(msg, *args)
|
10389
|
-
|
10390
|
-
rec = LoggingContextLogRecord(
|
10689
|
+
ctx.set_basic(
|
10391
10690
|
name=self._std.name,
|
10392
|
-
msg=ms,
|
10393
|
-
args=args,
|
10394
10691
|
|
10395
|
-
|
10692
|
+
msg=msg,
|
10693
|
+
args=args,
|
10396
10694
|
)
|
10397
10695
|
|
10696
|
+
ctx.capture()
|
10697
|
+
|
10698
|
+
rec = LoggingContextLogRecord(_logging_context=ctx)
|
10699
|
+
|
10398
10700
|
self._std.handle(rec)
|
10399
10701
|
|
10400
10702
|
|