ominfra 0.0.0.dev430__py3-none-any.whl → 0.0.0.dev432__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 +1007 -691
- ominfra/scripts/manage.py +995 -679
- ominfra/scripts/supervisor.py +993 -677
- {ominfra-0.0.0.dev430.dist-info → ominfra-0.0.0.dev432.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev430.dist-info → ominfra-0.0.0.dev432.dist-info}/RECORD +9 -9
- {ominfra-0.0.0.dev430.dist-info → ominfra-0.0.0.dev432.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev430.dist-info → ominfra-0.0.0.dev432.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev430.dist-info → ominfra-0.0.0.dev432.dist-info}/licenses/LICENSE +0 -0
- {ominfra-0.0.0.dev430.dist-info → ominfra-0.0.0.dev432.dist-info}/top_level.txt +0 -0
ominfra/scripts/journald2aws.py
CHANGED
@@ -92,13 +92,15 @@ LogLevel = int # ta.TypeAlias
|
|
92
92
|
# ../../../../omlish/configs/formats.py
|
93
93
|
ConfigDataT = ta.TypeVar('ConfigDataT', bound='ConfigData')
|
94
94
|
|
95
|
-
# ../../../../omlish/logs/
|
95
|
+
# ../../../../omlish/logs/infos.py
|
96
|
+
LoggingMsgFn = ta.Callable[[], ta.Union[str, tuple]] # ta.TypeAlias
|
96
97
|
LoggingExcInfoTuple = ta.Tuple[ta.Type[BaseException], BaseException, ta.Optional[types.TracebackType]] # ta.TypeAlias
|
97
98
|
LoggingExcInfo = ta.Union[BaseException, LoggingExcInfoTuple] # ta.TypeAlias
|
98
99
|
LoggingExcInfoArg = ta.Union[LoggingExcInfo, bool, None] # ta.TypeAlias
|
100
|
+
LoggingContextInfo = ta.Any # ta.TypeAlias
|
99
101
|
|
100
|
-
# ../../../../omlish/logs/
|
101
|
-
|
102
|
+
# ../../../../omlish/logs/contexts.py
|
103
|
+
LoggingContextInfoT = ta.TypeVar('LoggingContextInfoT', bound=LoggingContextInfo)
|
102
104
|
|
103
105
|
# ../../../threadworkers.py
|
104
106
|
ThreadWorkerT = ta.TypeVar('ThreadWorkerT', bound='ThreadWorker')
|
@@ -2689,124 +2691,6 @@ def format_num_bytes(num_bytes: int) -> str:
|
|
2689
2691
|
return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
|
2690
2692
|
|
2691
2693
|
|
2692
|
-
########################################
|
2693
|
-
# ../../../../../omlish/logs/infos.py
|
2694
|
-
|
2695
|
-
|
2696
|
-
##
|
2697
|
-
|
2698
|
-
|
2699
|
-
def logging_context_info(cls):
|
2700
|
-
return cls
|
2701
|
-
|
2702
|
-
|
2703
|
-
##
|
2704
|
-
|
2705
|
-
|
2706
|
-
@logging_context_info
|
2707
|
-
@ta.final
|
2708
|
-
class LoggingSourceFileInfo(ta.NamedTuple):
|
2709
|
-
file_name: str
|
2710
|
-
module: str
|
2711
|
-
|
2712
|
-
@classmethod
|
2713
|
-
def build(cls, file_path: ta.Optional[str]) -> ta.Optional['LoggingSourceFileInfo']:
|
2714
|
-
if file_path is None:
|
2715
|
-
return None
|
2716
|
-
|
2717
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L331-L336 # noqa
|
2718
|
-
try:
|
2719
|
-
file_name = os.path.basename(file_path)
|
2720
|
-
module = os.path.splitext(file_name)[0]
|
2721
|
-
except (TypeError, ValueError, AttributeError):
|
2722
|
-
return None
|
2723
|
-
|
2724
|
-
return cls(
|
2725
|
-
file_name=file_name,
|
2726
|
-
module=module,
|
2727
|
-
)
|
2728
|
-
|
2729
|
-
|
2730
|
-
##
|
2731
|
-
|
2732
|
-
|
2733
|
-
@logging_context_info
|
2734
|
-
@ta.final
|
2735
|
-
class LoggingThreadInfo(ta.NamedTuple):
|
2736
|
-
ident: int
|
2737
|
-
native_id: ta.Optional[int]
|
2738
|
-
name: str
|
2739
|
-
|
2740
|
-
@classmethod
|
2741
|
-
def build(cls) -> 'LoggingThreadInfo':
|
2742
|
-
return cls(
|
2743
|
-
ident=threading.get_ident(),
|
2744
|
-
native_id=threading.get_native_id() if hasattr(threading, 'get_native_id') else None,
|
2745
|
-
name=threading.current_thread().name,
|
2746
|
-
)
|
2747
|
-
|
2748
|
-
|
2749
|
-
##
|
2750
|
-
|
2751
|
-
|
2752
|
-
@logging_context_info
|
2753
|
-
@ta.final
|
2754
|
-
class LoggingProcessInfo(ta.NamedTuple):
|
2755
|
-
pid: int
|
2756
|
-
|
2757
|
-
@classmethod
|
2758
|
-
def build(cls) -> 'LoggingProcessInfo':
|
2759
|
-
return cls(
|
2760
|
-
pid=os.getpid(),
|
2761
|
-
)
|
2762
|
-
|
2763
|
-
|
2764
|
-
##
|
2765
|
-
|
2766
|
-
|
2767
|
-
@logging_context_info
|
2768
|
-
@ta.final
|
2769
|
-
class LoggingMultiprocessingInfo(ta.NamedTuple):
|
2770
|
-
process_name: str
|
2771
|
-
|
2772
|
-
@classmethod
|
2773
|
-
def build(cls) -> ta.Optional['LoggingMultiprocessingInfo']:
|
2774
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L355-L364 # noqa
|
2775
|
-
if (mp := sys.modules.get('multiprocessing')) is None:
|
2776
|
-
return None
|
2777
|
-
|
2778
|
-
return cls(
|
2779
|
-
process_name=mp.current_process().name,
|
2780
|
-
)
|
2781
|
-
|
2782
|
-
|
2783
|
-
##
|
2784
|
-
|
2785
|
-
|
2786
|
-
@logging_context_info
|
2787
|
-
@ta.final
|
2788
|
-
class LoggingAsyncioTaskInfo(ta.NamedTuple):
|
2789
|
-
name: str
|
2790
|
-
|
2791
|
-
@classmethod
|
2792
|
-
def build(cls) -> ta.Optional['LoggingAsyncioTaskInfo']:
|
2793
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L372-L377 # noqa
|
2794
|
-
if (asyncio := sys.modules.get('asyncio')) is None:
|
2795
|
-
return None
|
2796
|
-
|
2797
|
-
try:
|
2798
|
-
task = asyncio.current_task()
|
2799
|
-
except Exception: # noqa
|
2800
|
-
return None
|
2801
|
-
|
2802
|
-
if task is None:
|
2803
|
-
return None
|
2804
|
-
|
2805
|
-
return cls(
|
2806
|
-
name=task.get_name(), # Always non-None
|
2807
|
-
)
|
2808
|
-
|
2809
|
-
|
2810
2694
|
########################################
|
2811
2695
|
# ../../../../../omlish/logs/levels.py
|
2812
2696
|
|
@@ -4945,74 +4829,362 @@ def check_lite_runtime_version() -> None:
|
|
4945
4829
|
|
4946
4830
|
|
4947
4831
|
########################################
|
4948
|
-
# ../../../../../omlish/logs/
|
4832
|
+
# ../../../../../omlish/logs/infos.py
|
4833
|
+
"""
|
4834
|
+
TODO:
|
4835
|
+
- remove redundant info fields only present for std adaptation (Level.name, ...)
|
4836
|
+
"""
|
4949
4837
|
|
4950
4838
|
|
4951
4839
|
##
|
4952
4840
|
|
4953
4841
|
|
4954
|
-
|
4842
|
+
def logging_context_info(cls):
|
4843
|
+
return cls
|
4844
|
+
|
4845
|
+
|
4955
4846
|
@ta.final
|
4956
|
-
class
|
4957
|
-
|
4958
|
-
|
4959
|
-
name: str
|
4960
|
-
stack_info: ta.Optional[str]
|
4847
|
+
class LoggingContextInfos:
|
4848
|
+
def __new__(cls, *args, **kwargs): # noqa
|
4849
|
+
raise TypeError
|
4961
4850
|
|
4962
|
-
|
4963
|
-
def is_internal_frame(cls, frame: types.FrameType) -> bool:
|
4964
|
-
file_path = os.path.normcase(frame.f_code.co_filename)
|
4851
|
+
#
|
4965
4852
|
|
4966
|
-
|
4967
|
-
|
4968
|
-
|
4969
|
-
|
4970
|
-
return True
|
4853
|
+
@logging_context_info
|
4854
|
+
@ta.final
|
4855
|
+
class Name(ta.NamedTuple):
|
4856
|
+
name: str
|
4971
4857
|
|
4972
|
-
|
4858
|
+
@logging_context_info
|
4859
|
+
@ta.final
|
4860
|
+
class Level(ta.NamedTuple):
|
4861
|
+
level: NamedLogLevel
|
4862
|
+
name: str
|
4973
4863
|
|
4974
|
-
|
4975
|
-
|
4976
|
-
|
4864
|
+
@classmethod
|
4865
|
+
def build(cls, level: int) -> 'LoggingContextInfos.Level':
|
4866
|
+
nl: NamedLogLevel = level if level.__class__ is NamedLogLevel else NamedLogLevel(level) # type: ignore[assignment] # noqa
|
4867
|
+
return cls(
|
4868
|
+
level=nl,
|
4869
|
+
name=logging.getLevelName(nl),
|
4870
|
+
)
|
4871
|
+
|
4872
|
+
@logging_context_info
|
4873
|
+
@ta.final
|
4874
|
+
class Msg(ta.NamedTuple):
|
4875
|
+
msg: str
|
4876
|
+
args: ta.Union[tuple, ta.Mapping[ta.Any, ta.Any], None]
|
4977
4877
|
|
4978
|
-
|
4979
|
-
|
4980
|
-
|
4981
|
-
|
4982
|
-
|
4878
|
+
@classmethod
|
4879
|
+
def build(
|
4880
|
+
cls,
|
4881
|
+
msg: ta.Union[str, tuple, LoggingMsgFn],
|
4882
|
+
*args: ta.Any,
|
4883
|
+
) -> 'LoggingContextInfos.Msg':
|
4884
|
+
s: str
|
4885
|
+
a: ta.Any
|
4886
|
+
|
4887
|
+
if callable(msg):
|
4888
|
+
if args:
|
4889
|
+
raise TypeError(f'Must not provide both a message function and args: {msg=} {args=}')
|
4890
|
+
x = msg()
|
4891
|
+
if isinstance(x, str):
|
4892
|
+
s, a = x, ()
|
4893
|
+
elif isinstance(x, tuple):
|
4894
|
+
if x:
|
4895
|
+
s, a = x[0], x[1:]
|
4896
|
+
else:
|
4897
|
+
s, a = '', ()
|
4898
|
+
else:
|
4899
|
+
raise TypeError(x)
|
4983
4900
|
|
4984
|
-
|
4901
|
+
elif isinstance(msg, tuple):
|
4902
|
+
if args:
|
4903
|
+
raise TypeError(f'Must not provide both a tuple message and args: {msg=} {args=}')
|
4904
|
+
if msg:
|
4905
|
+
s, a = msg[0], msg[1:]
|
4906
|
+
else:
|
4907
|
+
s, a = '', ()
|
4985
4908
|
|
4986
|
-
|
4909
|
+
elif isinstance(msg, str):
|
4910
|
+
s, a = msg, args
|
4911
|
+
|
4912
|
+
else:
|
4913
|
+
raise TypeError(msg)
|
4914
|
+
|
4915
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L307 # noqa
|
4916
|
+
if a and len(a) == 1 and isinstance(a[0], collections.abc.Mapping) and a[0]:
|
4917
|
+
a = a[0]
|
4918
|
+
|
4919
|
+
return cls(
|
4920
|
+
msg=s,
|
4921
|
+
args=a,
|
4922
|
+
)
|
4923
|
+
|
4924
|
+
@logging_context_info
|
4925
|
+
@ta.final
|
4926
|
+
class Extra(ta.NamedTuple):
|
4927
|
+
extra: ta.Mapping[ta.Any, ta.Any]
|
4928
|
+
|
4929
|
+
@logging_context_info
|
4930
|
+
@ta.final
|
4931
|
+
class Time(ta.NamedTuple):
|
4932
|
+
ns: int
|
4933
|
+
secs: float
|
4934
|
+
msecs: float
|
4935
|
+
relative_secs: float
|
4936
|
+
|
4937
|
+
@classmethod
|
4938
|
+
def get_std_start_ns(cls) -> int:
|
4939
|
+
x: ta.Any = logging._startTime # type: ignore[attr-defined] # noqa
|
4940
|
+
|
4941
|
+
# Before 3.13.0b1 this will be `time.time()`, a float of seconds. After that, it will be `time.time_ns()`,
|
4942
|
+
# an int.
|
4943
|
+
#
|
4944
|
+
# See:
|
4945
|
+
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
4946
|
+
#
|
4947
|
+
if isinstance(x, float):
|
4948
|
+
return int(x * 1e9)
|
4949
|
+
else:
|
4950
|
+
return x
|
4951
|
+
|
4952
|
+
@classmethod
|
4953
|
+
def build(
|
4954
|
+
cls,
|
4955
|
+
ns: int,
|
4956
|
+
*,
|
4957
|
+
start_ns: ta.Optional[int] = None,
|
4958
|
+
) -> 'LoggingContextInfos.Time':
|
4959
|
+
# https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
4960
|
+
secs = ns / 1e9 # ns to float seconds
|
4961
|
+
|
4962
|
+
# Get the number of whole milliseconds (0-999) in the fractional part of seconds.
|
4963
|
+
# Eg: 1_677_903_920_999_998_503 ns --> 999_998_503 ns--> 999 ms
|
4964
|
+
# Convert to float by adding 0.0 for historical reasons. See gh-89047
|
4965
|
+
msecs = (ns % 1_000_000_000) // 1_000_000 + 0.0
|
4966
|
+
|
4967
|
+
# https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
4968
|
+
if msecs == 999.0 and int(secs) != ns // 1_000_000_000:
|
4969
|
+
# ns -> sec conversion can round up, e.g:
|
4970
|
+
# 1_677_903_920_999_999_900 ns --> 1_677_903_921.0 sec
|
4971
|
+
msecs = 0.0
|
4972
|
+
|
4973
|
+
if start_ns is None:
|
4974
|
+
start_ns = cls.get_std_start_ns()
|
4975
|
+
relative_secs = (ns - start_ns) / 1e6
|
4976
|
+
|
4977
|
+
return cls(
|
4978
|
+
ns=ns,
|
4979
|
+
secs=secs,
|
4980
|
+
msecs=msecs,
|
4981
|
+
relative_secs=relative_secs,
|
4982
|
+
)
|
4983
|
+
|
4984
|
+
@logging_context_info
|
4985
|
+
@ta.final
|
4986
|
+
class Exc(ta.NamedTuple):
|
4987
|
+
info: LoggingExcInfo
|
4988
|
+
info_tuple: LoggingExcInfoTuple
|
4989
|
+
|
4990
|
+
@classmethod
|
4991
|
+
def build(
|
4992
|
+
cls,
|
4993
|
+
arg: LoggingExcInfoArg = False,
|
4994
|
+
) -> ta.Optional['LoggingContextInfos.Exc']:
|
4995
|
+
if arg is True:
|
4996
|
+
sys_exc_info = sys.exc_info()
|
4997
|
+
if sys_exc_info[0] is not None:
|
4998
|
+
arg = sys_exc_info
|
4999
|
+
else:
|
5000
|
+
arg = None
|
5001
|
+
elif arg is False:
|
5002
|
+
arg = None
|
5003
|
+
if arg is None:
|
5004
|
+
return None
|
5005
|
+
|
5006
|
+
info: LoggingExcInfo = arg
|
5007
|
+
if isinstance(info, BaseException):
|
5008
|
+
info_tuple: LoggingExcInfoTuple = (type(info), info, info.__traceback__) # noqa
|
5009
|
+
else:
|
5010
|
+
info_tuple = info
|
5011
|
+
|
5012
|
+
return cls(
|
5013
|
+
info=info,
|
5014
|
+
info_tuple=info_tuple,
|
5015
|
+
)
|
5016
|
+
|
5017
|
+
@logging_context_info
|
5018
|
+
@ta.final
|
5019
|
+
class Caller(ta.NamedTuple):
|
5020
|
+
file_path: str
|
5021
|
+
line_no: int
|
5022
|
+
func_name: str
|
5023
|
+
stack_info: ta.Optional[str]
|
5024
|
+
|
5025
|
+
@classmethod
|
5026
|
+
def is_internal_frame(cls, frame: types.FrameType) -> bool:
|
5027
|
+
file_path = os.path.normcase(frame.f_code.co_filename)
|
5028
|
+
|
5029
|
+
# Yes, really.
|
5030
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L204 # noqa
|
5031
|
+
# https://github.com/python/cpython/commit/5ca6d7469be53960843df39bb900e9c3359f127f
|
5032
|
+
if 'importlib' in file_path and '_bootstrap' in file_path:
|
5033
|
+
return True
|
5034
|
+
|
5035
|
+
return False
|
5036
|
+
|
5037
|
+
@classmethod
|
5038
|
+
def find_frame(cls, stack_offset: int = 0) -> ta.Optional[types.FrameType]:
|
5039
|
+
f: ta.Optional[types.FrameType] = sys._getframe(2 + stack_offset) # noqa
|
5040
|
+
|
5041
|
+
while f is not None:
|
5042
|
+
# NOTE: We don't check __file__ like stdlib since we may be running amalgamated - we rely on careful,
|
5043
|
+
# manual stack_offset management.
|
5044
|
+
if hasattr(f, 'f_code'):
|
5045
|
+
return f
|
5046
|
+
|
5047
|
+
f = f.f_back
|
4987
5048
|
|
4988
|
-
@classmethod
|
4989
|
-
def find(
|
4990
|
-
cls,
|
4991
|
-
ofs: int = 0,
|
4992
|
-
*,
|
4993
|
-
stack_info: bool = False,
|
4994
|
-
) -> ta.Optional['LoggingCaller']:
|
4995
|
-
if (f := cls.find_frame(ofs + 1)) is None:
|
4996
5049
|
return None
|
4997
5050
|
|
4998
|
-
|
4999
|
-
|
5000
|
-
|
5001
|
-
|
5002
|
-
|
5003
|
-
|
5004
|
-
|
5005
|
-
if
|
5006
|
-
|
5051
|
+
@classmethod
|
5052
|
+
def build(
|
5053
|
+
cls,
|
5054
|
+
stack_offset: int = 0,
|
5055
|
+
*,
|
5056
|
+
stack_info: bool = False,
|
5057
|
+
) -> ta.Optional['LoggingContextInfos.Caller']:
|
5058
|
+
if (f := cls.find_frame(stack_offset + 1)) is None:
|
5059
|
+
return None
|
5060
|
+
|
5061
|
+
# https://github.com/python/cpython/blob/08e9794517063c8cd92c48714071b1d3c60b71bd/Lib/logging/__init__.py#L1616-L1623 # noqa
|
5062
|
+
sinfo = None
|
5063
|
+
if stack_info:
|
5064
|
+
sio = io.StringIO()
|
5065
|
+
traceback.print_stack(f, file=sio)
|
5066
|
+
sinfo = sio.getvalue()
|
5067
|
+
sio.close()
|
5068
|
+
if sinfo[-1] == '\n':
|
5069
|
+
sinfo = sinfo[:-1]
|
5070
|
+
|
5071
|
+
return cls(
|
5072
|
+
file_path=f.f_code.co_filename,
|
5073
|
+
line_no=f.f_lineno or 0,
|
5074
|
+
func_name=f.f_code.co_name,
|
5075
|
+
stack_info=sinfo,
|
5076
|
+
)
|
5007
5077
|
|
5008
|
-
|
5009
|
-
|
5010
|
-
|
5011
|
-
|
5012
|
-
|
5078
|
+
@logging_context_info
|
5079
|
+
@ta.final
|
5080
|
+
class SourceFile(ta.NamedTuple):
|
5081
|
+
file_name: str
|
5082
|
+
module: str
|
5083
|
+
|
5084
|
+
@classmethod
|
5085
|
+
def build(cls, caller_file_path: ta.Optional[str]) -> ta.Optional['LoggingContextInfos.SourceFile']:
|
5086
|
+
if caller_file_path is None:
|
5087
|
+
return None
|
5088
|
+
|
5089
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L331-L336 # noqa
|
5090
|
+
try:
|
5091
|
+
file_name = os.path.basename(caller_file_path)
|
5092
|
+
module = os.path.splitext(file_name)[0]
|
5093
|
+
except (TypeError, ValueError, AttributeError):
|
5094
|
+
return None
|
5095
|
+
|
5096
|
+
return cls(
|
5097
|
+
file_name=file_name,
|
5098
|
+
module=module,
|
5099
|
+
)
|
5100
|
+
|
5101
|
+
@logging_context_info
|
5102
|
+
@ta.final
|
5103
|
+
class Thread(ta.NamedTuple):
|
5104
|
+
ident: int
|
5105
|
+
native_id: ta.Optional[int]
|
5106
|
+
name: str
|
5107
|
+
|
5108
|
+
@classmethod
|
5109
|
+
def build(cls) -> 'LoggingContextInfos.Thread':
|
5110
|
+
return cls(
|
5111
|
+
ident=threading.get_ident(),
|
5112
|
+
native_id=threading.get_native_id() if hasattr(threading, 'get_native_id') else None,
|
5113
|
+
name=threading.current_thread().name,
|
5114
|
+
)
|
5115
|
+
|
5116
|
+
@logging_context_info
|
5117
|
+
@ta.final
|
5118
|
+
class Process(ta.NamedTuple):
|
5119
|
+
pid: int
|
5120
|
+
|
5121
|
+
@classmethod
|
5122
|
+
def build(cls) -> 'LoggingContextInfos.Process':
|
5123
|
+
return cls(
|
5124
|
+
pid=os.getpid(),
|
5125
|
+
)
|
5126
|
+
|
5127
|
+
@logging_context_info
|
5128
|
+
@ta.final
|
5129
|
+
class Multiprocessing(ta.NamedTuple):
|
5130
|
+
process_name: str
|
5131
|
+
|
5132
|
+
@classmethod
|
5133
|
+
def build(cls) -> ta.Optional['LoggingContextInfos.Multiprocessing']:
|
5134
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L355-L364 # noqa
|
5135
|
+
if (mp := sys.modules.get('multiprocessing')) is None:
|
5136
|
+
return None
|
5137
|
+
|
5138
|
+
return cls(
|
5139
|
+
process_name=mp.current_process().name,
|
5140
|
+
)
|
5141
|
+
|
5142
|
+
@logging_context_info
|
5143
|
+
@ta.final
|
5144
|
+
class AsyncioTask(ta.NamedTuple):
|
5145
|
+
name: str
|
5146
|
+
|
5147
|
+
@classmethod
|
5148
|
+
def build(cls) -> ta.Optional['LoggingContextInfos.AsyncioTask']:
|
5149
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L372-L377 # noqa
|
5150
|
+
if (asyncio := sys.modules.get('asyncio')) is None:
|
5151
|
+
return None
|
5152
|
+
|
5153
|
+
try:
|
5154
|
+
task = asyncio.current_task()
|
5155
|
+
except Exception: # noqa
|
5156
|
+
return None
|
5157
|
+
|
5158
|
+
if task is None:
|
5159
|
+
return None
|
5160
|
+
|
5161
|
+
return cls(
|
5162
|
+
name=task.get_name(), # Always non-None
|
5163
|
+
)
|
5164
|
+
|
5165
|
+
|
5166
|
+
##
|
5167
|
+
|
5168
|
+
|
5169
|
+
class UnexpectedLoggingStartTimeWarning(LoggingSetupWarning):
|
5170
|
+
pass
|
5171
|
+
|
5172
|
+
|
5173
|
+
def _check_logging_start_time() -> None:
|
5174
|
+
if (x := LoggingContextInfos.Time.get_std_start_ns()) < (t := time.time()):
|
5175
|
+
import warnings # noqa
|
5176
|
+
|
5177
|
+
warnings.warn(
|
5178
|
+
f'Unexpected logging start time detected: '
|
5179
|
+
f'get_std_start_ns={x}, '
|
5180
|
+
f'time.time()={t}',
|
5181
|
+
UnexpectedLoggingStartTimeWarning,
|
5013
5182
|
)
|
5014
5183
|
|
5015
5184
|
|
5185
|
+
_check_logging_start_time()
|
5186
|
+
|
5187
|
+
|
5016
5188
|
########################################
|
5017
5189
|
# ../../../../../omlish/logs/std/json.py
|
5018
5190
|
"""
|
@@ -5071,131 +5243,46 @@ class JsonLoggingFormatter(logging.Formatter):
|
|
5071
5243
|
|
5072
5244
|
|
5073
5245
|
########################################
|
5074
|
-
#
|
5246
|
+
# ../../logs.py
|
5247
|
+
"""
|
5248
|
+
https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html :
|
5249
|
+
- The maximum batch size is 1,048,576 bytes. This size is calculated as the sum of all event messages in UTF-8, plus 26
|
5250
|
+
bytes for each log event.
|
5251
|
+
- None of the log events in the batch can be more than 2 hours in the future.
|
5252
|
+
- None of the log events in the batch can be more than 14 days in the past. Also, none of the log events can be from
|
5253
|
+
earlier than the retention period of the log group.
|
5254
|
+
- The log events in the batch must be in chronological order by their timestamp. The timestamp is the time that the
|
5255
|
+
event occurred, expressed as the number of milliseconds after Jan 1, 1970 00:00:00 UTC. (In AWS Tools for PowerShell
|
5256
|
+
and the AWS SDK for .NET, the timestamp is specified in .NET format: yyyy-mm-ddThh:mm:ss. For example,
|
5257
|
+
2017-09-15T13:45:30.)
|
5258
|
+
- A batch of log events in a single request cannot span more than 24 hours. Otherwise, the operation fails.
|
5259
|
+
- Each log event can be no larger than 256 KB.
|
5260
|
+
- The maximum number of log events in a batch is 10,000.
|
5261
|
+
"""
|
5075
5262
|
|
5076
5263
|
|
5077
5264
|
##
|
5078
5265
|
|
5079
5266
|
|
5080
|
-
@
|
5081
|
-
|
5082
|
-
|
5083
|
-
|
5267
|
+
@dc.dataclass(frozen=True)
|
5268
|
+
class AwsLogEvent(AwsDataclass):
|
5269
|
+
message: str
|
5270
|
+
timestamp: int # milliseconds UTC
|
5084
5271
|
|
5085
|
-
created: float
|
5086
|
-
msecs: float
|
5087
|
-
relative_created: float
|
5088
5272
|
|
5089
|
-
|
5090
|
-
|
5091
|
-
|
5273
|
+
@dc.dataclass(frozen=True)
|
5274
|
+
class AwsPutLogEventsRequest(AwsDataclass):
|
5275
|
+
log_group_name: str
|
5276
|
+
log_stream_name: str
|
5277
|
+
log_events: ta.Sequence[AwsLogEvent]
|
5278
|
+
sequence_token: ta.Optional[str] = None
|
5092
5279
|
|
5093
|
-
# Before 3.13.0b1 this will be `time.time()`, a float of seconds. After that, it will be `time.time_ns()`, an
|
5094
|
-
# int.
|
5095
|
-
#
|
5096
|
-
# See:
|
5097
|
-
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
5098
|
-
#
|
5099
|
-
if isinstance(x, float):
|
5100
|
-
return int(x * 1e9)
|
5101
|
-
else:
|
5102
|
-
return x
|
5103
5280
|
|
5104
|
-
|
5105
|
-
|
5106
|
-
|
5107
|
-
|
5108
|
-
|
5109
|
-
start_time_ns: ta.Optional[int] = None,
|
5110
|
-
) -> 'LoggingTimeFields':
|
5111
|
-
# https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
5112
|
-
created = time_ns / 1e9 # ns to float seconds
|
5113
|
-
|
5114
|
-
# Get the number of whole milliseconds (0-999) in the fractional part of seconds.
|
5115
|
-
# Eg: 1_677_903_920_999_998_503 ns --> 999_998_503 ns--> 999 ms
|
5116
|
-
# Convert to float by adding 0.0 for historical reasons. See gh-89047
|
5117
|
-
msecs = (time_ns % 1_000_000_000) // 1_000_000 + 0.0
|
5118
|
-
|
5119
|
-
# https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
5120
|
-
if msecs == 999.0 and int(created) != time_ns // 1_000_000_000:
|
5121
|
-
# ns -> sec conversion can round up, e.g:
|
5122
|
-
# 1_677_903_920_999_999_900 ns --> 1_677_903_921.0 sec
|
5123
|
-
msecs = 0.0
|
5124
|
-
|
5125
|
-
if start_time_ns is None:
|
5126
|
-
start_time_ns = cls.get_std_start_time_ns()
|
5127
|
-
relative_created = (time_ns - start_time_ns) / 1e6
|
5128
|
-
|
5129
|
-
return cls(
|
5130
|
-
created=created,
|
5131
|
-
msecs=msecs,
|
5132
|
-
relative_created=relative_created,
|
5133
|
-
)
|
5134
|
-
|
5135
|
-
|
5136
|
-
##
|
5137
|
-
|
5138
|
-
|
5139
|
-
class UnexpectedLoggingStartTimeWarning(LoggingSetupWarning):
|
5140
|
-
pass
|
5141
|
-
|
5142
|
-
|
5143
|
-
def _check_logging_start_time() -> None:
|
5144
|
-
if (x := LoggingTimeFields.get_std_start_time_ns()) < (t := time.time()):
|
5145
|
-
import warnings # noqa
|
5146
|
-
|
5147
|
-
warnings.warn(
|
5148
|
-
f'Unexpected logging start time detected: '
|
5149
|
-
f'get_std_start_time_ns={x}, '
|
5150
|
-
f'time.time()={t}',
|
5151
|
-
UnexpectedLoggingStartTimeWarning,
|
5152
|
-
)
|
5153
|
-
|
5154
|
-
|
5155
|
-
_check_logging_start_time()
|
5156
|
-
|
5157
|
-
|
5158
|
-
########################################
|
5159
|
-
# ../../logs.py
|
5160
|
-
"""
|
5161
|
-
https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html :
|
5162
|
-
- The maximum batch size is 1,048,576 bytes. This size is calculated as the sum of all event messages in UTF-8, plus 26
|
5163
|
-
bytes for each log event.
|
5164
|
-
- None of the log events in the batch can be more than 2 hours in the future.
|
5165
|
-
- None of the log events in the batch can be more than 14 days in the past. Also, none of the log events can be from
|
5166
|
-
earlier than the retention period of the log group.
|
5167
|
-
- The log events in the batch must be in chronological order by their timestamp. The timestamp is the time that the
|
5168
|
-
event occurred, expressed as the number of milliseconds after Jan 1, 1970 00:00:00 UTC. (In AWS Tools for PowerShell
|
5169
|
-
and the AWS SDK for .NET, the timestamp is specified in .NET format: yyyy-mm-ddThh:mm:ss. For example,
|
5170
|
-
2017-09-15T13:45:30.)
|
5171
|
-
- A batch of log events in a single request cannot span more than 24 hours. Otherwise, the operation fails.
|
5172
|
-
- Each log event can be no larger than 256 KB.
|
5173
|
-
- The maximum number of log events in a batch is 10,000.
|
5174
|
-
"""
|
5175
|
-
|
5176
|
-
|
5177
|
-
##
|
5178
|
-
|
5179
|
-
|
5180
|
-
@dc.dataclass(frozen=True)
|
5181
|
-
class AwsLogEvent(AwsDataclass):
|
5182
|
-
message: str
|
5183
|
-
timestamp: int # milliseconds UTC
|
5184
|
-
|
5185
|
-
|
5186
|
-
@dc.dataclass(frozen=True)
|
5187
|
-
class AwsPutLogEventsRequest(AwsDataclass):
|
5188
|
-
log_group_name: str
|
5189
|
-
log_stream_name: str
|
5190
|
-
log_events: ta.Sequence[AwsLogEvent]
|
5191
|
-
sequence_token: ta.Optional[str] = None
|
5192
|
-
|
5193
|
-
|
5194
|
-
@dc.dataclass(frozen=True)
|
5195
|
-
class AwsRejectedLogEventsInfo(AwsDataclass):
|
5196
|
-
expired_log_event_end_index: ta.Optional[int] = None
|
5197
|
-
too_new_log_event_start_index: ta.Optional[int] = None
|
5198
|
-
too_old_log_event_end_index: ta.Optional[int] = None
|
5281
|
+
@dc.dataclass(frozen=True)
|
5282
|
+
class AwsRejectedLogEventsInfo(AwsDataclass):
|
5283
|
+
expired_log_event_end_index: ta.Optional[int] = None
|
5284
|
+
too_new_log_event_start_index: ta.Optional[int] = None
|
5285
|
+
too_old_log_event_end_index: ta.Optional[int] = None
|
5199
5286
|
|
5200
5287
|
|
5201
5288
|
@dc.dataclass(frozen=True)
|
@@ -5366,68 +5453,46 @@ def load_config_file_obj(
|
|
5366
5453
|
|
5367
5454
|
|
5368
5455
|
class LoggingContext(Abstract):
|
5369
|
-
@property
|
5370
|
-
@abc.abstractmethod
|
5371
|
-
def level(self) -> NamedLogLevel:
|
5372
|
-
raise NotImplementedError
|
5373
|
-
|
5374
|
-
#
|
5375
|
-
|
5376
|
-
@property
|
5377
5456
|
@abc.abstractmethod
|
5378
|
-
def
|
5457
|
+
def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
5379
5458
|
raise NotImplementedError
|
5380
5459
|
|
5381
|
-
@
|
5382
|
-
|
5383
|
-
|
5384
|
-
raise NotImplementedError
|
5385
|
-
|
5386
|
-
#
|
5387
|
-
|
5388
|
-
@property
|
5389
|
-
@abc.abstractmethod
|
5390
|
-
def exc_info(self) -> ta.Optional[LoggingExcInfo]:
|
5391
|
-
raise NotImplementedError
|
5460
|
+
@ta.final
|
5461
|
+
def __getitem__(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
5462
|
+
return self.get_info(ty)
|
5392
5463
|
|
5393
|
-
@
|
5394
|
-
|
5395
|
-
|
5396
|
-
|
5464
|
+
@ta.final
|
5465
|
+
def must_get_info(self, ty: ta.Type[LoggingContextInfoT]) -> LoggingContextInfoT:
|
5466
|
+
if (info := self.get_info(ty)) is None:
|
5467
|
+
raise TypeError(f'LoggingContextInfo absent: {ty}')
|
5468
|
+
return info
|
5397
5469
|
|
5398
|
-
#
|
5399
5470
|
|
5400
|
-
|
5401
|
-
|
5402
|
-
|
5471
|
+
@ta.final
|
5472
|
+
class SimpleLoggingContext(LoggingContext):
|
5473
|
+
def __init__(self, *infos: LoggingContextInfo) -> None:
|
5474
|
+
self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {type(i): i for i in infos}
|
5403
5475
|
|
5404
|
-
|
5405
|
-
|
5406
|
-
raise NotImplementedError
|
5476
|
+
def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
5477
|
+
return self._infos.get(ty)
|
5407
5478
|
|
5408
|
-
#
|
5409
5479
|
|
5410
|
-
|
5411
|
-
def thread(self) -> ta.Optional[LoggingThreadInfo]:
|
5412
|
-
raise NotImplementedError
|
5480
|
+
##
|
5413
5481
|
|
5414
|
-
@abc.abstractmethod
|
5415
|
-
def process(self) -> ta.Optional[LoggingProcessInfo]:
|
5416
|
-
raise NotImplementedError
|
5417
5482
|
|
5483
|
+
class CaptureLoggingContext(LoggingContext, Abstract):
|
5418
5484
|
@abc.abstractmethod
|
5419
|
-
def
|
5420
|
-
|
5485
|
+
def set_basic(
|
5486
|
+
self,
|
5487
|
+
name: str,
|
5421
5488
|
|
5422
|
-
|
5423
|
-
|
5489
|
+
msg: ta.Union[str, tuple, LoggingMsgFn],
|
5490
|
+
args: tuple,
|
5491
|
+
) -> 'CaptureLoggingContext':
|
5424
5492
|
raise NotImplementedError
|
5425
5493
|
|
5494
|
+
#
|
5426
5495
|
|
5427
|
-
##
|
5428
|
-
|
5429
|
-
|
5430
|
-
class CaptureLoggingContext(LoggingContext, Abstract):
|
5431
5496
|
class AlreadyCapturedError(Exception):
|
5432
5497
|
pass
|
5433
5498
|
|
@@ -5458,80 +5523,50 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
|
|
5458
5523
|
|
5459
5524
|
exc_info: LoggingExcInfoArg = False,
|
5460
5525
|
|
5461
|
-
caller: ta.Union[
|
5526
|
+
caller: ta.Union[LoggingContextInfos.Caller, ta.Type[NOT_SET], None] = NOT_SET,
|
5462
5527
|
stack_offset: int = 0,
|
5463
5528
|
stack_info: bool = False,
|
5464
5529
|
) -> None:
|
5465
|
-
|
5466
|
-
|
5467
|
-
#
|
5530
|
+
# TODO: Name, Msg, Extra
|
5468
5531
|
|
5469
5532
|
if time_ns is None:
|
5470
5533
|
time_ns = time.time_ns()
|
5471
|
-
self._time_ns: int = time_ns
|
5472
|
-
|
5473
|
-
#
|
5474
5534
|
|
5475
|
-
|
5476
|
-
|
5477
|
-
|
5478
|
-
|
5479
|
-
|
5480
|
-
|
5481
|
-
elif exc_info is False:
|
5482
|
-
exc_info = None
|
5483
|
-
|
5484
|
-
if exc_info is not None:
|
5485
|
-
self._exc_info: ta.Optional[LoggingExcInfo] = exc_info
|
5486
|
-
if isinstance(exc_info, BaseException):
|
5487
|
-
self._exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = (type(exc_info), exc_info, exc_info.__traceback__) # noqa
|
5488
|
-
else:
|
5489
|
-
self._exc_info_tuple = exc_info
|
5490
|
-
|
5491
|
-
#
|
5535
|
+
self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {}
|
5536
|
+
self._set_info(
|
5537
|
+
LoggingContextInfos.Level.build(level),
|
5538
|
+
LoggingContextInfos.Time.build(time_ns),
|
5539
|
+
LoggingContextInfos.Exc.build(exc_info),
|
5540
|
+
)
|
5492
5541
|
|
5493
5542
|
if caller is not CaptureLoggingContextImpl.NOT_SET:
|
5494
|
-
self.
|
5543
|
+
self._infos[LoggingContextInfos.Caller] = caller
|
5495
5544
|
else:
|
5496
5545
|
self._stack_offset = stack_offset
|
5497
5546
|
self._stack_info = stack_info
|
5498
5547
|
|
5499
|
-
|
5500
|
-
|
5501
|
-
|
5502
|
-
|
5503
|
-
return self
|
5504
|
-
|
5505
|
-
#
|
5506
|
-
|
5507
|
-
@property
|
5508
|
-
def time_ns(self) -> int:
|
5509
|
-
return self._time_ns
|
5510
|
-
|
5511
|
-
_times: LoggingTimeFields
|
5512
|
-
|
5513
|
-
@property
|
5514
|
-
def times(self) -> LoggingTimeFields:
|
5515
|
-
try:
|
5516
|
-
return self._times
|
5517
|
-
except AttributeError:
|
5518
|
-
pass
|
5519
|
-
|
5520
|
-
times = self._times = LoggingTimeFields.build(self.time_ns)
|
5521
|
-
return times
|
5548
|
+
def _set_info(self, *infos: ta.Optional[LoggingContextInfo]) -> 'CaptureLoggingContextImpl':
|
5549
|
+
for info in infos:
|
5550
|
+
if info is not None:
|
5551
|
+
self._infos[type(info)] = info
|
5552
|
+
return self
|
5522
5553
|
|
5523
|
-
|
5554
|
+
def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
5555
|
+
return self._infos.get(ty)
|
5524
5556
|
|
5525
|
-
|
5526
|
-
_exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = None
|
5557
|
+
##
|
5527
5558
|
|
5528
|
-
|
5529
|
-
|
5530
|
-
|
5559
|
+
def set_basic(
|
5560
|
+
self,
|
5561
|
+
name: str,
|
5531
5562
|
|
5532
|
-
|
5533
|
-
|
5534
|
-
|
5563
|
+
msg: ta.Union[str, tuple, LoggingMsgFn],
|
5564
|
+
args: tuple,
|
5565
|
+
) -> 'CaptureLoggingContextImpl':
|
5566
|
+
return self._set_info(
|
5567
|
+
LoggingContextInfos.Name(name),
|
5568
|
+
LoggingContextInfos.Msg.build(msg, *args),
|
5569
|
+
)
|
5535
5570
|
|
5536
5571
|
##
|
5537
5572
|
|
@@ -5545,74 +5580,28 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
|
|
5545
5580
|
|
5546
5581
|
_has_captured: bool = False
|
5547
5582
|
|
5548
|
-
_caller: ta.Optional[LoggingCaller]
|
5549
|
-
_source_file: ta.Optional[LoggingSourceFileInfo]
|
5550
|
-
|
5551
|
-
_thread: ta.Optional[LoggingThreadInfo]
|
5552
|
-
_process: ta.Optional[LoggingProcessInfo]
|
5553
|
-
_multiprocessing: ta.Optional[LoggingMultiprocessingInfo]
|
5554
|
-
_asyncio_task: ta.Optional[LoggingAsyncioTaskInfo]
|
5555
|
-
|
5556
5583
|
def capture(self) -> None:
|
5557
5584
|
if self._has_captured:
|
5558
5585
|
raise CaptureLoggingContextImpl.AlreadyCapturedError
|
5559
5586
|
self._has_captured = True
|
5560
5587
|
|
5561
|
-
if not
|
5562
|
-
self.
|
5588
|
+
if LoggingContextInfos.Caller not in self._infos:
|
5589
|
+
self._set_info(LoggingContextInfos.Caller.build(
|
5563
5590
|
self._stack_offset + 1,
|
5564
5591
|
stack_info=self._stack_info,
|
5565
|
-
)
|
5566
|
-
|
5567
|
-
if (caller := self._caller) is not None:
|
5568
|
-
self._source_file = LoggingSourceFileInfo.build(caller.file_path)
|
5569
|
-
else:
|
5570
|
-
self._source_file = None
|
5571
|
-
|
5572
|
-
self._thread = LoggingThreadInfo.build()
|
5573
|
-
self._process = LoggingProcessInfo.build()
|
5574
|
-
self._multiprocessing = LoggingMultiprocessingInfo.build()
|
5575
|
-
self._asyncio_task = LoggingAsyncioTaskInfo.build()
|
5576
|
-
|
5577
|
-
#
|
5578
|
-
|
5579
|
-
def caller(self) -> ta.Optional[LoggingCaller]:
|
5580
|
-
try:
|
5581
|
-
return self._caller
|
5582
|
-
except AttributeError:
|
5583
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
5584
|
-
|
5585
|
-
def source_file(self) -> ta.Optional[LoggingSourceFileInfo]:
|
5586
|
-
try:
|
5587
|
-
return self._source_file
|
5588
|
-
except AttributeError:
|
5589
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
5590
|
-
|
5591
|
-
#
|
5592
|
-
|
5593
|
-
def thread(self) -> ta.Optional[LoggingThreadInfo]:
|
5594
|
-
try:
|
5595
|
-
return self._thread
|
5596
|
-
except AttributeError:
|
5597
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
5598
|
-
|
5599
|
-
def process(self) -> ta.Optional[LoggingProcessInfo]:
|
5600
|
-
try:
|
5601
|
-
return self._process
|
5602
|
-
except AttributeError:
|
5603
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
5592
|
+
))
|
5604
5593
|
|
5605
|
-
|
5606
|
-
|
5607
|
-
|
5608
|
-
|
5609
|
-
raise CaptureLoggingContext.NotCapturedError from None
|
5594
|
+
if (caller := self[LoggingContextInfos.Caller]) is not None:
|
5595
|
+
self._set_info(LoggingContextInfos.SourceFile.build(
|
5596
|
+
caller.file_path,
|
5597
|
+
))
|
5610
5598
|
|
5611
|
-
|
5612
|
-
|
5613
|
-
|
5614
|
-
|
5615
|
-
|
5599
|
+
self._set_info(
|
5600
|
+
LoggingContextInfos.Thread.build(),
|
5601
|
+
LoggingContextInfos.Process.build(),
|
5602
|
+
LoggingContextInfos.Multiprocessing.build(),
|
5603
|
+
LoggingContextInfos.AsyncioTask.build(),
|
5604
|
+
)
|
5616
5605
|
|
5617
5606
|
|
5618
5607
|
########################################
|
@@ -5689,10 +5678,14 @@ def _locking_logging_module_lock() -> ta.Iterator[None]:
|
|
5689
5678
|
def configure_standard_logging(
|
5690
5679
|
level: ta.Union[int, str] = logging.INFO,
|
5691
5680
|
*,
|
5692
|
-
json: bool = False,
|
5693
5681
|
target: ta.Optional[logging.Logger] = None,
|
5682
|
+
|
5694
5683
|
force: bool = False,
|
5684
|
+
|
5695
5685
|
handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
|
5686
|
+
|
5687
|
+
formatter: ta.Optional[logging.Formatter] = None, # noqa
|
5688
|
+
json: bool = False,
|
5696
5689
|
) -> ta.Optional[StandardConfiguredLoggingHandler]:
|
5697
5690
|
with _locking_logging_module_lock():
|
5698
5691
|
if target is None:
|
@@ -5713,11 +5706,11 @@ def configure_standard_logging(
|
|
5713
5706
|
|
5714
5707
|
#
|
5715
5708
|
|
5716
|
-
formatter:
|
5717
|
-
|
5718
|
-
|
5719
|
-
|
5720
|
-
|
5709
|
+
if formatter is None:
|
5710
|
+
if json:
|
5711
|
+
formatter = JsonLoggingFormatter()
|
5712
|
+
else:
|
5713
|
+
formatter = StandardLoggingFormatter(StandardLoggingFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS)) # noqa
|
5721
5714
|
handler.setFormatter(formatter)
|
5722
5715
|
|
5723
5716
|
#
|
@@ -5917,36 +5910,6 @@ class AnyLogger(Abstract, ta.Generic[T]):
|
|
5917
5910
|
|
5918
5911
|
##
|
5919
5912
|
|
5920
|
-
@classmethod
|
5921
|
-
def _prepare_msg_args(cls, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> ta.Tuple[str, tuple]:
|
5922
|
-
if callable(msg):
|
5923
|
-
if args:
|
5924
|
-
raise TypeError(f'Must not provide both a message function and args: {msg=} {args=}')
|
5925
|
-
x = msg()
|
5926
|
-
if isinstance(x, str):
|
5927
|
-
return x, ()
|
5928
|
-
elif isinstance(x, tuple):
|
5929
|
-
if x:
|
5930
|
-
return x[0], x[1:]
|
5931
|
-
else:
|
5932
|
-
return '', ()
|
5933
|
-
else:
|
5934
|
-
raise TypeError(x)
|
5935
|
-
|
5936
|
-
elif isinstance(msg, tuple):
|
5937
|
-
if args:
|
5938
|
-
raise TypeError(f'Must not provide both a tuple message and args: {msg=} {args=}')
|
5939
|
-
if msg:
|
5940
|
-
return msg[0], msg[1:]
|
5941
|
-
else:
|
5942
|
-
return '', ()
|
5943
|
-
|
5944
|
-
elif isinstance(msg, str):
|
5945
|
-
return msg, args
|
5946
|
-
|
5947
|
-
else:
|
5948
|
-
raise TypeError(msg)
|
5949
|
-
|
5950
5913
|
@abc.abstractmethod
|
5951
5914
|
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> T: # noqa
|
5952
5915
|
raise NotImplementedError
|
@@ -5987,144 +5950,560 @@ class AsyncNopLogger(AnyNopLogger[ta.Awaitable[None]], AsyncLogger):
|
|
5987
5950
|
|
5988
5951
|
########################################
|
5989
5952
|
# ../../../../../omlish/logs/std/records.py
|
5953
|
+
"""
|
5954
|
+
TODO:
|
5955
|
+
- TypedDict?
|
5956
|
+
"""
|
5990
5957
|
|
5991
5958
|
|
5992
5959
|
##
|
5993
5960
|
|
5994
5961
|
|
5995
|
-
|
5996
|
-
#
|
5997
|
-
#
|
5998
|
-
#
|
5999
|
-
#
|
6000
|
-
# - https://github.com/python/cpython/blob/
|
6001
|
-
#
|
6002
|
-
#
|
6003
|
-
# - name: str
|
6004
|
-
# - level: int
|
6005
|
-
# - pathname: str - Confusingly referred to as `fn` before the LogRecord ctor. May be empty or "(unknown file)".
|
6006
|
-
# - lineno: int - May be 0.
|
6007
|
-
# - msg: str
|
6008
|
-
# - args: tuple | dict | 1-tuple[dict]
|
6009
|
-
# - exc_info: LoggingExcInfoTuple | None
|
6010
|
-
# - func: str | None = None -> funcName
|
6011
|
-
# - sinfo: str | None = None -> stack_info
|
6012
|
-
#
|
6013
|
-
KNOWN_STD_LOGGING_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
|
6014
|
-
# Name of the logger used to log the call. Unmodified by ctor.
|
6015
|
-
name=str,
|
5962
|
+
class LoggingContextInfoRecordAdapters:
|
5963
|
+
# Ref:
|
5964
|
+
# - https://docs.python.org/3/library/logging.html#logrecord-attributes
|
5965
|
+
#
|
5966
|
+
# LogRecord:
|
5967
|
+
# - https://github.com/python/cpython/blob/39b2f82717a69dde7212bc39b673b0f55c99e6a3/Lib/logging/__init__.py#L276 (3.8) # noqa
|
5968
|
+
# - https://github.com/python/cpython/blob/f070f54c5f4a42c7c61d1d5d3b8f3b7203b4a0fb/Lib/logging/__init__.py#L286 (~3.14) # noqa
|
5969
|
+
#
|
6016
5970
|
|
6017
|
-
|
6018
|
-
|
6019
|
-
msg=str,
|
5971
|
+
def __new__(cls, *args, **kwargs): # noqa
|
5972
|
+
raise TypeError
|
6020
5973
|
|
6021
|
-
|
6022
|
-
|
6023
|
-
|
6024
|
-
|
5974
|
+
class Adapter(Abstract, ta.Generic[T]):
|
5975
|
+
@property
|
5976
|
+
@abc.abstractmethod
|
5977
|
+
def info_cls(self) -> ta.Type[LoggingContextInfo]:
|
5978
|
+
raise NotImplementedError
|
6025
5979
|
|
6026
|
-
|
5980
|
+
#
|
6027
5981
|
|
6028
|
-
|
6029
|
-
|
6030
|
-
|
5982
|
+
@ta.final
|
5983
|
+
class NOT_SET: # noqa
|
5984
|
+
def __new__(cls, *args, **kwargs): # noqa
|
5985
|
+
raise TypeError
|
6031
5986
|
|
6032
|
-
|
6033
|
-
|
5987
|
+
class RecordAttr(ta.NamedTuple):
|
5988
|
+
name: str
|
5989
|
+
type: ta.Any
|
5990
|
+
default: ta.Any
|
6034
5991
|
|
6035
|
-
|
5992
|
+
# @abc.abstractmethod
|
5993
|
+
record_attrs: ta.ClassVar[ta.Mapping[str, RecordAttr]]
|
6036
5994
|
|
6037
|
-
|
6038
|
-
|
6039
|
-
|
5995
|
+
@property
|
5996
|
+
@abc.abstractmethod
|
5997
|
+
def _record_attrs(self) -> ta.Union[
|
5998
|
+
ta.Mapping[str, ta.Any],
|
5999
|
+
ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]],
|
6000
|
+
]:
|
6001
|
+
raise NotImplementedError
|
6040
6002
|
|
6041
|
-
|
6042
|
-
filename=str,
|
6003
|
+
#
|
6043
6004
|
|
6044
|
-
|
6045
|
-
|
6046
|
-
|
6005
|
+
@abc.abstractmethod
|
6006
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
6007
|
+
raise NotImplementedError
|
6047
6008
|
|
6048
|
-
|
6009
|
+
#
|
6049
6010
|
|
6050
|
-
|
6051
|
-
|
6011
|
+
@abc.abstractmethod
|
6012
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[T]:
|
6013
|
+
raise NotImplementedError
|
6052
6014
|
|
6053
|
-
|
6054
|
-
exc_text=ta.Optional[str],
|
6015
|
+
#
|
6055
6016
|
|
6056
|
-
|
6017
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
6018
|
+
super().__init_subclass__(**kwargs)
|
6057
6019
|
|
6058
|
-
|
6059
|
-
|
6060
|
-
# unmodified. Mostly set, if requested, by `Logger.findCaller`, to `traceback.print_stack(f)`, but prepended with
|
6061
|
-
# the literal "Stack (most recent call last):\n", and stripped of exactly one trailing `\n` if present.
|
6062
|
-
stack_info=ta.Optional[str],
|
6020
|
+
if Abstract in cls.__bases__:
|
6021
|
+
return
|
6063
6022
|
|
6064
|
-
|
6065
|
-
|
6066
|
-
|
6023
|
+
if 'record_attrs' in cls.__dict__:
|
6024
|
+
raise TypeError(cls)
|
6025
|
+
if not isinstance(ra := cls.__dict__['_record_attrs'], collections.abc.Mapping):
|
6026
|
+
raise TypeError(ra)
|
6027
|
+
|
6028
|
+
rd: ta.Dict[str, LoggingContextInfoRecordAdapters.Adapter.RecordAttr] = {}
|
6029
|
+
for n, v in ra.items():
|
6030
|
+
if not n or not isinstance(n, str) or n in rd:
|
6031
|
+
raise AttributeError(n)
|
6032
|
+
if isinstance(v, tuple):
|
6033
|
+
t, d = v
|
6034
|
+
else:
|
6035
|
+
t, d = v, cls.NOT_SET
|
6036
|
+
rd[n] = cls.RecordAttr(
|
6037
|
+
name=n,
|
6038
|
+
type=t,
|
6039
|
+
default=d,
|
6040
|
+
)
|
6041
|
+
cls.record_attrs = rd
|
6067
6042
|
|
6068
|
-
|
6069
|
-
|
6070
|
-
|
6043
|
+
class RequiredAdapter(Adapter[T], Abstract):
|
6044
|
+
@property
|
6045
|
+
@abc.abstractmethod
|
6046
|
+
def _record_attrs(self) -> ta.Mapping[str, ta.Any]:
|
6047
|
+
raise NotImplementedError
|
6071
6048
|
|
6072
|
-
|
6049
|
+
#
|
6073
6050
|
|
6074
|
-
|
6075
|
-
|
6076
|
-
|
6077
|
-
|
6078
|
-
|
6079
|
-
|
6080
|
-
created=float,
|
6051
|
+
@ta.final
|
6052
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
6053
|
+
if (info := ctx.get_info(self.info_cls)) is not None:
|
6054
|
+
return self._info_to_record(info)
|
6055
|
+
else:
|
6056
|
+
raise TypeError # FIXME: fallback?
|
6081
6057
|
|
6082
|
-
|
6083
|
-
|
6058
|
+
@abc.abstractmethod
|
6059
|
+
def _info_to_record(self, info: T) -> ta.Mapping[str, ta.Any]:
|
6060
|
+
raise NotImplementedError
|
6084
6061
|
|
6085
|
-
|
6086
|
-
relativeCreated=float,
|
6062
|
+
#
|
6087
6063
|
|
6088
|
-
|
6064
|
+
@abc.abstractmethod
|
6065
|
+
def record_to_info(self, rec: logging.LogRecord) -> T:
|
6066
|
+
raise NotImplementedError
|
6089
6067
|
|
6090
|
-
|
6091
|
-
thread=ta.Optional[int],
|
6068
|
+
#
|
6092
6069
|
|
6093
|
-
|
6094
|
-
|
6070
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
6071
|
+
super().__init_subclass__(**kwargs)
|
6095
6072
|
|
6096
|
-
|
6073
|
+
if any(a.default is not cls.NOT_SET for a in cls.record_attrs.values()):
|
6074
|
+
raise TypeError(cls.record_attrs)
|
6075
|
+
|
6076
|
+
class OptionalAdapter(Adapter[T], Abstract, ta.Generic[T]):
|
6077
|
+
@property
|
6078
|
+
@abc.abstractmethod
|
6079
|
+
def _record_attrs(self) -> ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]:
|
6080
|
+
raise NotImplementedError
|
6081
|
+
|
6082
|
+
record_defaults: ta.ClassVar[ta.Mapping[str, ta.Any]]
|
6083
|
+
|
6084
|
+
#
|
6085
|
+
|
6086
|
+
@ta.final
|
6087
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
6088
|
+
if (info := ctx.get_info(self.info_cls)) is not None:
|
6089
|
+
return self._info_to_record(info)
|
6090
|
+
else:
|
6091
|
+
return self.record_defaults
|
6092
|
+
|
6093
|
+
@abc.abstractmethod
|
6094
|
+
def _info_to_record(self, info: T) -> ta.Mapping[str, ta.Any]:
|
6095
|
+
raise NotImplementedError
|
6096
|
+
|
6097
|
+
#
|
6098
|
+
|
6099
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
6100
|
+
super().__init_subclass__(**kwargs)
|
6101
|
+
|
6102
|
+
dd: ta.Dict[str, ta.Any] = {a.name: a.default for a in cls.record_attrs.values()}
|
6103
|
+
if any(d is cls.NOT_SET for d in dd.values()):
|
6104
|
+
raise TypeError(cls.record_attrs)
|
6105
|
+
cls.record_defaults = dd
|
6097
6106
|
|
6098
|
-
# Process name if available. Set to None if `logging.logMultiprocessing` is not truthy. Otherwise, set to
|
6099
|
-
# 'MainProcess', then `sys.modules.get('multiprocessing').current_process().name` if that works, otherwise remains
|
6100
|
-
# as 'MainProcess'.
|
6101
6107
|
#
|
6102
|
-
|
6108
|
+
|
6109
|
+
class Name(RequiredAdapter[LoggingContextInfos.Name]):
|
6110
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Name]] = LoggingContextInfos.Name
|
6111
|
+
|
6112
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
6113
|
+
# Name of the logger used to log the call. Unmodified by ctor.
|
6114
|
+
name=str,
|
6115
|
+
)
|
6116
|
+
|
6117
|
+
def _info_to_record(self, info: LoggingContextInfos.Name) -> ta.Mapping[str, ta.Any]:
|
6118
|
+
return dict(
|
6119
|
+
name=info.name,
|
6120
|
+
)
|
6121
|
+
|
6122
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Name:
|
6123
|
+
return LoggingContextInfos.Name(
|
6124
|
+
name=rec.name,
|
6125
|
+
)
|
6126
|
+
|
6127
|
+
class Level(RequiredAdapter[LoggingContextInfos.Level]):
|
6128
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Level]] = LoggingContextInfos.Level
|
6129
|
+
|
6130
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
6131
|
+
# Text logging level for the message ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). Set to
|
6132
|
+
# `getLevelName(level)`.
|
6133
|
+
levelname=str,
|
6134
|
+
|
6135
|
+
# Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL). Unmodified by ctor.
|
6136
|
+
levelno=int,
|
6137
|
+
)
|
6138
|
+
|
6139
|
+
def _info_to_record(self, info: LoggingContextInfos.Level) -> ta.Mapping[str, ta.Any]:
|
6140
|
+
return dict(
|
6141
|
+
levelname=info.name,
|
6142
|
+
levelno=int(info.level),
|
6143
|
+
)
|
6144
|
+
|
6145
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Level:
|
6146
|
+
return LoggingContextInfos.Level.build(rec.levelno)
|
6147
|
+
|
6148
|
+
class Msg(RequiredAdapter[LoggingContextInfos.Msg]):
|
6149
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Msg]] = LoggingContextInfos.Msg
|
6150
|
+
|
6151
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
6152
|
+
# The format string passed in the original logging call. Merged with args to produce message, or an
|
6153
|
+
# arbitrary object (see Using arbitrary objects as messages). Unmodified by ctor.
|
6154
|
+
msg=str,
|
6155
|
+
|
6156
|
+
# The tuple of arguments merged into msg to produce message, or a dict whose values are used for the merge
|
6157
|
+
# (when there is only one argument, and it is a dictionary). Ctor will transform a 1-tuple containing a
|
6158
|
+
# Mapping into just the mapping, but is otherwise unmodified.
|
6159
|
+
args=ta.Union[tuple, dict, None],
|
6160
|
+
)
|
6161
|
+
|
6162
|
+
def _info_to_record(self, info: LoggingContextInfos.Msg) -> ta.Mapping[str, ta.Any]:
|
6163
|
+
return dict(
|
6164
|
+
msg=info.msg,
|
6165
|
+
args=info.args,
|
6166
|
+
)
|
6167
|
+
|
6168
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Msg:
|
6169
|
+
return LoggingContextInfos.Msg(
|
6170
|
+
msg=rec.msg,
|
6171
|
+
args=rec.args,
|
6172
|
+
)
|
6173
|
+
|
6174
|
+
# FIXME: handled specially - all unknown attrs on LogRecord
|
6175
|
+
# class Extra(Adapter[LoggingContextInfos.Extra]):
|
6176
|
+
# _record_attrs: ta.ClassVar[ta.Mapping[str, ta.Union[ta.Any, ta.Tuple[ta.Any, ta.Any]]]] = dict()
|
6103
6177
|
#
|
6104
|
-
#
|
6105
|
-
#
|
6178
|
+
# def info_to_record(self, info: ta.Optional[LoggingContextInfos.Extra]) -> ta.Mapping[str, ta.Any]:
|
6179
|
+
# # FIXME:
|
6180
|
+
# # if extra is not None:
|
6181
|
+
# # for key in extra:
|
6182
|
+
# # if (key in ["message", "asctime"]) or (key in rv.__dict__):
|
6183
|
+
# # raise KeyError("Attempt to overwrite %r in LogRecord" % key)
|
6184
|
+
# # rv.__dict__[key] = extra[key]
|
6185
|
+
# return dict()
|
6106
6186
|
#
|
6107
|
-
|
6187
|
+
# def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Extra]:
|
6188
|
+
# return None
|
6189
|
+
|
6190
|
+
class Time(RequiredAdapter[LoggingContextInfos.Time]):
|
6191
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Time]] = LoggingContextInfos.Time
|
6192
|
+
|
6193
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
6194
|
+
# Time when the LogRecord was created. Set to `time.time_ns() / 1e9` for >=3.13.0b1, otherwise simply
|
6195
|
+
# `time.time()`.
|
6196
|
+
#
|
6197
|
+
# See:
|
6198
|
+
# - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
|
6199
|
+
# - https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
|
6200
|
+
#
|
6201
|
+
created=float,
|
6202
|
+
|
6203
|
+
# Millisecond portion of the time when the LogRecord was created.
|
6204
|
+
msecs=float,
|
6205
|
+
|
6206
|
+
# Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.
|
6207
|
+
relativeCreated=float,
|
6208
|
+
)
|
6108
6209
|
|
6109
|
-
|
6110
|
-
|
6111
|
-
|
6210
|
+
def _info_to_record(self, info: LoggingContextInfos.Time) -> ta.Mapping[str, ta.Any]:
|
6211
|
+
return dict(
|
6212
|
+
created=info.secs,
|
6213
|
+
msecs=info.msecs,
|
6214
|
+
relativeCreated=info.relative_secs,
|
6215
|
+
)
|
6112
6216
|
|
6113
|
-
|
6217
|
+
def record_to_info(self, rec: logging.LogRecord) -> LoggingContextInfos.Time:
|
6218
|
+
return LoggingContextInfos.Time.build(
|
6219
|
+
int(rec.created * 1e9),
|
6220
|
+
)
|
6114
6221
|
|
6115
|
-
|
6116
|
-
|
6117
|
-
|
6118
|
-
|
6222
|
+
class Exc(OptionalAdapter[LoggingContextInfos.Exc]):
|
6223
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Exc]] = LoggingContextInfos.Exc
|
6224
|
+
|
6225
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
6226
|
+
# Exception tuple (à la sys.exc_info) or, if no exception has occurred, None. Unmodified by ctor.
|
6227
|
+
exc_info=(ta.Optional[LoggingExcInfoTuple], None),
|
6228
|
+
|
6229
|
+
# Used to cache the traceback text. Simply set to None by ctor, later set by Formatter.format.
|
6230
|
+
exc_text=(ta.Optional[str], None),
|
6231
|
+
)
|
6232
|
+
|
6233
|
+
def _info_to_record(self, info: LoggingContextInfos.Exc) -> ta.Mapping[str, ta.Any]:
|
6234
|
+
return dict(
|
6235
|
+
exc_info=info.info_tuple,
|
6236
|
+
exc_text=None,
|
6237
|
+
)
|
6238
|
+
|
6239
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Exc]:
|
6240
|
+
# FIXME:
|
6241
|
+
# error: Argument 1 to "build" of "Exc" has incompatible type
|
6242
|
+
# "tuple[type[BaseException], BaseException, TracebackType | None] | tuple[None, None, None] | None"; expected # noqa
|
6243
|
+
# "BaseException | tuple[type[BaseException], BaseException, TracebackType | None] | bool | None" [arg-type] # noqa
|
6244
|
+
return LoggingContextInfos.Exc.build(rec.exc_info) # type: ignore[arg-type]
|
6245
|
+
|
6246
|
+
class Caller(OptionalAdapter[LoggingContextInfos.Caller]):
|
6247
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Caller]] = LoggingContextInfos.Caller
|
6248
|
+
|
6249
|
+
_UNKNOWN_PATH_NAME: ta.ClassVar[str] = '(unknown file)'
|
6250
|
+
_UNKNOWN_FUNC_NAME: ta.ClassVar[str] = '(unknown function)'
|
6251
|
+
|
6252
|
+
_STACK_INFO_PREFIX: ta.ClassVar[str] = 'Stack (most recent call last):\n'
|
6253
|
+
|
6254
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
6255
|
+
# Full pathname of the source file where the logging call was issued (if available). Unmodified by ctor. May
|
6256
|
+
# default to "(unknown file)" by Logger.findCaller / Logger._log.
|
6257
|
+
pathname=(str, _UNKNOWN_PATH_NAME),
|
6258
|
+
|
6259
|
+
# Source line number where the logging call was issued (if available). Unmodified by ctor. May default to 0
|
6260
|
+
# y Logger.findCaller / Logger._log.
|
6261
|
+
lineno=(int, 0),
|
6262
|
+
|
6263
|
+
# Name of function containing the logging call. Set by ctor to `func` arg, unmodified. May default to
|
6264
|
+
# "(unknown function)" by Logger.findCaller / Logger._log.
|
6265
|
+
funcName=(str, _UNKNOWN_FUNC_NAME),
|
6266
|
+
|
6267
|
+
# Stack frame information (where available) from the bottom of the stack in the current thread, up to and
|
6268
|
+
# including the stack frame of the logging call which resulted in the creation of this record. Set by ctor
|
6269
|
+
# to `sinfo` arg, unmodified. Mostly set, if requested, by `Logger.findCaller`, to
|
6270
|
+
# `traceback.print_stack(f)`, but prepended with the literal "Stack (most recent call last):\n", and
|
6271
|
+
# stripped of exactly one trailing `\n` if present.
|
6272
|
+
stack_info=(ta.Optional[str], None),
|
6273
|
+
)
|
6274
|
+
|
6275
|
+
def _info_to_record(self, caller: LoggingContextInfos.Caller) -> ta.Mapping[str, ta.Any]:
|
6276
|
+
if (sinfo := caller.stack_info) is not None:
|
6277
|
+
stack_info: ta.Optional[str] = '\n'.join([
|
6278
|
+
self._STACK_INFO_PREFIX,
|
6279
|
+
sinfo[1:] if sinfo.endswith('\n') else sinfo,
|
6280
|
+
])
|
6281
|
+
else:
|
6282
|
+
stack_info = None
|
6283
|
+
|
6284
|
+
return dict(
|
6285
|
+
pathname=caller.file_path,
|
6286
|
+
|
6287
|
+
lineno=caller.line_no,
|
6288
|
+
funcName=caller.func_name,
|
6289
|
+
|
6290
|
+
stack_info=stack_info,
|
6291
|
+
)
|
6292
|
+
|
6293
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Caller]:
|
6294
|
+
# FIXME: piecemeal?
|
6295
|
+
if (
|
6296
|
+
rec.pathname != self._UNKNOWN_PATH_NAME and
|
6297
|
+
rec.lineno != 0 and
|
6298
|
+
rec.funcName != self._UNKNOWN_FUNC_NAME
|
6299
|
+
):
|
6300
|
+
if (sinfo := rec.stack_info) is not None and sinfo.startswith(self._STACK_INFO_PREFIX):
|
6301
|
+
sinfo = sinfo[len(self._STACK_INFO_PREFIX):]
|
6302
|
+
return LoggingContextInfos.Caller(
|
6303
|
+
file_path=rec.pathname,
|
6304
|
+
|
6305
|
+
line_no=rec.lineno,
|
6306
|
+
func_name=rec.funcName,
|
6307
|
+
|
6308
|
+
stack_info=sinfo,
|
6309
|
+
)
|
6310
|
+
|
6311
|
+
return None
|
6312
|
+
|
6313
|
+
class SourceFile(Adapter[LoggingContextInfos.SourceFile]):
|
6314
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.SourceFile]] = LoggingContextInfos.SourceFile
|
6315
|
+
|
6316
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Any]] = dict(
|
6317
|
+
# Filename portion of pathname. Set to `os.path.basename(pathname)` if successful, otherwise defaults to
|
6318
|
+
# pathname.
|
6319
|
+
filename=str,
|
6320
|
+
|
6321
|
+
# Module (name portion of filename). Set to `os.path.splitext(filename)[0]`, otherwise defaults to
|
6322
|
+
# "Unknown module".
|
6323
|
+
module=str,
|
6324
|
+
)
|
6325
|
+
|
6326
|
+
_UNKNOWN_MODULE: ta.ClassVar[str] = 'Unknown module'
|
6327
|
+
|
6328
|
+
def context_to_record(self, ctx: LoggingContext) -> ta.Mapping[str, ta.Any]:
|
6329
|
+
if (info := ctx.get_info(LoggingContextInfos.SourceFile)) is not None:
|
6330
|
+
return dict(
|
6331
|
+
filename=info.file_name,
|
6332
|
+
module=info.module,
|
6333
|
+
)
|
6334
|
+
|
6335
|
+
if (caller := ctx.get_info(LoggingContextInfos.Caller)) is not None:
|
6336
|
+
return dict(
|
6337
|
+
filename=caller.file_path,
|
6338
|
+
module=self._UNKNOWN_MODULE,
|
6339
|
+
)
|
6119
6340
|
|
6120
|
-
|
6341
|
+
return dict(
|
6342
|
+
filename=LoggingContextInfoRecordAdapters.Caller._UNKNOWN_PATH_NAME, # noqa
|
6343
|
+
module=self._UNKNOWN_MODULE,
|
6344
|
+
)
|
6345
|
+
|
6346
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.SourceFile]:
|
6347
|
+
if (
|
6348
|
+
rec.module is not None and
|
6349
|
+
rec.module != self._UNKNOWN_MODULE
|
6350
|
+
):
|
6351
|
+
return LoggingContextInfos.SourceFile(
|
6352
|
+
file_name=rec.filename,
|
6353
|
+
module=rec.module, # FIXME: piecemeal?
|
6354
|
+
)
|
6355
|
+
|
6356
|
+
return None
|
6357
|
+
|
6358
|
+
class Thread(OptionalAdapter[LoggingContextInfos.Thread]):
|
6359
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Thread]] = LoggingContextInfos.Thread
|
6360
|
+
|
6361
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
6362
|
+
# Thread ID if available, and `logging.logThreads` is truthy.
|
6363
|
+
thread=(ta.Optional[int], None),
|
6364
|
+
|
6365
|
+
# Thread name if available, and `logging.logThreads` is truthy.
|
6366
|
+
threadName=(ta.Optional[str], None),
|
6367
|
+
)
|
6368
|
+
|
6369
|
+
def _info_to_record(self, info: LoggingContextInfos.Thread) -> ta.Mapping[str, ta.Any]:
|
6370
|
+
if logging.logThreads:
|
6371
|
+
return dict(
|
6372
|
+
thread=info.ident,
|
6373
|
+
threadName=info.name,
|
6374
|
+
)
|
6375
|
+
|
6376
|
+
return self.record_defaults
|
6377
|
+
|
6378
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Thread]:
|
6379
|
+
if (
|
6380
|
+
(ident := rec.thread) is not None and
|
6381
|
+
(name := rec.threadName) is not None
|
6382
|
+
):
|
6383
|
+
return LoggingContextInfos.Thread(
|
6384
|
+
ident=ident,
|
6385
|
+
native_id=None,
|
6386
|
+
name=name,
|
6387
|
+
)
|
6388
|
+
|
6389
|
+
return None
|
6390
|
+
|
6391
|
+
class Process(OptionalAdapter[LoggingContextInfos.Process]):
|
6392
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Process]] = LoggingContextInfos.Process
|
6393
|
+
|
6394
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
6395
|
+
# Process ID if available - that is, if `hasattr(os, 'getpid')` - and `logging.logProcesses` is truthy,
|
6396
|
+
# otherwise None.
|
6397
|
+
process=(ta.Optional[int], None),
|
6398
|
+
)
|
6399
|
+
|
6400
|
+
def _info_to_record(self, info: LoggingContextInfos.Process) -> ta.Mapping[str, ta.Any]:
|
6401
|
+
if logging.logProcesses:
|
6402
|
+
return dict(
|
6403
|
+
process=info.pid,
|
6404
|
+
)
|
6405
|
+
|
6406
|
+
return self.record_defaults
|
6407
|
+
|
6408
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Process]:
|
6409
|
+
if (
|
6410
|
+
(pid := rec.process) is not None
|
6411
|
+
):
|
6412
|
+
return LoggingContextInfos.Process(
|
6413
|
+
pid=pid,
|
6414
|
+
)
|
6415
|
+
|
6416
|
+
return None
|
6417
|
+
|
6418
|
+
class Multiprocessing(OptionalAdapter[LoggingContextInfos.Multiprocessing]):
|
6419
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Multiprocessing]] = LoggingContextInfos.Multiprocessing
|
6420
|
+
|
6421
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Tuple[ta.Any, ta.Any]]] = dict(
|
6422
|
+
# Process name if available. Set to None if `logging.logMultiprocessing` is not truthy. Otherwise, set to
|
6423
|
+
# 'MainProcess', then `sys.modules.get('multiprocessing').current_process().name` if that works, otherwise
|
6424
|
+
# remains as 'MainProcess'.
|
6425
|
+
#
|
6426
|
+
# As noted by stdlib:
|
6427
|
+
#
|
6428
|
+
# Errors may occur if multiprocessing has not finished loading yet - e.g. if a custom import hook causes
|
6429
|
+
# third-party code to run when multiprocessing calls import. See issue 8200 for an example
|
6430
|
+
#
|
6431
|
+
processName=(ta.Optional[str], None),
|
6432
|
+
)
|
6433
|
+
|
6434
|
+
def _info_to_record(self, info: LoggingContextInfos.Multiprocessing) -> ta.Mapping[str, ta.Any]:
|
6435
|
+
if logging.logMultiprocessing:
|
6436
|
+
return dict(
|
6437
|
+
processName=info.process_name,
|
6438
|
+
)
|
6439
|
+
|
6440
|
+
return self.record_defaults
|
6441
|
+
|
6442
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Multiprocessing]:
|
6443
|
+
if (
|
6444
|
+
(process_name := rec.processName) is not None
|
6445
|
+
):
|
6446
|
+
return LoggingContextInfos.Multiprocessing(
|
6447
|
+
process_name=process_name,
|
6448
|
+
)
|
6449
|
+
|
6450
|
+
return None
|
6451
|
+
|
6452
|
+
class AsyncioTask(OptionalAdapter[LoggingContextInfos.AsyncioTask]):
|
6453
|
+
info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.AsyncioTask]] = LoggingContextInfos.AsyncioTask
|
6454
|
+
|
6455
|
+
_record_attrs: ta.ClassVar[ta.Mapping[str, ta.Union[ta.Any, ta.Tuple[ta.Any, ta.Any]]]] = dict(
|
6456
|
+
# Absent <3.12, otherwise asyncio.Task name if available, and `logging.logAsyncioTasks` is truthy. Set to
|
6457
|
+
# `sys.modules.get('asyncio').current_task().get_name()`, otherwise None.
|
6458
|
+
taskName=(ta.Optional[str], None),
|
6459
|
+
)
|
6460
|
+
|
6461
|
+
def _info_to_record(self, info: LoggingContextInfos.AsyncioTask) -> ta.Mapping[str, ta.Any]:
|
6462
|
+
if getattr(logging, 'logAsyncioTasks', None): # Absent <3.12
|
6463
|
+
return dict(
|
6464
|
+
taskName=info.name,
|
6465
|
+
)
|
6466
|
+
|
6467
|
+
return self.record_defaults
|
6468
|
+
|
6469
|
+
def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.AsyncioTask]:
|
6470
|
+
if (
|
6471
|
+
(name := getattr(rec, 'taskName', None)) is not None
|
6472
|
+
):
|
6473
|
+
return LoggingContextInfos.AsyncioTask(
|
6474
|
+
name=name,
|
6475
|
+
)
|
6476
|
+
|
6477
|
+
return None
|
6478
|
+
|
6479
|
+
|
6480
|
+
_LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_: ta.Sequence[LoggingContextInfoRecordAdapters.Adapter] = [ # noqa
|
6481
|
+
LoggingContextInfoRecordAdapters.Name(),
|
6482
|
+
LoggingContextInfoRecordAdapters.Level(),
|
6483
|
+
LoggingContextInfoRecordAdapters.Msg(),
|
6484
|
+
LoggingContextInfoRecordAdapters.Time(),
|
6485
|
+
LoggingContextInfoRecordAdapters.Exc(),
|
6486
|
+
LoggingContextInfoRecordAdapters.Caller(),
|
6487
|
+
LoggingContextInfoRecordAdapters.SourceFile(),
|
6488
|
+
LoggingContextInfoRecordAdapters.Thread(),
|
6489
|
+
LoggingContextInfoRecordAdapters.Process(),
|
6490
|
+
LoggingContextInfoRecordAdapters.Multiprocessing(),
|
6491
|
+
LoggingContextInfoRecordAdapters.AsyncioTask(),
|
6492
|
+
]
|
6493
|
+
|
6494
|
+
_LOGGING_CONTEXT_INFO_RECORD_ADAPTERS: ta.Mapping[ta.Type[LoggingContextInfo], LoggingContextInfoRecordAdapters.Adapter] = { # noqa
|
6495
|
+
ad.info_cls: ad for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_
|
6496
|
+
}
|
6497
|
+
|
6498
|
+
|
6499
|
+
##
|
6121
6500
|
|
6122
6501
|
|
6123
6502
|
# Formatter:
|
6124
6503
|
# - https://github.com/python/cpython/blob/39b2f82717a69dde7212bc39b673b0f55c99e6a3/Lib/logging/__init__.py#L514 (3.8)
|
6125
6504
|
# - https://github.com/python/cpython/blob/f070f54c5f4a42c7c61d1d5d3b8f3b7203b4a0fb/Lib/logging/__init__.py#L554 (~3.14) # noqa
|
6126
6505
|
#
|
6127
|
-
|
6506
|
+
_KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
|
6128
6507
|
# The logged message, computed as msg % args. Set to `record.getMessage()`.
|
6129
6508
|
message=str,
|
6130
6509
|
|
@@ -6138,20 +6517,31 @@ KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
|
|
6138
6517
|
exc_text=ta.Optional[str],
|
6139
6518
|
)
|
6140
6519
|
|
6141
|
-
KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
|
6142
|
-
|
6143
6520
|
|
6144
6521
|
##
|
6145
6522
|
|
6146
6523
|
|
6524
|
+
_KNOWN_STD_LOGGING_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(
|
6525
|
+
a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs
|
6526
|
+
)
|
6527
|
+
|
6528
|
+
_KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(_KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
|
6529
|
+
|
6530
|
+
|
6147
6531
|
class UnknownStdLoggingRecordAttrsWarning(LoggingSetupWarning):
|
6148
6532
|
pass
|
6149
6533
|
|
6150
6534
|
|
6151
6535
|
def _check_std_logging_record_attrs() -> None:
|
6536
|
+
if (
|
6537
|
+
len([a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs]) !=
|
6538
|
+
len(_KNOWN_STD_LOGGING_RECORD_ATTR_SET)
|
6539
|
+
):
|
6540
|
+
raise RuntimeError('Duplicate LoggingContextInfoRecordAdapter record attrs')
|
6541
|
+
|
6152
6542
|
rec_dct = dict(logging.makeLogRecord({}).__dict__)
|
6153
6543
|
|
6154
|
-
if (unk_rec_fields := frozenset(rec_dct) -
|
6544
|
+
if (unk_rec_fields := frozenset(rec_dct) - _KNOWN_STD_LOGGING_RECORD_ATTR_SET):
|
6155
6545
|
import warnings # noqa
|
6156
6546
|
|
6157
6547
|
warnings.warn(
|
@@ -6167,116 +6557,43 @@ _check_std_logging_record_attrs()
|
|
6167
6557
|
|
6168
6558
|
|
6169
6559
|
class LoggingContextLogRecord(logging.LogRecord):
|
6170
|
-
|
6560
|
+
# LogRecord.__init__ args:
|
6561
|
+
# - name: str
|
6562
|
+
# - level: int
|
6563
|
+
# - pathname: str - Confusingly referred to as `fn` before the LogRecord ctor. May be empty or "(unknown file)".
|
6564
|
+
# - lineno: int - May be 0.
|
6565
|
+
# - msg: str
|
6566
|
+
# - args: tuple | dict | 1-tuple[dict]
|
6567
|
+
# - exc_info: LoggingExcInfoTuple | None
|
6568
|
+
# - func: str | None = None -> funcName
|
6569
|
+
# - sinfo: str | None = None -> stack_info
|
6171
6570
|
|
6172
|
-
|
6173
|
-
|
6174
|
-
_UNKNOWN_MODULE: ta.ClassVar[str] = 'Unknown module'
|
6571
|
+
def __init__(self, *, _logging_context: LoggingContext) -> None: # noqa
|
6572
|
+
self._logging_context = _logging_context
|
6175
6573
|
|
6176
|
-
|
6574
|
+
for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_:
|
6575
|
+
self.__dict__.update(ad.context_to_record(_logging_context))
|
6177
6576
|
|
6178
|
-
def __init__( # noqa
|
6179
|
-
self,
|
6180
|
-
# name,
|
6181
|
-
# level,
|
6182
|
-
# pathname,
|
6183
|
-
# lineno,
|
6184
|
-
# msg,
|
6185
|
-
# args,
|
6186
|
-
# exc_info,
|
6187
|
-
# func=None,
|
6188
|
-
# sinfo=None,
|
6189
|
-
# **kwargs,
|
6190
|
-
*,
|
6191
|
-
name: str,
|
6192
|
-
msg: str,
|
6193
|
-
args: ta.Union[tuple, dict],
|
6194
6577
|
|
6195
|
-
|
6196
|
-
) -> None:
|
6197
|
-
ctx = _logging_context
|
6198
|
-
|
6199
|
-
self.name: str = name
|
6200
|
-
|
6201
|
-
self.msg: str = msg
|
6202
|
-
|
6203
|
-
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L307
|
6204
|
-
if args and len(args) == 1 and isinstance(args[0], collections.abc.Mapping) and args[0]:
|
6205
|
-
args = args[0] # type: ignore[assignment]
|
6206
|
-
self.args: ta.Union[tuple, dict] = args
|
6207
|
-
|
6208
|
-
self.levelname: str = logging.getLevelName(ctx.level)
|
6209
|
-
self.levelno: int = ctx.level
|
6210
|
-
|
6211
|
-
if (caller := ctx.caller()) is not None:
|
6212
|
-
self.pathname: str = caller.file_path
|
6213
|
-
else:
|
6214
|
-
self.pathname = self._UNKNOWN_PATH_NAME
|
6215
|
-
|
6216
|
-
if (src_file := ctx.source_file()) is not None:
|
6217
|
-
self.filename: str = src_file.file_name
|
6218
|
-
self.module: str = src_file.module
|
6219
|
-
else:
|
6220
|
-
self.filename = self.pathname
|
6221
|
-
self.module = self._UNKNOWN_MODULE
|
6222
|
-
|
6223
|
-
self.exc_info: ta.Optional[LoggingExcInfoTuple] = ctx.exc_info_tuple
|
6224
|
-
self.exc_text: ta.Optional[str] = None
|
6225
|
-
|
6226
|
-
# If ctx.build_caller() was never called, we simply don't have a stack trace.
|
6227
|
-
if caller is not None:
|
6228
|
-
if (sinfo := caller.stack_info) is not None:
|
6229
|
-
self.stack_info: ta.Optional[str] = '\n'.join([
|
6230
|
-
self._STACK_INFO_PREFIX,
|
6231
|
-
sinfo[1:] if sinfo.endswith('\n') else sinfo,
|
6232
|
-
])
|
6233
|
-
else:
|
6234
|
-
self.stack_info = None
|
6235
|
-
|
6236
|
-
self.lineno: int = caller.line_no
|
6237
|
-
self.funcName: str = caller.name
|
6238
|
-
|
6239
|
-
else:
|
6240
|
-
self.stack_info = None
|
6241
|
-
|
6242
|
-
self.lineno = 0
|
6243
|
-
self.funcName = self._UNKNOWN_FUNC_NAME
|
6578
|
+
##
|
6244
6579
|
|
6245
|
-
times = ctx.times
|
6246
|
-
self.created: float = times.created
|
6247
|
-
self.msecs: float = times.msecs
|
6248
|
-
self.relativeCreated: float = times.relative_created
|
6249
6580
|
|
6250
|
-
|
6251
|
-
|
6252
|
-
|
6253
|
-
|
6254
|
-
|
6255
|
-
self.thread = None
|
6256
|
-
self.threadName = None
|
6581
|
+
@ta.final
|
6582
|
+
class LogRecordLoggingContext(LoggingContext):
|
6583
|
+
def __init__(self, rec: logging.LogRecord) -> None:
|
6584
|
+
if isinstance(rec, LoggingContextLogRecord):
|
6585
|
+
raise TypeError(rec)
|
6257
6586
|
|
6258
|
-
|
6259
|
-
process = check.not_none(ctx.process())
|
6260
|
-
self.process: ta.Optional[int] = process.pid
|
6261
|
-
else:
|
6262
|
-
self.process = None
|
6587
|
+
self._rec = rec
|
6263
6588
|
|
6264
|
-
|
6265
|
-
|
6266
|
-
|
6267
|
-
|
6268
|
-
|
6269
|
-
else:
|
6270
|
-
self.processName = None
|
6589
|
+
self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {
|
6590
|
+
type(info): info
|
6591
|
+
for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_
|
6592
|
+
if (info := ad.record_to_info(rec)) is not None
|
6593
|
+
}
|
6271
6594
|
|
6272
|
-
|
6273
|
-
|
6274
|
-
if (at := ctx.asyncio_task()) is not None:
|
6275
|
-
self.taskName: ta.Optional[str] = at.name
|
6276
|
-
else:
|
6277
|
-
self.taskName = None
|
6278
|
-
else:
|
6279
|
-
self.taskName = None
|
6595
|
+
def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
|
6596
|
+
return self._infos.get(ty)
|
6280
6597
|
|
6281
6598
|
|
6282
6599
|
########################################
|
@@ -6303,21 +6620,20 @@ class StdLogger(Logger):
|
|
6303
6620
|
return self._std.getEffectiveLevel()
|
6304
6621
|
|
6305
6622
|
def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> None:
|
6306
|
-
if not self.is_enabled_for(ctx.level):
|
6623
|
+
if not self.is_enabled_for(ctx.must_get_info(LoggingContextInfos.Level).level):
|
6307
6624
|
return
|
6308
6625
|
|
6309
|
-
ctx.
|
6310
|
-
|
6311
|
-
ms, args = self._prepare_msg_args(msg, *args)
|
6312
|
-
|
6313
|
-
rec = LoggingContextLogRecord(
|
6626
|
+
ctx.set_basic(
|
6314
6627
|
name=self._std.name,
|
6315
|
-
msg=ms,
|
6316
|
-
args=args,
|
6317
6628
|
|
6318
|
-
|
6629
|
+
msg=msg,
|
6630
|
+
args=args,
|
6319
6631
|
)
|
6320
6632
|
|
6633
|
+
ctx.capture()
|
6634
|
+
|
6635
|
+
rec = LoggingContextLogRecord(_logging_context=ctx)
|
6636
|
+
|
6321
6637
|
self._std.handle(rec)
|
6322
6638
|
|
6323
6639
|
|