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/manage.py
CHANGED
@@ -133,6 +133,13 @@ U = ta.TypeVar('U')
|
|
133
133
|
# ../../omlish/lite/timeouts.py
|
134
134
|
TimeoutLike = ta.Union['Timeout', ta.Type['Timeout.DEFAULT'], ta.Iterable['TimeoutLike'], float, None] # ta.TypeAlias
|
135
135
|
|
136
|
+
# ../../omlish/logs/infos.py
|
137
|
+
LoggingMsgFn = ta.Callable[[], ta.Union[str, tuple]] # ta.TypeAlias
|
138
|
+
LoggingExcInfoTuple = ta.Tuple[ta.Type[BaseException], BaseException, ta.Optional[types.TracebackType]] # ta.TypeAlias
|
139
|
+
LoggingExcInfo = ta.Union[BaseException, LoggingExcInfoTuple] # ta.TypeAlias
|
140
|
+
LoggingExcInfoArg = ta.Union[LoggingExcInfo, bool, None] # ta.TypeAlias
|
141
|
+
LoggingContextInfo = ta.Any # ta.TypeAlias
|
142
|
+
|
136
143
|
# ../../omlish/os/atomics.py
|
137
144
|
AtomicPathSwapKind = ta.Literal['dir', 'file']
|
138
145
|
AtomicPathSwapState = ta.Literal['open', 'committed', 'aborted'] # ta.TypeAlias
|
@@ -151,16 +158,11 @@ InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
|
|
151
158
|
InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
|
152
159
|
|
153
160
|
# ../../omlish/logs/contexts.py
|
154
|
-
|
155
|
-
LoggingExcInfo = ta.Union[BaseException, LoggingExcInfoTuple] # ta.TypeAlias
|
156
|
-
LoggingExcInfoArg = ta.Union[LoggingExcInfo, bool, None] # ta.TypeAlias
|
161
|
+
LoggingContextInfoT = ta.TypeVar('LoggingContextInfoT', bound=LoggingContextInfo)
|
157
162
|
|
158
163
|
# deploy/specs.py
|
159
164
|
KeyDeployTagT = ta.TypeVar('KeyDeployTagT', bound='KeyDeployTag')
|
160
165
|
|
161
|
-
# ../../omlish/logs/base.py
|
162
|
-
LoggingMsgFn = ta.Callable[[], ta.Union[str, tuple]] # ta.TypeAlias
|
163
|
-
|
164
166
|
# ../../omlish/subprocesses/base.py
|
165
167
|
SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
|
166
168
|
|
@@ -2779,8 +2781,6 @@ class AttrOps(ta.Generic[T]):
|
|
2779
2781
|
self._eq = _eq
|
2780
2782
|
return _eq
|
2781
2783
|
|
2782
|
-
#
|
2783
|
-
|
2784
2784
|
@property
|
2785
2785
|
def hash_eq(self) -> ta.Tuple[
|
2786
2786
|
ta.Callable[[T], int],
|
@@ -2788,6 +2788,8 @@ class AttrOps(ta.Generic[T]):
|
|
2788
2788
|
]:
|
2789
2789
|
return (self.hash, self.eq)
|
2790
2790
|
|
2791
|
+
#
|
2792
|
+
|
2791
2793
|
@property
|
2792
2794
|
def repr_hash_eq(self) -> ta.Tuple[
|
2793
2795
|
ta.Callable[[T], str],
|
@@ -2798,20 +2800,25 @@ class AttrOps(ta.Generic[T]):
|
|
2798
2800
|
|
2799
2801
|
#
|
2800
2802
|
|
2803
|
+
class NOT_SET: # noqa
|
2804
|
+
def __new__(cls, *args, **kwargs): # noqa
|
2805
|
+
raise TypeError
|
2806
|
+
|
2801
2807
|
def install(
|
2802
2808
|
self,
|
2803
2809
|
locals_dct: ta.MutableMapping[str, ta.Any],
|
2804
2810
|
*,
|
2805
|
-
|
2806
|
-
|
2807
|
-
|
2808
|
-
eq: bool = False,
|
2811
|
+
repr: ta.Union[bool, ta.Type[NOT_SET]] = NOT_SET, # noqa
|
2812
|
+
hash: ta.Union[bool, ta.Type[NOT_SET]] = NOT_SET, # noqa
|
2813
|
+
eq: ta.Union[bool, ta.Type[NOT_SET]] = NOT_SET,
|
2809
2814
|
) -> 'AttrOps[T]':
|
2810
|
-
if repr
|
2815
|
+
if all(a is self.NOT_SET for a in (repr, hash, eq)):
|
2816
|
+
repr = hash = eq = True # noqa
|
2817
|
+
if repr:
|
2811
2818
|
locals_dct.update(__repr__=self.repr)
|
2812
|
-
if hash
|
2819
|
+
if hash:
|
2813
2820
|
locals_dct.update(__hash__=self.hash)
|
2814
|
-
if eq
|
2821
|
+
if eq:
|
2815
2822
|
locals_dct.update(__eq__=self.eq)
|
2816
2823
|
return self
|
2817
2824
|
|
@@ -3986,124 +3993,6 @@ def typing_annotations_attr() -> str:
|
|
3986
3993
|
return _TYPING_ANNOTATIONS_ATTR
|
3987
3994
|
|
3988
3995
|
|
3989
|
-
########################################
|
3990
|
-
# ../../../omlish/logs/infos.py
|
3991
|
-
|
3992
|
-
|
3993
|
-
##
|
3994
|
-
|
3995
|
-
|
3996
|
-
def logging_context_info(cls):
|
3997
|
-
return cls
|
3998
|
-
|
3999
|
-
|
4000
|
-
##
|
4001
|
-
|
4002
|
-
|
4003
|
-
@logging_context_info
|
4004
|
-
@ta.final
|
4005
|
-
class LoggingSourceFileInfo(ta.NamedTuple):
|
4006
|
-
file_name: str
|
4007
|
-
module: str
|
4008
|
-
|
4009
|
-
@classmethod
|
4010
|
-
def build(cls, file_path: ta.Optional[str]) -> ta.Optional['LoggingSourceFileInfo']:
|
4011
|
-
if file_path is None:
|
4012
|
-
return None
|
4013
|
-
|
4014
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L331-L336 # noqa
|
4015
|
-
try:
|
4016
|
-
file_name = os.path.basename(file_path)
|
4017
|
-
module = os.path.splitext(file_name)[0]
|
4018
|
-
except (TypeError, ValueError, AttributeError):
|
4019
|
-
return None
|
4020
|
-
|
4021
|
-
return cls(
|
4022
|
-
file_name,
|
4023
|
-
module,
|
4024
|
-
)
|
4025
|
-
|
4026
|
-
|
4027
|
-
##
|
4028
|
-
|
4029
|
-
|
4030
|
-
@logging_context_info
|
4031
|
-
@ta.final
|
4032
|
-
class LoggingThreadInfo(ta.NamedTuple):
|
4033
|
-
ident: int
|
4034
|
-
native_id: ta.Optional[int]
|
4035
|
-
name: str
|
4036
|
-
|
4037
|
-
@classmethod
|
4038
|
-
def build(cls) -> 'LoggingThreadInfo':
|
4039
|
-
return cls(
|
4040
|
-
threading.get_ident(),
|
4041
|
-
threading.get_native_id() if hasattr(threading, 'get_native_id') else None,
|
4042
|
-
threading.current_thread().name,
|
4043
|
-
)
|
4044
|
-
|
4045
|
-
|
4046
|
-
##
|
4047
|
-
|
4048
|
-
|
4049
|
-
@logging_context_info
|
4050
|
-
@ta.final
|
4051
|
-
class LoggingProcessInfo(ta.NamedTuple):
|
4052
|
-
pid: int
|
4053
|
-
|
4054
|
-
@classmethod
|
4055
|
-
def build(cls) -> 'LoggingProcessInfo':
|
4056
|
-
return cls(
|
4057
|
-
os.getpid(),
|
4058
|
-
)
|
4059
|
-
|
4060
|
-
|
4061
|
-
##
|
4062
|
-
|
4063
|
-
|
4064
|
-
@logging_context_info
|
4065
|
-
@ta.final
|
4066
|
-
class LoggingMultiprocessingInfo(ta.NamedTuple):
|
4067
|
-
process_name: str
|
4068
|
-
|
4069
|
-
@classmethod
|
4070
|
-
def build(cls) -> ta.Optional['LoggingMultiprocessingInfo']:
|
4071
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L355-L364 # noqa
|
4072
|
-
if (mp := sys.modules.get('multiprocessing')) is None:
|
4073
|
-
return None
|
4074
|
-
|
4075
|
-
return cls(
|
4076
|
-
mp.current_process().name,
|
4077
|
-
)
|
4078
|
-
|
4079
|
-
|
4080
|
-
##
|
4081
|
-
|
4082
|
-
|
4083
|
-
@logging_context_info
|
4084
|
-
@ta.final
|
4085
|
-
class LoggingAsyncioTaskInfo(ta.NamedTuple):
|
4086
|
-
name: str
|
4087
|
-
|
4088
|
-
@classmethod
|
4089
|
-
def build(cls) -> ta.Optional['LoggingAsyncioTaskInfo']:
|
4090
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L372-L377 # noqa
|
4091
|
-
if (asyncio := sys.modules.get('asyncio')) is None:
|
4092
|
-
return None
|
4093
|
-
|
4094
|
-
try:
|
4095
|
-
task = asyncio.current_task()
|
4096
|
-
except Exception: # noqa
|
4097
|
-
return None
|
4098
|
-
|
4099
|
-
if task is None:
|
4100
|
-
return None
|
4101
|
-
|
4102
|
-
return cls(
|
4103
|
-
task.get_name(), # Always non-None
|
4104
|
-
)
|
4105
|
-
|
4106
|
-
|
4107
3996
|
########################################
|
4108
3997
|
# ../../../omlish/logs/levels.py
|
4109
3998
|
|
@@ -4123,36 +4012,66 @@ class NamedLogLevel(int):
|
|
4123
4012
|
|
4124
4013
|
#
|
4125
4014
|
|
4126
|
-
|
4127
|
-
|
4128
|
-
|
4015
|
+
_CACHE: ta.ClassVar[ta.MutableMapping[int, 'NamedLogLevel']] = {}
|
4016
|
+
|
4017
|
+
@ta.overload
|
4018
|
+
def __new__(cls, name: str, offset: int = 0, /) -> 'NamedLogLevel':
|
4019
|
+
...
|
4020
|
+
|
4021
|
+
@ta.overload
|
4022
|
+
def __new__(cls, i: int, /) -> 'NamedLogLevel':
|
4023
|
+
...
|
4129
4024
|
|
4130
|
-
|
4025
|
+
def __new__(cls, x, offset=0, /):
|
4026
|
+
if isinstance(x, str):
|
4027
|
+
return cls(cls._INTS_BY_NAME[x.upper()] + offset)
|
4028
|
+
elif not offset and (c := cls._CACHE.get(x)) is not None:
|
4029
|
+
return c
|
4030
|
+
else:
|
4031
|
+
return super().__new__(cls, x + offset)
|
4032
|
+
|
4033
|
+
#
|
4034
|
+
|
4035
|
+
_name_and_offset: ta.Tuple[str, int]
|
4131
4036
|
|
4132
4037
|
@property
|
4133
|
-
def
|
4038
|
+
def name_and_offset(self) -> ta.Tuple[str, int]:
|
4134
4039
|
try:
|
4135
|
-
return self.
|
4040
|
+
return self._name_and_offset
|
4136
4041
|
except AttributeError:
|
4137
4042
|
pass
|
4138
4043
|
|
4139
|
-
if (n := self.
|
4044
|
+
if (n := self._NAMES_BY_INT.get(self)) is not None:
|
4045
|
+
t = (n, 0)
|
4046
|
+
else:
|
4140
4047
|
for n, i in self._NAME_INT_PAIRS: # noqa
|
4141
4048
|
if self >= i:
|
4049
|
+
t = (n, (self - i))
|
4142
4050
|
break
|
4143
4051
|
else:
|
4144
|
-
|
4052
|
+
t = ('NOTSET', int(self))
|
4053
|
+
|
4054
|
+
self._name_and_offset = t
|
4055
|
+
return t
|
4056
|
+
|
4057
|
+
@property
|
4058
|
+
def exact_name(self) -> ta.Optional[str]:
|
4059
|
+
n, o = self.name_and_offset
|
4060
|
+
return n if not o else None
|
4145
4061
|
|
4146
|
-
|
4062
|
+
@property
|
4063
|
+
def effective_name(self) -> str:
|
4064
|
+
n, _ = self.name_and_offset
|
4147
4065
|
return n
|
4148
4066
|
|
4149
4067
|
#
|
4150
4068
|
|
4151
|
-
def __repr__(self) -> str:
|
4152
|
-
return f'{self.__class__.__name__}({int(self)})'
|
4153
|
-
|
4154
4069
|
def __str__(self) -> str:
|
4155
|
-
return self.exact_name or f'{self.effective_name
|
4070
|
+
return self.exact_name or f'{self.effective_name}{int(self):+}'
|
4071
|
+
|
4072
|
+
def __repr__(self) -> str:
|
4073
|
+
n, o = self.name_and_offset
|
4074
|
+
return f'{self.__class__.__name__}({n!r}{f", {int(o)}" if o else ""})'
|
4156
4075
|
|
4157
4076
|
#
|
4158
4077
|
|
@@ -4172,6 +4091,9 @@ NamedLogLevel.DEBUG = NamedLogLevel(logging.DEBUG)
|
|
4172
4091
|
NamedLogLevel.NOTSET = NamedLogLevel(logging.NOTSET)
|
4173
4092
|
|
4174
4093
|
|
4094
|
+
NamedLogLevel._CACHE.update({i: NamedLogLevel(i) for i in NamedLogLevel._NAMES_BY_INT}) # noqa
|
4095
|
+
|
4096
|
+
|
4175
4097
|
########################################
|
4176
4098
|
# ../../../omlish/logs/std/filters.py
|
4177
4099
|
|
@@ -7585,223 +7507,338 @@ class PredicateTimeout(Timeout):
|
|
7585
7507
|
|
7586
7508
|
|
7587
7509
|
########################################
|
7588
|
-
# ../../../omlish/logs/
|
7510
|
+
# ../../../omlish/logs/infos.py
|
7511
|
+
"""
|
7512
|
+
TODO:
|
7513
|
+
- remove redundant info fields only present for std adaptation (Level.name, ...)
|
7514
|
+
"""
|
7589
7515
|
|
7590
7516
|
|
7591
7517
|
##
|
7592
7518
|
|
7593
7519
|
|
7594
|
-
|
7595
|
-
|
7596
|
-
class LoggingCaller(ta.NamedTuple):
|
7597
|
-
file_path: str
|
7598
|
-
line_no: int
|
7599
|
-
name: str
|
7600
|
-
stack_info: ta.Optional[str]
|
7520
|
+
def logging_context_info(cls):
|
7521
|
+
return cls
|
7601
7522
|
|
7602
|
-
@classmethod
|
7603
|
-
def is_internal_frame(cls, frame: types.FrameType) -> bool:
|
7604
|
-
file_path = os.path.normcase(frame.f_code.co_filename)
|
7605
7523
|
|
7606
|
-
|
7607
|
-
|
7608
|
-
|
7609
|
-
|
7610
|
-
return True
|
7524
|
+
@ta.final
|
7525
|
+
class LoggingContextInfos:
|
7526
|
+
def __new__(cls, *args, **kwargs): # noqa
|
7527
|
+
raise TypeError
|
7611
7528
|
|
7612
|
-
|
7529
|
+
#
|
7613
7530
|
|
7614
|
-
@
|
7615
|
-
|
7616
|
-
|
7531
|
+
@logging_context_info
|
7532
|
+
@ta.final
|
7533
|
+
class Name(ta.NamedTuple):
|
7534
|
+
name: str
|
7617
7535
|
|
7618
|
-
|
7619
|
-
|
7620
|
-
|
7621
|
-
|
7622
|
-
|
7536
|
+
@logging_context_info
|
7537
|
+
@ta.final
|
7538
|
+
class Level(ta.NamedTuple):
|
7539
|
+
level: NamedLogLevel
|
7540
|
+
name: str
|
7623
7541
|
|
7624
|
-
|
7542
|
+
@classmethod
|
7543
|
+
def build(cls, level: int) -> 'LoggingContextInfos.Level':
|
7544
|
+
nl: NamedLogLevel = level if level.__class__ is NamedLogLevel else NamedLogLevel(level) # type: ignore[assignment] # noqa
|
7545
|
+
return cls(
|
7546
|
+
level=nl,
|
7547
|
+
name=logging.getLevelName(nl),
|
7548
|
+
)
|
7625
7549
|
|
7626
|
-
|
7550
|
+
@logging_context_info
|
7551
|
+
@ta.final
|
7552
|
+
class Msg(ta.NamedTuple):
|
7553
|
+
msg: str
|
7554
|
+
args: ta.Union[tuple, ta.Mapping[ta.Any, ta.Any], None]
|
7627
7555
|
|
7628
|
-
|
7629
|
-
|
7630
|
-
|
7631
|
-
|
7632
|
-
|
7633
|
-
|
7634
|
-
|
7635
|
-
|
7636
|
-
|
7556
|
+
@classmethod
|
7557
|
+
def build(
|
7558
|
+
cls,
|
7559
|
+
msg: ta.Union[str, tuple, LoggingMsgFn],
|
7560
|
+
*args: ta.Any,
|
7561
|
+
) -> 'LoggingContextInfos.Msg':
|
7562
|
+
s: str
|
7563
|
+
a: ta.Any
|
7564
|
+
|
7565
|
+
if callable(msg):
|
7566
|
+
if args:
|
7567
|
+
raise TypeError(f'Must not provide both a message function and args: {msg=} {args=}')
|
7568
|
+
x = msg()
|
7569
|
+
if isinstance(x, str):
|
7570
|
+
s, a = x, ()
|
7571
|
+
elif isinstance(x, tuple):
|
7572
|
+
if x:
|
7573
|
+
s, a = x[0], x[1:]
|
7574
|
+
else:
|
7575
|
+
s, a = '', ()
|
7576
|
+
else:
|
7577
|
+
raise TypeError(x)
|
7637
7578
|
|
7638
|
-
|
7639
|
-
|
7640
|
-
|
7641
|
-
|
7642
|
-
|
7643
|
-
|
7644
|
-
|
7645
|
-
if sinfo[-1] == '\n':
|
7646
|
-
sinfo = sinfo[:-1]
|
7579
|
+
elif isinstance(msg, tuple):
|
7580
|
+
if args:
|
7581
|
+
raise TypeError(f'Must not provide both a tuple message and args: {msg=} {args=}')
|
7582
|
+
if msg:
|
7583
|
+
s, a = msg[0], msg[1:]
|
7584
|
+
else:
|
7585
|
+
s, a = '', ()
|
7647
7586
|
|
7648
|
-
|
7649
|
-
|
7650
|
-
f.f_lineno or 0,
|
7651
|
-
f.f_code.co_name,
|
7652
|
-
sinfo,
|
7653
|
-
)
|
7587
|
+
elif isinstance(msg, str):
|
7588
|
+
s, a = msg, args
|
7654
7589
|
|
7590
|
+
else:
|
7591
|
+
raise TypeError(msg)
|
7655
7592
|
|
7656
|
-
|
7657
|
-
|
7593
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L307 # noqa
|
7594
|
+
if a and len(a) == 1 and isinstance(a[0], collections.abc.Mapping) and a[0]:
|
7595
|
+
a = a[0]
|
7658
7596
|
|
7597
|
+
return cls(
|
7598
|
+
msg=s,
|
7599
|
+
args=a,
|
7600
|
+
)
|
7659
7601
|
|
7660
|
-
|
7602
|
+
@logging_context_info
|
7603
|
+
@ta.final
|
7604
|
+
class Extra(ta.NamedTuple):
|
7605
|
+
extra: ta.Mapping[ta.Any, ta.Any]
|
7661
7606
|
|
7607
|
+
@logging_context_info
|
7608
|
+
@ta.final
|
7609
|
+
class Time(ta.NamedTuple):
|
7610
|
+
ns: int
|
7611
|
+
secs: float
|
7612
|
+
msecs: float
|
7613
|
+
relative_secs: float
|
7662
7614
|
|
7663
|
-
|
7664
|
-
|
7615
|
+
@classmethod
|
7616
|
+
def get_std_start_ns(cls) -> int:
|
7617
|
+
x: ta.Any = logging._startTime # type: ignore[attr-defined] # noqa
|
7665
7618
|
|
7666
|
-
|
7619
|
+
# Before 3.13.0b1 this will be `time.time()`, a float of seconds. After that, it will be `time.time_ns()`,
|
7620
|
+
# an int.
|
7621
|
+
#
|
7622
|
+
# See:
|
7623
|
+
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
7624
|
+
#
|
7625
|
+
if isinstance(x, float):
|
7626
|
+
return int(x * 1e9)
|
7627
|
+
else:
|
7628
|
+
return x
|
7667
7629
|
|
7668
|
-
|
7630
|
+
@classmethod
|
7631
|
+
def build(
|
7632
|
+
cls,
|
7633
|
+
ns: int,
|
7634
|
+
*,
|
7635
|
+
start_ns: ta.Optional[int] = None,
|
7636
|
+
) -> 'LoggingContextInfos.Time':
|
7637
|
+
# https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
7638
|
+
secs = ns / 1e9 # ns to float seconds
|
7639
|
+
|
7640
|
+
# Get the number of whole milliseconds (0-999) in the fractional part of seconds.
|
7641
|
+
# Eg: 1_677_903_920_999_998_503 ns --> 999_998_503 ns--> 999 ms
|
7642
|
+
# Convert to float by adding 0.0 for historical reasons. See gh-89047
|
7643
|
+
msecs = (ns % 1_000_000_000) // 1_000_000 + 0.0
|
7644
|
+
|
7645
|
+
# https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
7646
|
+
if msecs == 999.0 and int(secs) != ns // 1_000_000_000:
|
7647
|
+
# ns -> sec conversion can round up, e.g:
|
7648
|
+
# 1_677_903_920_999_999_900 ns --> 1_677_903_921.0 sec
|
7649
|
+
msecs = 0.0
|
7650
|
+
|
7651
|
+
if start_ns is None:
|
7652
|
+
start_ns = cls.get_std_start_ns()
|
7653
|
+
relative_secs = (ns - start_ns) / 1e6
|
7654
|
+
|
7655
|
+
return cls(
|
7656
|
+
ns=ns,
|
7657
|
+
secs=secs,
|
7658
|
+
msecs=msecs,
|
7659
|
+
relative_secs=relative_secs,
|
7660
|
+
)
|
7669
7661
|
|
7670
|
-
|
7662
|
+
@logging_context_info
|
7663
|
+
@ta.final
|
7664
|
+
class Exc(ta.NamedTuple):
|
7665
|
+
info: LoggingExcInfo
|
7666
|
+
info_tuple: LoggingExcInfoTuple
|
7671
7667
|
|
7672
|
-
|
7668
|
+
@classmethod
|
7669
|
+
def build(
|
7670
|
+
cls,
|
7671
|
+
arg: LoggingExcInfoArg = False,
|
7672
|
+
) -> ta.Optional['LoggingContextInfos.Exc']:
|
7673
|
+
if arg is True:
|
7674
|
+
sys_exc_info = sys.exc_info()
|
7675
|
+
if sys_exc_info[0] is not None:
|
7676
|
+
arg = sys_exc_info
|
7677
|
+
else:
|
7678
|
+
arg = None
|
7679
|
+
elif arg is False:
|
7680
|
+
arg = None
|
7681
|
+
if arg is None:
|
7682
|
+
return None
|
7673
7683
|
|
7674
|
-
|
7684
|
+
info: LoggingExcInfo = arg
|
7685
|
+
if isinstance(info, BaseException):
|
7686
|
+
info_tuple: LoggingExcInfoTuple = (type(info), info, info.__traceback__) # noqa
|
7687
|
+
else:
|
7688
|
+
info_tuple = info
|
7675
7689
|
|
7676
|
-
|
7690
|
+
return cls(
|
7691
|
+
info=info,
|
7692
|
+
info_tuple=info_tuple,
|
7693
|
+
)
|
7677
7694
|
|
7678
|
-
|
7695
|
+
@logging_context_info
|
7696
|
+
@ta.final
|
7697
|
+
class Caller(ta.NamedTuple):
|
7698
|
+
file_path: str
|
7699
|
+
line_no: int
|
7700
|
+
func_name: str
|
7701
|
+
stack_info: ta.Optional[str]
|
7679
7702
|
|
7680
|
-
|
7703
|
+
@classmethod
|
7704
|
+
def is_internal_frame(cls, frame: types.FrameType) -> bool:
|
7705
|
+
file_path = os.path.normcase(frame.f_code.co_filename)
|
7681
7706
|
|
7682
|
-
|
7707
|
+
# Yes, really.
|
7708
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L204 # noqa
|
7709
|
+
# https://github.com/python/cpython/commit/5ca6d7469be53960843df39bb900e9c3359f127f
|
7710
|
+
if 'importlib' in file_path and '_bootstrap' in file_path:
|
7711
|
+
return True
|
7683
7712
|
|
7684
|
-
|
7713
|
+
return False
|
7685
7714
|
|
7715
|
+
@classmethod
|
7716
|
+
def find_frame(cls, stack_offset: int = 0) -> ta.Optional[types.FrameType]:
|
7717
|
+
f: ta.Optional[types.FrameType] = sys._getframe(2 + stack_offset) # noqa
|
7686
7718
|
|
7687
|
-
|
7688
|
-
#
|
7689
|
-
|
7690
|
-
|
7691
|
-
|
7692
|
-
"""
|
7719
|
+
while f is not None:
|
7720
|
+
# NOTE: We don't check __file__ like stdlib since we may be running amalgamated - we rely on careful,
|
7721
|
+
# manual stack_offset management.
|
7722
|
+
if hasattr(f, 'f_code'):
|
7723
|
+
return f
|
7693
7724
|
|
7725
|
+
f = f.f_back
|
7694
7726
|
|
7695
|
-
|
7727
|
+
return None
|
7696
7728
|
|
7729
|
+
@classmethod
|
7730
|
+
def build(
|
7731
|
+
cls,
|
7732
|
+
stack_offset: int = 0,
|
7733
|
+
*,
|
7734
|
+
stack_info: bool = False,
|
7735
|
+
) -> ta.Optional['LoggingContextInfos.Caller']:
|
7736
|
+
if (f := cls.find_frame(stack_offset + 1)) is None:
|
7737
|
+
return None
|
7697
7738
|
|
7698
|
-
|
7699
|
-
|
7700
|
-
|
7701
|
-
|
7702
|
-
|
7703
|
-
|
7704
|
-
|
7705
|
-
|
7706
|
-
|
7707
|
-
|
7708
|
-
|
7709
|
-
|
7710
|
-
|
7711
|
-
|
7712
|
-
|
7713
|
-
|
7714
|
-
'msecs': False,
|
7715
|
-
'relativeCreated': False,
|
7716
|
-
'thread': False,
|
7717
|
-
'threadName': False,
|
7718
|
-
'processName': False,
|
7719
|
-
'process': False,
|
7720
|
-
}
|
7739
|
+
# https://github.com/python/cpython/blob/08e9794517063c8cd92c48714071b1d3c60b71bd/Lib/logging/__init__.py#L1616-L1623 # noqa
|
7740
|
+
sinfo = None
|
7741
|
+
if stack_info:
|
7742
|
+
sio = io.StringIO()
|
7743
|
+
traceback.print_stack(f, file=sio)
|
7744
|
+
sinfo = sio.getvalue()
|
7745
|
+
sio.close()
|
7746
|
+
if sinfo[-1] == '\n':
|
7747
|
+
sinfo = sinfo[:-1]
|
7748
|
+
|
7749
|
+
return cls(
|
7750
|
+
file_path=f.f_code.co_filename,
|
7751
|
+
line_no=f.f_lineno or 0,
|
7752
|
+
func_name=f.f_code.co_name,
|
7753
|
+
stack_info=sinfo,
|
7754
|
+
)
|
7721
7755
|
|
7722
|
-
|
7723
|
-
|
7724
|
-
|
7725
|
-
|
7726
|
-
|
7727
|
-
) -> None:
|
7728
|
-
super().__init__(*args, **kwargs)
|
7756
|
+
@logging_context_info
|
7757
|
+
@ta.final
|
7758
|
+
class SourceFile(ta.NamedTuple):
|
7759
|
+
file_name: str
|
7760
|
+
module: str
|
7729
7761
|
|
7730
|
-
|
7731
|
-
|
7732
|
-
|
7762
|
+
@classmethod
|
7763
|
+
def build(cls, caller_file_path: ta.Optional[str]) -> ta.Optional['LoggingContextInfos.SourceFile']:
|
7764
|
+
if caller_file_path is None:
|
7765
|
+
return None
|
7733
7766
|
|
7734
|
-
|
7735
|
-
|
7736
|
-
|
7737
|
-
|
7738
|
-
|
7739
|
-
|
7740
|
-
}
|
7741
|
-
return self._json_dumps(dct)
|
7767
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L331-L336 # noqa
|
7768
|
+
try:
|
7769
|
+
file_name = os.path.basename(caller_file_path)
|
7770
|
+
module = os.path.splitext(file_name)[0]
|
7771
|
+
except (TypeError, ValueError, AttributeError):
|
7772
|
+
return None
|
7742
7773
|
|
7774
|
+
return cls(
|
7775
|
+
file_name=file_name,
|
7776
|
+
module=module,
|
7777
|
+
)
|
7743
7778
|
|
7744
|
-
|
7745
|
-
|
7779
|
+
@logging_context_info
|
7780
|
+
@ta.final
|
7781
|
+
class Thread(ta.NamedTuple):
|
7782
|
+
ident: int
|
7783
|
+
native_id: ta.Optional[int]
|
7784
|
+
name: str
|
7746
7785
|
|
7786
|
+
@classmethod
|
7787
|
+
def build(cls) -> 'LoggingContextInfos.Thread':
|
7788
|
+
return cls(
|
7789
|
+
ident=threading.get_ident(),
|
7790
|
+
native_id=threading.get_native_id() if hasattr(threading, 'get_native_id') else None,
|
7791
|
+
name=threading.current_thread().name,
|
7792
|
+
)
|
7747
7793
|
|
7748
|
-
|
7794
|
+
@logging_context_info
|
7795
|
+
@ta.final
|
7796
|
+
class Process(ta.NamedTuple):
|
7797
|
+
pid: int
|
7749
7798
|
|
7799
|
+
@classmethod
|
7800
|
+
def build(cls) -> 'LoggingContextInfos.Process':
|
7801
|
+
return cls(
|
7802
|
+
pid=os.getpid(),
|
7803
|
+
)
|
7750
7804
|
|
7751
|
-
@logging_context_info
|
7752
|
-
@ta.final
|
7753
|
-
class
|
7754
|
-
|
7805
|
+
@logging_context_info
|
7806
|
+
@ta.final
|
7807
|
+
class Multiprocessing(ta.NamedTuple):
|
7808
|
+
process_name: str
|
7755
7809
|
|
7756
|
-
|
7757
|
-
|
7758
|
-
|
7810
|
+
@classmethod
|
7811
|
+
def build(cls) -> ta.Optional['LoggingContextInfos.Multiprocessing']:
|
7812
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L355-L364 # noqa
|
7813
|
+
if (mp := sys.modules.get('multiprocessing')) is None:
|
7814
|
+
return None
|
7759
7815
|
|
7760
|
-
|
7761
|
-
|
7762
|
-
|
7816
|
+
return cls(
|
7817
|
+
process_name=mp.current_process().name,
|
7818
|
+
)
|
7763
7819
|
|
7764
|
-
|
7765
|
-
|
7766
|
-
|
7767
|
-
|
7768
|
-
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
7769
|
-
#
|
7770
|
-
if isinstance(x, float):
|
7771
|
-
return int(x * 1e9)
|
7772
|
-
else:
|
7773
|
-
return x
|
7820
|
+
@logging_context_info
|
7821
|
+
@ta.final
|
7822
|
+
class AsyncioTask(ta.NamedTuple):
|
7823
|
+
name: str
|
7774
7824
|
|
7775
|
-
|
7776
|
-
|
7777
|
-
|
7778
|
-
|
7779
|
-
|
7780
|
-
start_time_ns: ta.Optional[int] = None,
|
7781
|
-
) -> 'LoggingTimeFields':
|
7782
|
-
# https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
7783
|
-
created = time_ns / 1e9 # ns to float seconds
|
7784
|
-
|
7785
|
-
# Get the number of whole milliseconds (0-999) in the fractional part of seconds.
|
7786
|
-
# Eg: 1_677_903_920_999_998_503 ns --> 999_998_503 ns--> 999 ms
|
7787
|
-
# Convert to float by adding 0.0 for historical reasons. See gh-89047
|
7788
|
-
msecs = (time_ns % 1_000_000_000) // 1_000_000 + 0.0
|
7789
|
-
|
7790
|
-
# https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
7791
|
-
if msecs == 999.0 and int(created) != time_ns // 1_000_000_000:
|
7792
|
-
# ns -> sec conversion can round up, e.g:
|
7793
|
-
# 1_677_903_920_999_999_900 ns --> 1_677_903_921.0 sec
|
7794
|
-
msecs = 0.0
|
7795
|
-
|
7796
|
-
if start_time_ns is None:
|
7797
|
-
start_time_ns = cls.get_std_start_time_ns()
|
7798
|
-
relative_created = (time_ns - start_time_ns) / 1e6
|
7825
|
+
@classmethod
|
7826
|
+
def build(cls) -> ta.Optional['LoggingContextInfos.AsyncioTask']:
|
7827
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L372-L377 # noqa
|
7828
|
+
if (asyncio := sys.modules.get('asyncio')) is None:
|
7829
|
+
return None
|
7799
7830
|
|
7800
|
-
|
7801
|
-
|
7802
|
-
|
7803
|
-
|
7804
|
-
|
7831
|
+
try:
|
7832
|
+
task = asyncio.current_task()
|
7833
|
+
except Exception: # noqa
|
7834
|
+
return None
|
7835
|
+
|
7836
|
+
if task is None:
|
7837
|
+
return None
|
7838
|
+
|
7839
|
+
return cls(
|
7840
|
+
name=task.get_name(), # Always non-None
|
7841
|
+
)
|
7805
7842
|
|
7806
7843
|
|
7807
7844
|
##
|
@@ -7812,12 +7849,12 @@ class UnexpectedLoggingStartTimeWarning(LoggingSetupWarning):
|
|
7812
7849
|
|
7813
7850
|
|
7814
7851
|
def _check_logging_start_time() -> None:
|
7815
|
-
if (x :=
|
7852
|
+
if (x := LoggingContextInfos.Time.get_std_start_ns()) < (t := time.time()):
|
7816
7853
|
import warnings # noqa
|
7817
7854
|
|
7818
7855
|
warnings.warn(
|
7819
7856
|
f'Unexpected logging start time detected: '
|
7820
|
-
f'
|
7857
|
+
f'get_std_start_ns={x}, '
|
7821
7858
|
f'time.time()={t}',
|
7822
7859
|
UnexpectedLoggingStartTimeWarning,
|
7823
7860
|
)
|
@@ -7826,6 +7863,95 @@ def _check_logging_start_time() -> None:
|
|
7826
7863
|
_check_logging_start_time()
|
7827
7864
|
|
7828
7865
|
|
7866
|
+
########################################
|
7867
|
+
# ../../../omlish/logs/protocols.py
|
7868
|
+
|
7869
|
+
|
7870
|
+
##
|
7871
|
+
|
7872
|
+
|
7873
|
+
@ta.runtime_checkable
|
7874
|
+
class LoggerLike(ta.Protocol):
|
7875
|
+
"""Satisfied by both our Logger and stdlib logging.Logger."""
|
7876
|
+
|
7877
|
+
def isEnabledFor(self, level: LogLevel) -> bool: ... # noqa
|
7878
|
+
|
7879
|
+
def getEffectiveLevel(self) -> LogLevel: ... # noqa
|
7880
|
+
|
7881
|
+
#
|
7882
|
+
|
7883
|
+
def log(self, level: LogLevel, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
7884
|
+
|
7885
|
+
def debug(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
7886
|
+
|
7887
|
+
def info(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
7888
|
+
|
7889
|
+
def warning(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
7890
|
+
|
7891
|
+
def error(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
7892
|
+
|
7893
|
+
def exception(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
7894
|
+
|
7895
|
+
def critical(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
7896
|
+
|
7897
|
+
|
7898
|
+
########################################
|
7899
|
+
# ../../../omlish/logs/std/json.py
|
7900
|
+
"""
|
7901
|
+
TODO:
|
7902
|
+
- translate json keys
|
7903
|
+
"""
|
7904
|
+
|
7905
|
+
|
7906
|
+
##
|
7907
|
+
|
7908
|
+
|
7909
|
+
class JsonLoggingFormatter(logging.Formatter):
|
7910
|
+
KEYS: ta.Mapping[str, bool] = {
|
7911
|
+
'name': False,
|
7912
|
+
'msg': False,
|
7913
|
+
'args': False,
|
7914
|
+
'levelname': False,
|
7915
|
+
'levelno': False,
|
7916
|
+
'pathname': False,
|
7917
|
+
'filename': False,
|
7918
|
+
'module': False,
|
7919
|
+
'exc_info': True,
|
7920
|
+
'exc_text': True,
|
7921
|
+
'stack_info': True,
|
7922
|
+
'lineno': False,
|
7923
|
+
'funcName': False,
|
7924
|
+
'created': False,
|
7925
|
+
'msecs': False,
|
7926
|
+
'relativeCreated': False,
|
7927
|
+
'thread': False,
|
7928
|
+
'threadName': False,
|
7929
|
+
'processName': False,
|
7930
|
+
'process': False,
|
7931
|
+
}
|
7932
|
+
|
7933
|
+
def __init__(
|
7934
|
+
self,
|
7935
|
+
*args: ta.Any,
|
7936
|
+
json_dumps: ta.Optional[ta.Callable[[ta.Any], str]] = None,
|
7937
|
+
**kwargs: ta.Any,
|
7938
|
+
) -> None:
|
7939
|
+
super().__init__(*args, **kwargs)
|
7940
|
+
|
7941
|
+
if json_dumps is None:
|
7942
|
+
json_dumps = json_dumps_compact
|
7943
|
+
self._json_dumps = json_dumps
|
7944
|
+
|
7945
|
+
def format(self, record: logging.LogRecord) -> str:
|
7946
|
+
dct = {
|
7947
|
+
k: v
|
7948
|
+
for k, o in self.KEYS.items()
|
7949
|
+
for v in [getattr(record, k)]
|
7950
|
+
if not (o and v is None)
|
7951
|
+
}
|
7952
|
+
return self._json_dumps(dct)
|
7953
|
+
|
7954
|
+
|
7829
7955
|
########################################
|
7830
7956
|
# ../../../omlish/os/atomics.py
|
7831
7957
|
|
@@ -9965,68 +10091,36 @@ inj = InjectionApi()
|
|
9965
10091
|
|
9966
10092
|
|
9967
10093
|
class LoggingContext(Abstract):
|
9968
|
-
@property
|
9969
10094
|
@abc.abstractmethod
|
9970
|
-
def
|
10095
|
+
def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
9971
10096
|
raise NotImplementedError
|
9972
10097
|
|
9973
|
-
|
9974
|
-
|
9975
|
-
|
9976
|
-
@abc.abstractmethod
|
9977
|
-
def time_ns(self) -> int:
|
9978
|
-
raise NotImplementedError
|
9979
|
-
|
9980
|
-
@property
|
9981
|
-
@abc.abstractmethod
|
9982
|
-
def times(self) -> LoggingTimeFields:
|
9983
|
-
raise NotImplementedError
|
9984
|
-
|
9985
|
-
#
|
10098
|
+
@ta.final
|
10099
|
+
def __getitem__(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
10100
|
+
return self.get_info(ty)
|
9986
10101
|
|
9987
|
-
@
|
9988
|
-
|
9989
|
-
|
9990
|
-
|
10102
|
+
@ta.final
|
10103
|
+
def must_get_info(self, ty: ta.Type[LoggingContextInfoT]) -> LoggingContextInfoT:
|
10104
|
+
if (info := self.get_info(ty)) is None:
|
10105
|
+
raise TypeError(f'LoggingContextInfo absent: {ty}')
|
10106
|
+
return info
|
9991
10107
|
|
9992
|
-
|
9993
|
-
@abc.abstractmethod
|
9994
|
-
def exc_info_tuple(self) -> ta.Optional[LoggingExcInfoTuple]:
|
9995
|
-
raise NotImplementedError
|
10108
|
+
##
|
9996
10109
|
|
9997
|
-
#
|
9998
10110
|
|
10111
|
+
class CaptureLoggingContext(LoggingContext, Abstract):
|
9999
10112
|
@abc.abstractmethod
|
10000
|
-
def
|
10001
|
-
|
10113
|
+
def set_basic(
|
10114
|
+
self,
|
10115
|
+
name: str,
|
10002
10116
|
|
10003
|
-
|
10004
|
-
|
10117
|
+
msg: ta.Union[str, tuple, LoggingMsgFn],
|
10118
|
+
args: tuple,
|
10119
|
+
) -> 'CaptureLoggingContext':
|
10005
10120
|
raise NotImplementedError
|
10006
10121
|
|
10007
10122
|
#
|
10008
10123
|
|
10009
|
-
@abc.abstractmethod
|
10010
|
-
def thread(self) -> ta.Optional[LoggingThreadInfo]:
|
10011
|
-
raise NotImplementedError
|
10012
|
-
|
10013
|
-
@abc.abstractmethod
|
10014
|
-
def process(self) -> ta.Optional[LoggingProcessInfo]:
|
10015
|
-
raise NotImplementedError
|
10016
|
-
|
10017
|
-
@abc.abstractmethod
|
10018
|
-
def multiprocessing(self) -> ta.Optional[LoggingMultiprocessingInfo]:
|
10019
|
-
raise NotImplementedError
|
10020
|
-
|
10021
|
-
@abc.abstractmethod
|
10022
|
-
def asyncio_task(self) -> ta.Optional[LoggingAsyncioTaskInfo]:
|
10023
|
-
raise NotImplementedError
|
10024
|
-
|
10025
|
-
|
10026
|
-
##
|
10027
|
-
|
10028
|
-
|
10029
|
-
class CaptureLoggingContext(LoggingContext, Abstract):
|
10030
10124
|
class AlreadyCapturedError(Exception):
|
10031
10125
|
pass
|
10032
10126
|
|
@@ -10057,80 +10151,50 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
|
|
10057
10151
|
|
10058
10152
|
exc_info: LoggingExcInfoArg = False,
|
10059
10153
|
|
10060
|
-
caller: ta.Union[
|
10154
|
+
caller: ta.Union[LoggingContextInfos.Caller, ta.Type[NOT_SET], None] = NOT_SET,
|
10061
10155
|
stack_offset: int = 0,
|
10062
10156
|
stack_info: bool = False,
|
10063
10157
|
) -> None:
|
10064
|
-
|
10065
|
-
|
10066
|
-
#
|
10158
|
+
# TODO: Name, Msg, Extra
|
10067
10159
|
|
10068
10160
|
if time_ns is None:
|
10069
10161
|
time_ns = time.time_ns()
|
10070
|
-
self._time_ns: int = time_ns
|
10071
10162
|
|
10072
|
-
|
10073
|
-
|
10074
|
-
|
10075
|
-
|
10076
|
-
|
10077
|
-
|
10078
|
-
else:
|
10079
|
-
exc_info = None
|
10080
|
-
elif exc_info is False:
|
10081
|
-
exc_info = None
|
10082
|
-
|
10083
|
-
if exc_info is not None:
|
10084
|
-
self._exc_info: ta.Optional[LoggingExcInfo] = exc_info
|
10085
|
-
if isinstance(exc_info, BaseException):
|
10086
|
-
self._exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = (type(exc_info), exc_info, exc_info.__traceback__) # noqa
|
10087
|
-
else:
|
10088
|
-
self._exc_info_tuple = exc_info
|
10089
|
-
|
10090
|
-
#
|
10163
|
+
self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {}
|
10164
|
+
self._set_info(
|
10165
|
+
LoggingContextInfos.Level.build(level),
|
10166
|
+
LoggingContextInfos.Time.build(time_ns),
|
10167
|
+
LoggingContextInfos.Exc.build(exc_info),
|
10168
|
+
)
|
10091
10169
|
|
10092
10170
|
if caller is not CaptureLoggingContextImpl.NOT_SET:
|
10093
|
-
self.
|
10171
|
+
self._infos[LoggingContextInfos.Caller] = caller
|
10094
10172
|
else:
|
10095
10173
|
self._stack_offset = stack_offset
|
10096
10174
|
self._stack_info = stack_info
|
10097
10175
|
|
10098
|
-
|
10099
|
-
|
10100
|
-
|
10101
|
-
|
10102
|
-
return self
|
10103
|
-
|
10104
|
-
#
|
10105
|
-
|
10106
|
-
@property
|
10107
|
-
def time_ns(self) -> int:
|
10108
|
-
return self._time_ns
|
10109
|
-
|
10110
|
-
_times: LoggingTimeFields
|
10111
|
-
|
10112
|
-
@property
|
10113
|
-
def times(self) -> LoggingTimeFields:
|
10114
|
-
try:
|
10115
|
-
return self._times
|
10116
|
-
except AttributeError:
|
10117
|
-
pass
|
10118
|
-
|
10119
|
-
times = self._times = LoggingTimeFields.build(self.time_ns)
|
10120
|
-
return times
|
10176
|
+
def _set_info(self, *infos: ta.Optional[LoggingContextInfo]) -> 'CaptureLoggingContextImpl':
|
10177
|
+
for info in infos:
|
10178
|
+
if info is not None:
|
10179
|
+
self._infos[type(info)] = info
|
10180
|
+
return self
|
10121
10181
|
|
10122
|
-
|
10182
|
+
def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
10183
|
+
return self._infos.get(ty)
|
10123
10184
|
|
10124
|
-
|
10125
|
-
_exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = None
|
10185
|
+
##
|
10126
10186
|
|
10127
|
-
|
10128
|
-
|
10129
|
-
|
10187
|
+
def set_basic(
|
10188
|
+
self,
|
10189
|
+
name: str,
|
10130
10190
|
|
10131
|
-
|
10132
|
-
|
10133
|
-
|
10191
|
+
msg: ta.Union[str, tuple, LoggingMsgFn],
|
10192
|
+
args: tuple,
|
10193
|
+
) -> 'CaptureLoggingContextImpl':
|
10194
|
+
return self._set_info(
|
10195
|
+
LoggingContextInfos.Name(name),
|
10196
|
+
LoggingContextInfos.Msg.build(msg, *args),
|
10197
|
+
)
|
10134
10198
|
|
10135
10199
|
##
|
10136
10200
|
|
@@ -10144,74 +10208,28 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
|
|
10144
10208
|
|
10145
10209
|
_has_captured: bool = False
|
10146
10210
|
|
10147
|
-
_caller: ta.Optional[LoggingCaller]
|
10148
|
-
_source_file: ta.Optional[LoggingSourceFileInfo]
|
10149
|
-
|
10150
|
-
_thread: ta.Optional[LoggingThreadInfo]
|
10151
|
-
_process: ta.Optional[LoggingProcessInfo]
|
10152
|
-
_multiprocessing: ta.Optional[LoggingMultiprocessingInfo]
|
10153
|
-
_asyncio_task: ta.Optional[LoggingAsyncioTaskInfo]
|
10154
|
-
|
10155
10211
|
def capture(self) -> None:
|
10156
10212
|
if self._has_captured:
|
10157
10213
|
raise CaptureLoggingContextImpl.AlreadyCapturedError
|
10158
10214
|
self._has_captured = True
|
10159
10215
|
|
10160
|
-
if not
|
10161
|
-
self.
|
10216
|
+
if LoggingContextInfos.Caller not in self._infos:
|
10217
|
+
self._set_info(LoggingContextInfos.Caller.build(
|
10162
10218
|
self._stack_offset + 1,
|
10163
10219
|
stack_info=self._stack_info,
|
10164
|
-
)
|
10165
|
-
|
10166
|
-
if (caller := self._caller) is not None:
|
10167
|
-
self._source_file = LoggingSourceFileInfo.build(caller.file_path)
|
10168
|
-
else:
|
10169
|
-
self._source_file = None
|
10170
|
-
|
10171
|
-
self._thread = LoggingThreadInfo.build()
|
10172
|
-
self._process = LoggingProcessInfo.build()
|
10173
|
-
self._multiprocessing = LoggingMultiprocessingInfo.build()
|
10174
|
-
self._asyncio_task = LoggingAsyncioTaskInfo.build()
|
10175
|
-
|
10176
|
-
#
|
10177
|
-
|
10178
|
-
def caller(self) -> ta.Optional[LoggingCaller]:
|
10179
|
-
try:
|
10180
|
-
return self._caller
|
10181
|
-
except AttributeError:
|
10182
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
10183
|
-
|
10184
|
-
def source_file(self) -> ta.Optional[LoggingSourceFileInfo]:
|
10185
|
-
try:
|
10186
|
-
return self._source_file
|
10187
|
-
except AttributeError:
|
10188
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
10189
|
-
|
10190
|
-
#
|
10191
|
-
|
10192
|
-
def thread(self) -> ta.Optional[LoggingThreadInfo]:
|
10193
|
-
try:
|
10194
|
-
return self._thread
|
10195
|
-
except AttributeError:
|
10196
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
10197
|
-
|
10198
|
-
def process(self) -> ta.Optional[LoggingProcessInfo]:
|
10199
|
-
try:
|
10200
|
-
return self._process
|
10201
|
-
except AttributeError:
|
10202
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
10220
|
+
))
|
10203
10221
|
|
10204
|
-
|
10205
|
-
|
10206
|
-
|
10207
|
-
|
10208
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
10222
|
+
if (caller := self[LoggingContextInfos.Caller]) is not None:
|
10223
|
+
self._set_info(LoggingContextInfos.SourceFile.build(
|
10224
|
+
caller.file_path,
|
10225
|
+
))
|
10209
10226
|
|
10210
|
-
|
10211
|
-
|
10212
|
-
|
10213
|
-
|
10214
|
-
|
10227
|
+
self._set_info(
|
10228
|
+
LoggingContextInfos.Thread.build(),
|
10229
|
+
LoggingContextInfos.Process.build(),
|
10230
|
+
LoggingContextInfos.Multiprocessing.build(),
|
10231
|
+
LoggingContextInfos.AsyncioTask.build(),
|
10232
|
+
)
|
10215
10233
|
|
10216
10234
|
|
10217
10235
|
########################################
|
@@ -10970,7 +10988,6 @@ class DeploySpec(DeploySpecKeyed[DeployKey]):
|
|
10970
10988
|
|
10971
10989
|
|
10972
10990
|
class AnyLogger(Abstract, ta.Generic[T]):
|
10973
|
-
@ta.final
|
10974
10991
|
def is_enabled_for(self, level: LogLevel) -> bool:
|
10975
10992
|
return level >= self.get_effective_level()
|
10976
10993
|
|
@@ -11116,36 +11133,6 @@ class AnyLogger(Abstract, ta.Generic[T]):
|
|
11116
11133
|
|
11117
11134
|
##
|
11118
11135
|
|
11119
|
-
@classmethod
|
11120
|
-
def _prepare_msg_args(cls, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> ta.Tuple[str, tuple]:
|
11121
|
-
if callable(msg):
|
11122
|
-
if args:
|
11123
|
-
raise TypeError(f'Must not provide both a message function and args: {msg=} {args=}')
|
11124
|
-
x = msg()
|
11125
|
-
if isinstance(x, str):
|
11126
|
-
return x, ()
|
11127
|
-
elif isinstance(x, tuple):
|
11128
|
-
if x:
|
11129
|
-
return x[0], x[1:]
|
11130
|
-
else:
|
11131
|
-
return '', ()
|
11132
|
-
else:
|
11133
|
-
raise TypeError(x)
|
11134
|
-
|
11135
|
-
elif isinstance(msg, tuple):
|
11136
|
-
if args:
|
11137
|
-
raise TypeError(f'Must not provide both a tuple message and args: {msg=} {args=}')
|
11138
|
-
if msg:
|
11139
|
-
return msg[0], msg[1:]
|
11140
|
-
else:
|
11141
|
-
return '', ()
|
11142
|
-
|
11143
|
-
elif isinstance(msg, str):
|
11144
|
-
return msg, args
|
11145
|
-
|
11146
|
-
else:
|
11147
|
-
raise TypeError(msg)
|
11148
|
-
|
11149
11136
|
@abc.abstractmethod
|
11150
11137
|
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> T: # noqa
|
11151
11138
|
raise NotImplementedError
|
@@ -11169,7 +11156,7 @@ class AsyncLogger(AnyLogger[ta.Awaitable[None]], Abstract):
|
|
11169
11156
|
class AnyNopLogger(AnyLogger[T], Abstract):
|
11170
11157
|
@ta.final
|
11171
11158
|
def get_effective_level(self) -> LogLevel:
|
11172
|
-
return 999
|
11159
|
+
return -999
|
11173
11160
|
|
11174
11161
|
|
11175
11162
|
@ta.final
|
@@ -11186,137 +11173,543 @@ class AsyncNopLogger(AnyNopLogger[ta.Awaitable[None]], AsyncLogger):
|
|
11186
11173
|
|
11187
11174
|
########################################
|
11188
11175
|
# ../../../omlish/logs/std/records.py
|
11176
|
+
"""
|
11177
|
+
TODO:
|
11178
|
+
- TypedDict?
|
11179
|
+
"""
|
11189
11180
|
|
11190
11181
|
|
11191
11182
|
##
|
11192
11183
|
|
11193
11184
|
|
11194
|
-
|
11195
|
-
#
|
11196
|
-
#
|
11197
|
-
#
|
11198
|
-
#
|
11199
|
-
# - https://github.com/python/cpython/blob/
|
11200
|
-
#
|
11201
|
-
#
|
11202
|
-
# - name: str
|
11203
|
-
# - level: int
|
11204
|
-
# - pathname: str - Confusingly referred to as `fn` before the LogRecord ctor. May be empty or "(unknown file)".
|
11205
|
-
# - lineno: int - May be 0.
|
11206
|
-
# - msg: str
|
11207
|
-
# - args: tuple | dict | 1-tuple[dict]
|
11208
|
-
# - exc_info: LoggingExcInfoTuple | None
|
11209
|
-
# - func: str | None = None -> funcName
|
11210
|
-
# - sinfo: str | None = None -> stack_info
|
11211
|
-
#
|
11212
|
-
KNOWN_STD_LOGGING_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
|
11213
|
-
# Name of the logger used to log the call. Unmodified by ctor.
|
11214
|
-
name=str,
|
11185
|
+
class LoggingContextInfoRecordAdapters:
|
11186
|
+
# Ref:
|
11187
|
+
# - https://docs.python.org/3/library/logging.html#logrecord-attributes
|
11188
|
+
#
|
11189
|
+
# LogRecord:
|
11190
|
+
# - https://github.com/python/cpython/blob/39b2f82717a69dde7212bc39b673b0f55c99e6a3/Lib/logging/__init__.py#L276 (3.8) # noqa
|
11191
|
+
# - https://github.com/python/cpython/blob/f070f54c5f4a42c7c61d1d5d3b8f3b7203b4a0fb/Lib/logging/__init__.py#L286 (~3.14) # noqa
|
11192
|
+
#
|
11215
11193
|
|
11216
|
-
|
11217
|
-
|
11218
|
-
msg=str,
|
11194
|
+
def __new__(cls, *args, **kwargs): # noqa
|
11195
|
+
raise TypeError
|
11219
11196
|
|
11220
|
-
|
11221
|
-
|
11222
|
-
|
11223
|
-
|
11197
|
+
class Adapter(Abstract, ta.Generic[T]):
|
11198
|
+
@property
|
11199
|
+
@abc.abstractmethod
|
11200
|
+
def info_cls(self) -> ta.Type[LoggingContextInfo]:
|
11201
|
+
raise NotImplementedError
|
11224
11202
|
|
11225
|
-
|
11203
|
+
#
|
11226
11204
|
|
11227
|
-
|
11228
|
-
|
11229
|
-
|
11205
|
+
@ta.final
|
11206
|
+
class NOT_SET: # noqa
|
11207
|
+
def __new__(cls, *args, **kwargs): # noqa
|
11208
|
+
raise TypeError
|
11230
11209
|
|
11231
|
-
|
11232
|
-
|
11210
|
+
class RecordAttr(ta.NamedTuple):
|
11211
|
+
name: str
|
11212
|
+
type: ta.Any
|
11213
|
+
default: ta.Any
|
11233
11214
|
|
11234
|
-
|
11215
|
+
# @abc.abstractmethod
|
11216
|
+
record_attrs: ta.ClassVar[ta.Mapping[str, RecordAttr]]
|
11235
11217
|
|
11236
|
-
|
11237
|
-
|
11238
|
-
|
11218
|
+
@property
|
11219
|
+
@abc.abstractmethod
|
11220
|
+
def _record_attrs(self) -> ta.Union[
|
11221
|
+
ta.Mapping[str, ta.Any],
|
11222
|
+
ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]],
|
11223
|
+
]:
|
11224
|
+
raise NotImplementedError
|
11239
11225
|
|
11240
|
-
|
11241
|
-
filename=str,
|
11226
|
+
#
|
11242
11227
|
|
11243
|
-
|
11244
|
-
|
11245
|
-
|
11228
|
+
@abc.abstractmethod
|
11229
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
11230
|
+
raise NotImplementedError
|
11246
11231
|
|
11247
|
-
|
11232
|
+
#
|
11248
11233
|
|
11249
|
-
|
11250
|
-
|
11234
|
+
@abc.abstractmethod
|
11235
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[T]:
|
11236
|
+
raise NotImplementedError
|
11251
11237
|
|
11252
|
-
|
11253
|
-
exc_text=ta.Optional[str],
|
11238
|
+
#
|
11254
11239
|
|
11255
|
-
|
11240
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
11241
|
+
super().__init_subclass__(**kwargs)
|
11256
11242
|
|
11257
|
-
|
11258
|
-
|
11259
|
-
# unmodified. Mostly set, if requested, by `Logger.findCaller`, to `traceback.print_stack(f)`, but prepended with
|
11260
|
-
# the literal "Stack (most recent call last):\n", and stripped of exactly one trailing `\n` if present.
|
11261
|
-
stack_info=ta.Optional[str],
|
11243
|
+
if Abstract in cls.__bases__:
|
11244
|
+
return
|
11262
11245
|
|
11263
|
-
|
11264
|
-
|
11265
|
-
|
11246
|
+
if 'record_attrs' in cls.__dict__:
|
11247
|
+
raise TypeError(cls)
|
11248
|
+
if not isinstance(ra := cls.__dict__['_record_attrs'], collections.abc.Mapping):
|
11249
|
+
raise TypeError(ra)
|
11250
|
+
|
11251
|
+
rd: ta.Dict[str, LoggingContextInfoRecordAdapters.Adapter.RecordAttr] = {}
|
11252
|
+
for n, v in ra.items():
|
11253
|
+
if not n or not isinstance(n, str) or n in rd:
|
11254
|
+
raise AttributeError(n)
|
11255
|
+
if isinstance(v, tuple):
|
11256
|
+
t, d = v
|
11257
|
+
else:
|
11258
|
+
t, d = v, cls.NOT_SET
|
11259
|
+
rd[n] = cls.RecordAttr(
|
11260
|
+
name=n,
|
11261
|
+
type=t,
|
11262
|
+
default=d,
|
11263
|
+
)
|
11264
|
+
cls.record_attrs = rd
|
11266
11265
|
|
11267
|
-
|
11268
|
-
|
11269
|
-
|
11266
|
+
class RequiredAdapter(Adapter[T], Abstract):
|
11267
|
+
@property
|
11268
|
+
@abc.abstractmethod
|
11269
|
+
def _record_attrs(self) -> ta.Mapping[str, ta.Any]:
|
11270
|
+
raise NotImplementedError
|
11270
11271
|
|
11271
|
-
|
11272
|
+
#
|
11272
11273
|
|
11273
|
-
|
11274
|
-
|
11275
|
-
|
11276
|
-
|
11277
|
-
|
11278
|
-
|
11279
|
-
|
11274
|
+
@ta.final
|
11275
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
11276
|
+
if (info := ctx.get_info(self.info_cls)) is not None:
|
11277
|
+
return self._info_to_record(info)
|
11278
|
+
else:
|
11279
|
+
raise TypeError # FIXME: fallback?
|
11280
|
+
|
11281
|
+
@abc.abstractmethod
|
11282
|
+
def _info_to_record(self, info: T) -> ta.Mapping[str, ta.Any]:
|
11283
|
+
raise NotImplementedError
|
11280
11284
|
|
11281
|
-
|
11282
|
-
msecs=float,
|
11285
|
+
#
|
11283
11286
|
|
11284
|
-
|
11285
|
-
|
11287
|
+
@abc.abstractmethod
|
11288
|
+
def record_to_info(self, rec: logging.LogRecord) -> T:
|
11289
|
+
raise NotImplementedError
|
11286
11290
|
|
11287
|
-
|
11291
|
+
#
|
11288
11292
|
|
11289
|
-
|
11290
|
-
|
11293
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
11294
|
+
super().__init_subclass__(**kwargs)
|
11291
11295
|
|
11292
|
-
|
11293
|
-
|
11296
|
+
if any(a.default is not cls.NOT_SET for a in cls.record_attrs.values()):
|
11297
|
+
raise TypeError(cls.record_attrs)
|
11294
11298
|
|
11295
|
-
|
11299
|
+
class OptionalAdapter(Adapter[T], Abstract, ta.Generic[T]):
|
11300
|
+
@property
|
11301
|
+
@abc.abstractmethod
|
11302
|
+
def _record_attrs(self) -> ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]:
|
11303
|
+
raise NotImplementedError
|
11304
|
+
|
11305
|
+
record_defaults: ta.ClassVar[ta.Mapping[str, ta.Any]]
|
11306
|
+
|
11307
|
+
#
|
11308
|
+
|
11309
|
+
@ta.final
|
11310
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
11311
|
+
if (info := ctx.get_info(self.info_cls)) is not None:
|
11312
|
+
return self._info_to_record(info)
|
11313
|
+
else:
|
11314
|
+
return self.record_defaults
|
11315
|
+
|
11316
|
+
@abc.abstractmethod
|
11317
|
+
def _info_to_record(self, info: T) -> ta.Mapping[str, ta.Any]:
|
11318
|
+
raise NotImplementedError
|
11319
|
+
|
11320
|
+
#
|
11321
|
+
|
11322
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
11323
|
+
super().__init_subclass__(**kwargs)
|
11324
|
+
|
11325
|
+
dd: ta.Dict[str, ta.Any] = {a.name: a.default for a in cls.record_attrs.values()}
|
11326
|
+
if any(d is cls.NOT_SET for d in dd.values()):
|
11327
|
+
raise TypeError(cls.record_attrs)
|
11328
|
+
cls.record_defaults = dd
|
11296
11329
|
|
11297
|
-
# Process name if available. Set to None if `logging.logMultiprocessing` is not truthy. Otherwise, set to
|
11298
|
-
# 'MainProcess', then `sys.modules.get('multiprocessing').current_process().name` if that works, otherwise remains
|
11299
|
-
# as 'MainProcess'.
|
11300
11330
|
#
|
11301
|
-
|
11331
|
+
|
11332
|
+
class Name(RequiredAdapter[LoggingContextInfos.Name]):
|
11333
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Name]] = LoggingContextInfos.Name
|
11334
|
+
|
11335
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
11336
|
+
# Name of the logger used to log the call. Unmodified by ctor.
|
11337
|
+
name=str,
|
11338
|
+
)
|
11339
|
+
|
11340
|
+
def _info_to_record(self, info: LoggingContextInfos.Name) -> ta.Mapping[str, ta.Any]:
|
11341
|
+
return dict(
|
11342
|
+
name=info.name,
|
11343
|
+
)
|
11344
|
+
|
11345
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Name:
|
11346
|
+
return LoggingContextInfos.Name(
|
11347
|
+
name=rec.name,
|
11348
|
+
)
|
11349
|
+
|
11350
|
+
class Level(RequiredAdapter[LoggingContextInfos.Level]):
|
11351
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Level]] = LoggingContextInfos.Level
|
11352
|
+
|
11353
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
11354
|
+
# Text logging level for the message ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). Set to
|
11355
|
+
# `getLevelName(level)`.
|
11356
|
+
levelname=str,
|
11357
|
+
|
11358
|
+
# Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL). Unmodified by ctor.
|
11359
|
+
levelno=int,
|
11360
|
+
)
|
11361
|
+
|
11362
|
+
def _info_to_record(self, info: LoggingContextInfos.Level) -> ta.Mapping[str, ta.Any]:
|
11363
|
+
return dict(
|
11364
|
+
levelname=info.name,
|
11365
|
+
levelno=int(info.level),
|
11366
|
+
)
|
11367
|
+
|
11368
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Level:
|
11369
|
+
return LoggingContextInfos.Level.build(rec.levelno)
|
11370
|
+
|
11371
|
+
class Msg(RequiredAdapter[LoggingContextInfos.Msg]):
|
11372
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Msg]] = LoggingContextInfos.Msg
|
11373
|
+
|
11374
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
11375
|
+
# The format string passed in the original logging call. Merged with args to produce message, or an
|
11376
|
+
# arbitrary object (see Using arbitrary objects as messages). Unmodified by ctor.
|
11377
|
+
msg=str,
|
11378
|
+
|
11379
|
+
# The tuple of arguments merged into msg to produce message, or a dict whose values are used for the merge
|
11380
|
+
# (when there is only one argument, and it is a dictionary). Ctor will transform a 1-tuple containing a
|
11381
|
+
# Mapping into just the mapping, but is otherwise unmodified.
|
11382
|
+
args=ta.Union[tuple, dict, None],
|
11383
|
+
)
|
11384
|
+
|
11385
|
+
def _info_to_record(self, info: LoggingContextInfos.Msg) -> ta.Mapping[str, ta.Any]:
|
11386
|
+
return dict(
|
11387
|
+
msg=info.msg,
|
11388
|
+
args=info.args,
|
11389
|
+
)
|
11390
|
+
|
11391
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Msg:
|
11392
|
+
return LoggingContextInfos.Msg(
|
11393
|
+
msg=rec.msg,
|
11394
|
+
args=rec.args,
|
11395
|
+
)
|
11396
|
+
|
11397
|
+
# FIXME: handled specially - all unknown attrs on LogRecord
|
11398
|
+
# class Extra(Adapter[LoggingContextInfos.Extra]):
|
11399
|
+
# _record_attrs: ta.ClassVar[ta.Mapping[str, ta.Union[ta.Any, ta.Tuple[ta.Any, ta.Any]]]] = dict()
|
11302
11400
|
#
|
11303
|
-
#
|
11304
|
-
#
|
11401
|
+
# def info_to_record(self, info: ta.Optional[LoggingContextInfos.Extra]) -> ta.Mapping[str, ta.Any]:
|
11402
|
+
# # FIXME:
|
11403
|
+
# # if extra is not None:
|
11404
|
+
# # for key in extra:
|
11405
|
+
# # if (key in ["message", "asctime"]) or (key in rv.__dict__):
|
11406
|
+
# # raise KeyError("Attempt to overwrite %r in LogRecord" % key)
|
11407
|
+
# # rv.__dict__[key] = extra[key]
|
11408
|
+
# return dict()
|
11305
11409
|
#
|
11306
|
-
|
11410
|
+
# def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Extra]:
|
11411
|
+
# return None
|
11307
11412
|
|
11308
|
-
|
11309
|
-
|
11310
|
-
process=ta.Optional[int],
|
11413
|
+
class Time(RequiredAdapter[LoggingContextInfos.Time]):
|
11414
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Time]] = LoggingContextInfos.Time
|
11311
11415
|
|
11312
|
-
|
11416
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
11417
|
+
# Time when the LogRecord was created. Set to `time.time_ns() / 1e9` for >=3.13.0b1, otherwise simply
|
11418
|
+
# `time.time()`.
|
11419
|
+
#
|
11420
|
+
# See:
|
11421
|
+
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
11422
|
+
# - https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
11423
|
+
#
|
11424
|
+
created=float,
|
11313
11425
|
|
11314
|
-
|
11315
|
-
|
11316
|
-
|
11317
|
-
|
11426
|
+
# Millisecond portion of the time when the LogRecord was created.
|
11427
|
+
msecs=float,
|
11428
|
+
|
11429
|
+
# Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.
|
11430
|
+
relativeCreated=float,
|
11431
|
+
)
|
11432
|
+
|
11433
|
+
def _info_to_record(self, info: LoggingContextInfos.Time) -> ta.Mapping[str, ta.Any]:
|
11434
|
+
return dict(
|
11435
|
+
created=info.secs,
|
11436
|
+
msecs=info.msecs,
|
11437
|
+
relativeCreated=info.relative_secs,
|
11438
|
+
)
|
11439
|
+
|
11440
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Time:
|
11441
|
+
return LoggingContextInfos.Time.build(
|
11442
|
+
int(rec.created * 1e9),
|
11443
|
+
)
|
11444
|
+
|
11445
|
+
class Exc(OptionalAdapter[LoggingContextInfos.Exc]):
|
11446
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Exc]] = LoggingContextInfos.Exc
|
11447
|
+
|
11448
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
11449
|
+
# Exception tuple (à la sys.exc_info) or, if no exception has occurred, None. Unmodified by ctor.
|
11450
|
+
exc_info=(ta.Optional[LoggingExcInfoTuple], None),
|
11451
|
+
|
11452
|
+
# Used to cache the traceback text. Simply set to None by ctor, later set by Formatter.format.
|
11453
|
+
exc_text=(ta.Optional[str], None),
|
11454
|
+
)
|
11455
|
+
|
11456
|
+
def _info_to_record(self, info: LoggingContextInfos.Exc) -> ta.Mapping[str, ta.Any]:
|
11457
|
+
return dict(
|
11458
|
+
exc_info=info.info_tuple,
|
11459
|
+
exc_text=None,
|
11460
|
+
)
|
11461
|
+
|
11462
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Exc]:
|
11463
|
+
# FIXME:
|
11464
|
+
# error: Argument 1 to "build" of "Exc" has incompatible type
|
11465
|
+
# "tuple[type[BaseException], BaseException, TracebackType | None] | tuple[None, None, None] | None"; expected # noqa
|
11466
|
+
# "BaseException | tuple[type[BaseException], BaseException, TracebackType | None] | bool | None" [arg-type] # noqa
|
11467
|
+
return LoggingContextInfos.Exc.build(rec.exc_info) # type: ignore[arg-type]
|
11468
|
+
|
11469
|
+
class Caller(OptionalAdapter[LoggingContextInfos.Caller]):
|
11470
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Caller]] = LoggingContextInfos.Caller
|
11471
|
+
|
11472
|
+
_UNKNOWN_PATH_NAME: ta.ClassVar[str] = '(unknown file)'
|
11473
|
+
_UNKNOWN_FUNC_NAME: ta.ClassVar[str] = '(unknown function)'
|
11474
|
+
|
11475
|
+
_STACK_INFO_PREFIX: ta.ClassVar[str] = 'Stack (most recent call last):\n'
|
11318
11476
|
|
11319
|
-
|
11477
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
11478
|
+
# Full pathname of the source file where the logging call was issued (if available). Unmodified by ctor. May
|
11479
|
+
# default to "(unknown file)" by Logger.findCaller / Logger._log.
|
11480
|
+
pathname=(str, _UNKNOWN_PATH_NAME),
|
11481
|
+
|
11482
|
+
# Source line number where the logging call was issued (if available). Unmodified by ctor. May default to 0
|
11483
|
+
# y Logger.findCaller / Logger._log.
|
11484
|
+
lineno=(int, 0),
|
11485
|
+
|
11486
|
+
# Name of function containing the logging call. Set by ctor to `func` arg, unmodified. May default to
|
11487
|
+
# "(unknown function)" by Logger.findCaller / Logger._log.
|
11488
|
+
funcName=(str, _UNKNOWN_FUNC_NAME),
|
11489
|
+
|
11490
|
+
# Stack frame information (where available) from the bottom of the stack in the current thread, up to and
|
11491
|
+
# including the stack frame of the logging call which resulted in the creation of this record. Set by ctor
|
11492
|
+
# to `sinfo` arg, unmodified. Mostly set, if requested, by `Logger.findCaller`, to
|
11493
|
+
# `traceback.print_stack(f)`, but prepended with the literal "Stack (most recent call last):\n", and
|
11494
|
+
# stripped of exactly one trailing `\n` if present.
|
11495
|
+
stack_info=(ta.Optional[str], None),
|
11496
|
+
)
|
11497
|
+
|
11498
|
+
def _info_to_record(self, caller: LoggingContextInfos.Caller) -> ta.Mapping[str, ta.Any]:
|
11499
|
+
if (sinfo := caller.stack_info) is not None:
|
11500
|
+
stack_info: ta.Optional[str] = '\n'.join([
|
11501
|
+
self._STACK_INFO_PREFIX,
|
11502
|
+
sinfo[1:] if sinfo.endswith('\n') else sinfo,
|
11503
|
+
])
|
11504
|
+
else:
|
11505
|
+
stack_info = None
|
11506
|
+
|
11507
|
+
return dict(
|
11508
|
+
pathname=caller.file_path,
|
11509
|
+
|
11510
|
+
lineno=caller.line_no,
|
11511
|
+
funcName=caller.func_name,
|
11512
|
+
|
11513
|
+
stack_info=stack_info,
|
11514
|
+
)
|
11515
|
+
|
11516
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Caller]:
|
11517
|
+
# FIXME: piecemeal?
|
11518
|
+
# FIXME: strip _STACK_INFO_PREFIX
|
11519
|
+
raise NotImplementedError
|
11520
|
+
|
11521
|
+
class SourceFile(Adapter[LoggingContextInfos.SourceFile]):
|
11522
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.SourceFile]] = LoggingContextInfos.SourceFile
|
11523
|
+
|
11524
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
11525
|
+
# Filename portion of pathname. Set to `os.path.basename(pathname)` if successful, otherwise defaults to
|
11526
|
+
# pathname.
|
11527
|
+
filename=str,
|
11528
|
+
|
11529
|
+
# Module (name portion of filename). Set to `os.path.splitext(filename)[0]`, otherwise defaults to
|
11530
|
+
# "Unknown module".
|
11531
|
+
module=str,
|
11532
|
+
)
|
11533
|
+
|
11534
|
+
_UNKNOWN_MODULE: ta.ClassVar[str] = 'Unknown module'
|
11535
|
+
|
11536
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
11537
|
+
if (info := ctx.get_info(LoggingContextInfos.SourceFile)) is not None:
|
11538
|
+
return dict(
|
11539
|
+
filename=info.file_name,
|
11540
|
+
module=info.module,
|
11541
|
+
)
|
11542
|
+
|
11543
|
+
if (caller := ctx.get_info(LoggingContextInfos.Caller)) is not None:
|
11544
|
+
return dict(
|
11545
|
+
filename=caller.file_path,
|
11546
|
+
module=self._UNKNOWN_MODULE,
|
11547
|
+
)
|
11548
|
+
|
11549
|
+
return dict(
|
11550
|
+
filename=LoggingContextInfoRecordAdapters.Caller._UNKNOWN_PATH_NAME, # noqa
|
11551
|
+
module=self._UNKNOWN_MODULE,
|
11552
|
+
)
|
11553
|
+
|
11554
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.SourceFile]:
|
11555
|
+
if not (
|
11556
|
+
rec.module is None or
|
11557
|
+
rec.module == self._UNKNOWN_MODULE
|
11558
|
+
):
|
11559
|
+
return LoggingContextInfos.SourceFile(
|
11560
|
+
file_name=rec.filename,
|
11561
|
+
module=rec.module, # FIXME: piecemeal?
|
11562
|
+
)
|
11563
|
+
|
11564
|
+
return None
|
11565
|
+
|
11566
|
+
class Thread(OptionalAdapter[LoggingContextInfos.Thread]):
|
11567
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Thread]] = LoggingContextInfos.Thread
|
11568
|
+
|
11569
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
11570
|
+
# Thread ID if available, and `logging.logThreads` is truthy.
|
11571
|
+
thread=(ta.Optional[int], None),
|
11572
|
+
|
11573
|
+
# Thread name if available, and `logging.logThreads` is truthy.
|
11574
|
+
threadName=(ta.Optional[str], None),
|
11575
|
+
)
|
11576
|
+
|
11577
|
+
def _info_to_record(self, info: LoggingContextInfos.Thread) -> ta.Mapping[str, ta.Any]:
|
11578
|
+
if logging.logThreads:
|
11579
|
+
return dict(
|
11580
|
+
thread=info.ident,
|
11581
|
+
threadName=info.name,
|
11582
|
+
)
|
11583
|
+
|
11584
|
+
return self.record_defaults
|
11585
|
+
|
11586
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Thread]:
|
11587
|
+
if (
|
11588
|
+
(ident := rec.thread) is not None and
|
11589
|
+
(name := rec.threadName) is not None
|
11590
|
+
):
|
11591
|
+
return LoggingContextInfos.Thread(
|
11592
|
+
ident=ident,
|
11593
|
+
native_id=None,
|
11594
|
+
name=name,
|
11595
|
+
)
|
11596
|
+
|
11597
|
+
return None
|
11598
|
+
|
11599
|
+
class Process(OptionalAdapter[LoggingContextInfos.Process]):
|
11600
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Process]] = LoggingContextInfos.Process
|
11601
|
+
|
11602
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
11603
|
+
# Process ID if available - that is, if `hasattr(os, 'getpid')` - and `logging.logProcesses` is truthy,
|
11604
|
+
# otherwise None.
|
11605
|
+
process=(ta.Optional[int], None),
|
11606
|
+
)
|
11607
|
+
|
11608
|
+
def _info_to_record(self, info: LoggingContextInfos.Process) -> ta.Mapping[str, ta.Any]:
|
11609
|
+
if logging.logProcesses:
|
11610
|
+
return dict(
|
11611
|
+
process=info.pid,
|
11612
|
+
)
|
11613
|
+
|
11614
|
+
return self.record_defaults
|
11615
|
+
|
11616
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Process]:
|
11617
|
+
if (
|
11618
|
+
(pid := rec.process) is not None
|
11619
|
+
):
|
11620
|
+
return LoggingContextInfos.Process(
|
11621
|
+
pid=pid,
|
11622
|
+
)
|
11623
|
+
|
11624
|
+
return None
|
11625
|
+
|
11626
|
+
class Multiprocessing(OptionalAdapter[LoggingContextInfos.Multiprocessing]):
|
11627
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Multiprocessing]] = LoggingContextInfos.Multiprocessing
|
11628
|
+
|
11629
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
11630
|
+
# Process name if available. Set to None if `logging.logMultiprocessing` is not truthy. Otherwise, set to
|
11631
|
+
# 'MainProcess', then `sys.modules.get('multiprocessing').current_process().name` if that works, otherwise
|
11632
|
+
# remains as 'MainProcess'.
|
11633
|
+
#
|
11634
|
+
# As noted by stdlib:
|
11635
|
+
#
|
11636
|
+
# Errors may occur if multiprocessing has not finished loading yet - e.g. if a custom import hook causes
|
11637
|
+
# third-party code to run when multiprocessing calls import. See issue 8200 for an example
|
11638
|
+
#
|
11639
|
+
processName=(ta.Optional[str], None),
|
11640
|
+
)
|
11641
|
+
|
11642
|
+
def _info_to_record(self, info: LoggingContextInfos.Multiprocessing) -> ta.Mapping[str, ta.Any]:
|
11643
|
+
if logging.logMultiprocessing:
|
11644
|
+
return dict(
|
11645
|
+
processName=info.process_name,
|
11646
|
+
)
|
11647
|
+
|
11648
|
+
return self.record_defaults
|
11649
|
+
|
11650
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Multiprocessing]:
|
11651
|
+
if (
|
11652
|
+
(process_name := rec.processName) is not None
|
11653
|
+
):
|
11654
|
+
return LoggingContextInfos.Multiprocessing(
|
11655
|
+
process_name=process_name,
|
11656
|
+
)
|
11657
|
+
|
11658
|
+
return None
|
11659
|
+
|
11660
|
+
class AsyncioTask(OptionalAdapter[LoggingContextInfos.AsyncioTask]):
|
11661
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.AsyncioTask]] = LoggingContextInfos.AsyncioTask
|
11662
|
+
|
11663
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Union[ta.Any, ta.Tuple[ta.Any, ta.Any]]]] = dict(
|
11664
|
+
# Absent <3.12, otherwise asyncio.Task name if available, and `logging.logAsyncioTasks` is truthy. Set to
|
11665
|
+
# `sys.modules.get('asyncio').current_task().get_name()`, otherwise None.
|
11666
|
+
taskName=(ta.Optional[str], None),
|
11667
|
+
)
|
11668
|
+
|
11669
|
+
def _info_to_record(self, info: LoggingContextInfos.AsyncioTask) -> ta.Mapping[str, ta.Any]:
|
11670
|
+
if getattr(logging, 'logAsyncioTasks', None): # Absent <3.12
|
11671
|
+
return dict(
|
11672
|
+
taskName=info.name,
|
11673
|
+
)
|
11674
|
+
|
11675
|
+
return self.record_defaults
|
11676
|
+
|
11677
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.AsyncioTask]:
|
11678
|
+
if (
|
11679
|
+
(name := getattr(rec, 'taskName', None)) is not None
|
11680
|
+
):
|
11681
|
+
return LoggingContextInfos.AsyncioTask(
|
11682
|
+
name=name,
|
11683
|
+
)
|
11684
|
+
|
11685
|
+
return None
|
11686
|
+
|
11687
|
+
|
11688
|
+
_LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_: ta.Sequence[LoggingContextInfoRecordAdapters.Adapter] = [ # noqa
|
11689
|
+
LoggingContextInfoRecordAdapters.Name(),
|
11690
|
+
LoggingContextInfoRecordAdapters.Level(),
|
11691
|
+
LoggingContextInfoRecordAdapters.Msg(),
|
11692
|
+
LoggingContextInfoRecordAdapters.Time(),
|
11693
|
+
LoggingContextInfoRecordAdapters.Exc(),
|
11694
|
+
LoggingContextInfoRecordAdapters.Caller(),
|
11695
|
+
LoggingContextInfoRecordAdapters.SourceFile(),
|
11696
|
+
LoggingContextInfoRecordAdapters.Thread(),
|
11697
|
+
LoggingContextInfoRecordAdapters.Process(),
|
11698
|
+
LoggingContextInfoRecordAdapters.Multiprocessing(),
|
11699
|
+
LoggingContextInfoRecordAdapters.AsyncioTask(),
|
11700
|
+
]
|
11701
|
+
|
11702
|
+
_LOGGING_CONTEXT_INFO_RECORD_ADAPTERS: ta.Mapping[ta.Type[LoggingContextInfo], LoggingContextInfoRecordAdapters.Adapter] = { # noqa
|
11703
|
+
ad.info_cls: ad for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_
|
11704
|
+
}
|
11705
|
+
|
11706
|
+
|
11707
|
+
##
|
11708
|
+
|
11709
|
+
|
11710
|
+
KNOWN_STD_LOGGING_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(
|
11711
|
+
a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs
|
11712
|
+
)
|
11320
11713
|
|
11321
11714
|
|
11322
11715
|
# Formatter:
|
@@ -11340,14 +11733,17 @@ KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
|
|
11340
11733
|
KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
|
11341
11734
|
|
11342
11735
|
|
11343
|
-
##
|
11344
|
-
|
11345
|
-
|
11346
11736
|
class UnknownStdLoggingRecordAttrsWarning(LoggingSetupWarning):
|
11347
11737
|
pass
|
11348
11738
|
|
11349
11739
|
|
11350
11740
|
def _check_std_logging_record_attrs() -> None:
|
11741
|
+
if (
|
11742
|
+
len([a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs]) !=
|
11743
|
+
len(KNOWN_STD_LOGGING_RECORD_ATTR_SET)
|
11744
|
+
):
|
11745
|
+
raise RuntimeError('Duplicate LoggingContextInfoRecordAdapter record attrs')
|
11746
|
+
|
11351
11747
|
rec_dct = dict(logging.makeLogRecord({}).__dict__)
|
11352
11748
|
|
11353
11749
|
if (unk_rec_fields := frozenset(rec_dct) - KNOWN_STD_LOGGING_RECORD_ATTR_SET):
|
@@ -11366,116 +11762,20 @@ _check_std_logging_record_attrs()
|
|
11366
11762
|
|
11367
11763
|
|
11368
11764
|
class LoggingContextLogRecord(logging.LogRecord):
|
11369
|
-
|
11370
|
-
|
11371
|
-
|
11372
|
-
|
11373
|
-
|
11374
|
-
|
11375
|
-
|
11376
|
-
|
11377
|
-
|
11378
|
-
|
11379
|
-
|
11380
|
-
|
11381
|
-
|
11382
|
-
|
11383
|
-
# msg,
|
11384
|
-
# args,
|
11385
|
-
# exc_info,
|
11386
|
-
# func=None,
|
11387
|
-
# sinfo=None,
|
11388
|
-
# **kwargs,
|
11389
|
-
*,
|
11390
|
-
name: str,
|
11391
|
-
msg: str,
|
11392
|
-
args: ta.Union[tuple, dict],
|
11393
|
-
|
11394
|
-
_logging_context: LoggingContext,
|
11395
|
-
) -> None:
|
11396
|
-
ctx = _logging_context
|
11397
|
-
|
11398
|
-
self.name: str = name
|
11399
|
-
|
11400
|
-
self.msg: str = msg
|
11401
|
-
|
11402
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L307
|
11403
|
-
if args and len(args) == 1 and isinstance(args[0], collections.abc.Mapping) and args[0]:
|
11404
|
-
args = args[0] # type: ignore[assignment]
|
11405
|
-
self.args: ta.Union[tuple, dict] = args
|
11406
|
-
|
11407
|
-
self.levelname: str = logging.getLevelName(ctx.level)
|
11408
|
-
self.levelno: int = ctx.level
|
11409
|
-
|
11410
|
-
if (caller := ctx.caller()) is not None:
|
11411
|
-
self.pathname: str = caller.file_path
|
11412
|
-
else:
|
11413
|
-
self.pathname = self._UNKNOWN_PATH_NAME
|
11414
|
-
|
11415
|
-
if (src_file := ctx.source_file()) is not None:
|
11416
|
-
self.filename: str = src_file.file_name
|
11417
|
-
self.module: str = src_file.module
|
11418
|
-
else:
|
11419
|
-
self.filename = self.pathname
|
11420
|
-
self.module = self._UNKNOWN_MODULE
|
11421
|
-
|
11422
|
-
self.exc_info: ta.Optional[LoggingExcInfoTuple] = ctx.exc_info_tuple
|
11423
|
-
self.exc_text: ta.Optional[str] = None
|
11424
|
-
|
11425
|
-
# If ctx.build_caller() was never called, we simply don't have a stack trace.
|
11426
|
-
if caller is not None:
|
11427
|
-
if (sinfo := caller.stack_info) is not None:
|
11428
|
-
self.stack_info: ta.Optional[str] = '\n'.join([
|
11429
|
-
self._STACK_INFO_PREFIX,
|
11430
|
-
sinfo[1:] if sinfo.endswith('\n') else sinfo,
|
11431
|
-
])
|
11432
|
-
else:
|
11433
|
-
self.stack_info = None
|
11434
|
-
|
11435
|
-
self.lineno: int = caller.line_no
|
11436
|
-
self.funcName: str = caller.name
|
11437
|
-
|
11438
|
-
else:
|
11439
|
-
self.stack_info = None
|
11440
|
-
|
11441
|
-
self.lineno = 0
|
11442
|
-
self.funcName = self._UNKNOWN_FUNC_NAME
|
11443
|
-
|
11444
|
-
times = ctx.times
|
11445
|
-
self.created: float = times.created
|
11446
|
-
self.msecs: float = times.msecs
|
11447
|
-
self.relativeCreated: float = times.relative_created
|
11448
|
-
|
11449
|
-
if logging.logThreads:
|
11450
|
-
thread = check.not_none(ctx.thread())
|
11451
|
-
self.thread: ta.Optional[int] = thread.ident
|
11452
|
-
self.threadName: ta.Optional[str] = thread.name
|
11453
|
-
else:
|
11454
|
-
self.thread = None
|
11455
|
-
self.threadName = None
|
11456
|
-
|
11457
|
-
if logging.logProcesses:
|
11458
|
-
process = check.not_none(ctx.process())
|
11459
|
-
self.process: ta.Optional[int] = process.pid
|
11460
|
-
else:
|
11461
|
-
self.process = None
|
11462
|
-
|
11463
|
-
if logging.logMultiprocessing:
|
11464
|
-
if (mp := ctx.multiprocessing()) is not None:
|
11465
|
-
self.processName: ta.Optional[str] = mp.process_name
|
11466
|
-
else:
|
11467
|
-
self.processName = None
|
11468
|
-
else:
|
11469
|
-
self.processName = None
|
11470
|
-
|
11471
|
-
# Absent <3.12
|
11472
|
-
if getattr(logging, 'logAsyncioTasks', None):
|
11473
|
-
if (at := ctx.asyncio_task()) is not None:
|
11474
|
-
self.taskName: ta.Optional[str] = at.name
|
11475
|
-
else:
|
11476
|
-
self.taskName = None
|
11477
|
-
else:
|
11478
|
-
self.taskName = None
|
11765
|
+
# LogRecord.__init__ args:
|
11766
|
+
# - name: str
|
11767
|
+
# - level: int
|
11768
|
+
# - pathname: str - Confusingly referred to as `fn` before the LogRecord ctor. May be empty or "(unknown file)".
|
11769
|
+
# - lineno: int - May be 0.
|
11770
|
+
# - msg: str
|
11771
|
+
# - args: tuple | dict | 1-tuple[dict]
|
11772
|
+
# - exc_info: LoggingExcInfoTuple | None
|
11773
|
+
# - func: str | None = None -> funcName
|
11774
|
+
# - sinfo: str | None = None -> stack_info
|
11775
|
+
|
11776
|
+
def __init__(self, *, _logging_context: LoggingContext) -> None: # noqa
|
11777
|
+
for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_:
|
11778
|
+
self.__dict__.update(ad.context_to_record(_logging_context))
|
11479
11779
|
|
11480
11780
|
|
11481
11781
|
########################################
|
@@ -12091,7 +12391,7 @@ class SingleDirDeployPathOwner(DeployPathOwner, Abstract):
|
|
12091
12391
|
|
12092
12392
|
|
12093
12393
|
########################################
|
12094
|
-
# ../../../omlish/logs/std/
|
12394
|
+
# ../../../omlish/logs/std/loggers.py
|
12095
12395
|
|
12096
12396
|
|
12097
12397
|
##
|
@@ -12107,25 +12407,27 @@ class StdLogger(Logger):
|
|
12107
12407
|
def std(self) -> logging.Logger:
|
12108
12408
|
return self._std
|
12109
12409
|
|
12410
|
+
def is_enabled_for(self, level: LogLevel) -> bool:
|
12411
|
+
return self._std.isEnabledFor(level)
|
12412
|
+
|
12110
12413
|
def get_effective_level(self) -> LogLevel:
|
12111
12414
|
return self._std.getEffectiveLevel()
|
12112
12415
|
|
12113
12416
|
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> None:
|
12114
|
-
if not self.is_enabled_for(ctx.level):
|
12417
|
+
if not self.is_enabled_for(ctx.must_get_info(LoggingContextInfos.Level).level):
|
12115
12418
|
return
|
12116
12419
|
|
12117
|
-
ctx.
|
12118
|
-
|
12119
|
-
ms, args = self._prepare_msg_args(msg, *args)
|
12120
|
-
|
12121
|
-
rec = LoggingContextLogRecord(
|
12420
|
+
ctx.set_basic(
|
12122
12421
|
name=self._std.name,
|
12123
|
-
msg=ms,
|
12124
|
-
args=args,
|
12125
12422
|
|
12126
|
-
|
12423
|
+
msg=msg,
|
12424
|
+
args=args,
|
12127
12425
|
)
|
12128
12426
|
|
12427
|
+
ctx.capture()
|
12428
|
+
|
12429
|
+
rec = LoggingContextLogRecord(_logging_context=ctx)
|
12430
|
+
|
12129
12431
|
self._std.handle(rec)
|
12130
12432
|
|
12131
12433
|
|