reykit 1.0.0__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.
reykit/rlog.py ADDED
@@ -0,0 +1,1061 @@
1
+ # !/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ @Time : 2023-10-08 21:26:43
6
+ @Author : Rey
7
+ @Contact : reyxbo@163.com
8
+ @Explain : Log methods.
9
+ """
10
+
11
+
12
+ from __future__ import annotations
13
+ from typing import Any, Optional, Union, Literal, Final, NoReturn, overload, override
14
+ from collections.abc import Callable
15
+ from queue import Queue
16
+ from os.path import abspath as os_abspath
17
+ from logging import (
18
+ getLogger,
19
+ Handler,
20
+ StreamHandler,
21
+ Formatter,
22
+ Filter,
23
+ LogRecord,
24
+ DEBUG as LDEBUG,
25
+ INFO as LINFO,
26
+ WARNING as LWARNING,
27
+ ERROR as LERROR,
28
+ CRITICAL as LCRITICAL
29
+ )
30
+ from logging.handlers import QueueHandler
31
+ from concurrent_log_handler import ConcurrentRotatingFileHandler, ConcurrentTimedRotatingFileHandler
32
+
33
+ from .rexception import throw, catch_exc
34
+ from .ros import RFile
35
+ from .rregex import search, sub
36
+ from .rstdout import RConfigStdout, modify_print, reset_print
37
+ from .rsystem import get_first_notnull, get_stack_param
38
+ from .rtext import to_text
39
+ from .rtime import now, time_to
40
+ from .rtype import RConfigMeta
41
+ from .rwrap import wrap_thread
42
+
43
+
44
+ __all__ = (
45
+ 'RConfigLog',
46
+ 'RLog',
47
+ 'RRecord'
48
+ )
49
+
50
+
51
+ class RConfigLog(object, metaclass=RConfigMeta):
52
+ """
53
+ Rey's `config log` type.
54
+ """
55
+
56
+ # Module path.
57
+ path_rlog: Final[str] = os_abspath(__file__)
58
+
59
+
60
+ class RLog(object):
61
+ """
62
+ Rey's `log` type.
63
+ """
64
+
65
+ # Status.
66
+ print_replaced: bool = False
67
+
68
+ # Default value.
69
+ default_format = (
70
+ '%(format_time)s | '
71
+ '%(format_levelname)s | '
72
+ '%(format_path)s | '
73
+ '%(format_message)s'
74
+ )
75
+ default_format_date = '%Y-%m-%d %H:%M:%S'
76
+ default_format_width = 100
77
+
78
+ # Whether print colour.
79
+ print_colour: bool = True
80
+
81
+ # Level.
82
+ DEBUG = LDEBUG
83
+ INFO = LINFO
84
+ WARNING = LWARNING
85
+ ERROR = LERROR
86
+ CRITICAL = LCRITICAL
87
+
88
+
89
+ def __init__(
90
+ self,
91
+ name: str = 'Log'
92
+ ) -> None:
93
+ """
94
+ Build `log` attributes.
95
+
96
+ Parameters
97
+ ----------
98
+ name : Log name. When log name existed, then direct return, otherwise build.
99
+ """
100
+
101
+ # Set attribute.
102
+ self.name: Final[str] = name
103
+ self.stoped = False
104
+
105
+ # Get logger.
106
+ self.logger = getLogger(name)
107
+
108
+ # Set level.
109
+ self.logger.setLevel(self.DEBUG)
110
+
111
+
112
+ def _get_message_stack(self) -> dict:
113
+ """
114
+ Get message stack parameters.
115
+
116
+ Returns
117
+ -------
118
+ Stack parameters.
119
+ """
120
+
121
+ # Get parameter.
122
+ stack_params = get_stack_param('full', 12)
123
+ stack_param = stack_params[-1]
124
+
125
+ # Compatible.
126
+
127
+ ## Compatible '__call__'.
128
+ if (
129
+ stack_param['filename'] == RConfigLog.path_rlog
130
+ and stack_param['name'] in ('debug', 'info', 'warning', 'error', 'critical')
131
+ ):
132
+ stack_param = stack_params[-2]
133
+
134
+ ## Compatible 'print'.
135
+ if (
136
+ stack_param['filename'] == RConfigLog.path_rlog
137
+ and stack_param['name'] == 'preprocess'
138
+ ):
139
+ stack_param = stack_params[-3]
140
+
141
+ ## Compatible 'echo'.
142
+ if (
143
+ stack_param['filename'] == RConfigStdout._path_rstdout
144
+ and stack_param['name'] == 'echo'
145
+ ):
146
+ stack_param = stack_params[-4]
147
+
148
+ return stack_param
149
+
150
+
151
+ def _supply_format_standard(
152
+ self,
153
+ format_: str,
154
+ record: LogRecord
155
+ ) -> None:
156
+ """
157
+ Supply format standard parameters.
158
+
159
+ Parameters
160
+ ----------
161
+ format_ : Record format.
162
+ record : Log record instance.
163
+ """
164
+
165
+ # Format 'format_time'.
166
+ if '%(format_time)s' in format_:
167
+ datetime = now()
168
+ datetime_str = time_to(datetime, True)
169
+ record.format_time = datetime_str[:-3]
170
+
171
+ # Format 'format_levelname'.
172
+ if '%(format_levelname)s' in format_:
173
+ record.format_levelname = record.levelname.ljust(8)
174
+
175
+ # Format 'format_path'.
176
+ if '%(format_path)s' in format_:
177
+ message_stack = self._get_message_stack()
178
+ record.format_path = '%s:%s' % (
179
+ message_stack['filename'],
180
+ message_stack['lineno']
181
+ )
182
+
183
+ # Format 'format_message'.
184
+ if '%(format_message)s' in format_:
185
+ record.format_message = record.getMessage()
186
+
187
+
188
+ def get_level_color_ansi(
189
+ self,
190
+ level: int
191
+ ) -> str:
192
+ """
193
+ Get level color `ANSI` code.
194
+
195
+ Parameters
196
+ ----------
197
+ level : Record level.
198
+
199
+ Returns
200
+ -------
201
+ Level color ansi code.
202
+ """
203
+
204
+ # Set parameters.
205
+ color_code_dict = {
206
+ 10: '\033[1;34m',
207
+ 20: '\033[1;37m',
208
+ 30: '\033[1;33m',
209
+ 40: '\033[1;31m',
210
+ 50: '\033[1;37;41m'
211
+ }
212
+
213
+ # Get.
214
+ color_code = color_code_dict.get(level, '')
215
+
216
+ return color_code
217
+
218
+
219
+ def _supply_format_print(
220
+ self,
221
+ format_: str,
222
+ record: LogRecord
223
+ ) -> None:
224
+ """
225
+ Supply format print parameters.
226
+
227
+ Parameters
228
+ ----------
229
+ format_ : Record format.
230
+ record : Log record instance.
231
+ """
232
+
233
+ # Break.
234
+
235
+ ## Switch.
236
+ if not self.print_colour: return
237
+
238
+ ## Added.
239
+ pattern = '\033\\[[\\d;]+?m'
240
+ result = search(pattern, format_)
241
+ if result is not None: return
242
+
243
+ # 'format_time'.
244
+ if '%(format_time)s' in format_:
245
+ record.format_time = '\033[32m%s\033[0m' % record.format_time
246
+
247
+ # 'format_levelname'.
248
+ if '%(format_levelname)s' in format_:
249
+ level_color_code = self.get_level_color_ansi(record.levelno)
250
+ record.format_levelname = '%s%s\033[0m' % (
251
+ level_color_code,
252
+ record.format_levelname
253
+ )
254
+
255
+ # 'format_path'.
256
+ if '%(format_path)s' in format_:
257
+ record.format_path = '\033[36m%s\033[0m' % record.format_path
258
+
259
+ # 'format_message'.
260
+ if (
261
+ '%(format_message)s' in format_
262
+ and search('\033\\[[\\d;]+?m', record.format_message) is None
263
+ ):
264
+ level_color_code = self.get_level_color_ansi(record.levelno)
265
+ record.format_message = '%s%s\033[0m' % (
266
+ level_color_code,
267
+ record.format_message
268
+ )
269
+
270
+
271
+ def _supply_format_file(
272
+ self,
273
+ format_: str,
274
+ record: LogRecord
275
+ ) -> None:
276
+ """
277
+ Supply format file parameters.
278
+
279
+ Parameters
280
+ ----------
281
+ format_ : Record format.
282
+ record : Log record instance.
283
+ """
284
+
285
+ # Format 'format_message'.
286
+ if '%(format_message)s' in format_:
287
+ pattern = '\033\\[[\\d;]+?m'
288
+ record.format_message = sub(pattern, record.format_message)
289
+
290
+
291
+ def get_default_filter_method(
292
+ self,
293
+ format_: str,
294
+ mode : Optional[Literal['print', 'file']] = None
295
+ ) -> Callable[[LogRecord], Literal[True]]:
296
+ """
297
+ Get default filter method of handler.
298
+
299
+ Parameters
300
+ ----------
301
+ format_ : Record format.
302
+ mode : Handler mode.
303
+ - `None`: Standard filter method.
304
+ - `Literal['print']`: Print handler filter method.
305
+ - `Literal['file']`: File handler filter method.
306
+
307
+ Returns
308
+ -------
309
+ Filter method.
310
+ """
311
+
312
+
313
+ # Define.
314
+ def default_filter_method(
315
+ record: LogRecord
316
+ ) -> Literal[True]:
317
+ """
318
+ Default filter method of handler.
319
+
320
+ Parameters
321
+ ----------
322
+ record : Log record instance.
323
+
324
+ Returns
325
+ -------
326
+ Whether pass.
327
+ """
328
+
329
+ # Format standard.
330
+ self._supply_format_standard(format_, record)
331
+
332
+ match mode:
333
+
334
+ # Format print.
335
+ case 'print':
336
+ self._supply_format_print(format_, record)
337
+
338
+ # Format file.
339
+ case 'file':
340
+ self._supply_format_file(format_, record)
341
+
342
+ return True
343
+
344
+
345
+ return default_filter_method
346
+
347
+
348
+ def get_filter(
349
+ self,
350
+ method: Callable[[LogRecord], bool]
351
+ ) -> Filter:
352
+ """
353
+ Get filter.
354
+
355
+ Parameters
356
+ ----------
357
+ method : Filter method.
358
+
359
+ Returns
360
+ -------
361
+ Filter.
362
+ """
363
+
364
+
365
+ # Define.
366
+ class RFilter(Filter):
367
+ """
368
+ Rey's filter type.
369
+ """
370
+
371
+
372
+ @override
373
+ def filter(
374
+ record: LogRecord
375
+ ) -> Literal[True]:
376
+ """
377
+ Filter method.
378
+
379
+ Parameters
380
+ ----------
381
+ record : Log record instance.
382
+
383
+ Returns
384
+ -------
385
+ Whether pass.
386
+ """
387
+
388
+ # Filter.
389
+ result = method(record)
390
+
391
+ return result
392
+
393
+
394
+ return RFilter
395
+
396
+
397
+ def add_print(
398
+ self,
399
+ level: int = DEBUG,
400
+ format_: Optional[str] = None,
401
+ filter_: Optional[Callable[[LogRecord], bool]] = None
402
+ ) -> StreamHandler:
403
+ """
404
+ Add print output record handler.
405
+
406
+ Parameters
407
+ ----------
408
+ level : Handler level.
409
+ format_ : Record format.
410
+ - `None`: Use attribute `default_format`.
411
+ - `str`: Use this value.
412
+ `Contain 'format_time'`: Date and time and millisecond, print output with color.
413
+ `Contain 'format_levelname'`: Level name and fixed width, print output with color.
414
+ `Contain 'format_path'`: Record code path, print output with color.
415
+ `Contain 'format_message'`: message content, file output delete ANSI code, print outputwith color.
416
+ filter_ : Filter method. The parameter is the `LogRecord` instance, return is `bool`.
417
+ - `None`: Use default filter method.
418
+ - `Callable`: Use this method.
419
+
420
+ Returns
421
+ -------
422
+ Handler.
423
+ """
424
+
425
+ # Get parameter.
426
+ format_ = get_first_notnull(format_, self.default_format, default='exception')
427
+ filter_ = filter_ or self.get_default_filter_method(format_, 'print')
428
+
429
+ # Create handler.
430
+ handler = StreamHandler()
431
+ handler.setLevel(level)
432
+ formatter = Formatter(format_, self.default_format_date)
433
+ handler.setFormatter(formatter)
434
+ handler_filter = self.get_filter(filter_)
435
+ handler.addFilter(handler_filter)
436
+
437
+ # Add.
438
+ self.logger.addHandler(handler)
439
+
440
+ return handler
441
+
442
+
443
+ @overload
444
+ def add_file(
445
+ self,
446
+ path: Optional[str] = None,
447
+ mb: Optional[float] = None,
448
+ time: None = None,
449
+ level: int = DEBUG,
450
+ format_: Optional[str] = None,
451
+ filter_: Optional[Callable[[LogRecord], bool]] = None
452
+ ) -> ConcurrentRotatingFileHandler: ...
453
+
454
+ @overload
455
+ def add_file(
456
+ self,
457
+ path: Optional[str] = None,
458
+ mb: None = None,
459
+ time: Union[float, Literal['m', 'w0', 'w1', 'w2', 'w3', 'w4', 'w5', 'w6']] = None,
460
+ level: int = DEBUG,
461
+ format_: Optional[str] = None,
462
+ filter_: Optional[Callable[[LogRecord], bool]] = None
463
+ ) -> ConcurrentTimedRotatingFileHandler: ...
464
+
465
+ @overload
466
+ def add_file(
467
+ self,
468
+ path: Optional[str] = None,
469
+ mb: None = None,
470
+ time: Any = None,
471
+ level: int = DEBUG,
472
+ format_: Optional[str] = None,
473
+ filter_: Optional[Callable[[LogRecord], bool]] = None
474
+ ) -> NoReturn: ...
475
+
476
+ @overload
477
+ def add_file(
478
+ self,
479
+ path: Optional[str] = None,
480
+ mb: float = None,
481
+ time: Union[float, Literal['m', 'w0', 'w1', 'w2', 'w3', 'w4', 'w5', 'w6']] = None,
482
+ level: int = DEBUG,
483
+ format_: Optional[str] = None,
484
+ filter_: Optional[Callable[[LogRecord], bool]] = None
485
+ ) -> NoReturn: ...
486
+
487
+ def add_file(
488
+ self,
489
+ path: Optional[str] = None,
490
+ mb: Optional[float] = None,
491
+ time: Optional[Union[float, Literal['m', 'w0', 'w1', 'w2', 'w3', 'w4', 'w5', 'w6']]] = None,
492
+ level: int = DEBUG,
493
+ format_: Optional[str] = None,
494
+ filter_: Optional[Callable[[LogRecord], bool]] = None
495
+ ) -> Union[ConcurrentRotatingFileHandler, ConcurrentTimedRotatingFileHandler]:
496
+ """
497
+ Add file output record handler, can split files based on size or time.
498
+
499
+ Parameters
500
+ ----------
501
+ path : File path.
502
+ - `None`: Use attribute `self.name`.
503
+ - `str`: Use this value.
504
+ mb : File split condition, max megabyte. Conflict with parameter `time`. Cannot be less than 1, prevent infinite split file.
505
+ time : File split condition, interval time. Conflict with parameter `mb`.
506
+ - `float`: Interval hours.
507
+ - `Literal['m']`: Everyday midnight.
508
+ - `Literal['w0', 'w1', 'w2', 'w3', 'w4', 'w5', 'w6']`: Weekly midnight, 'w0' is monday, 'w6' is sunday, and so on.
509
+ level : Handler level.
510
+ format_ : Record format.
511
+ - `None`: Use attribute `default_format`.
512
+ - `str`: Use this value.
513
+ `Contain 'format_time'`: Date and time and millisecond, print output with color.
514
+ `Contain 'format_levelname'`: Level name and fixed width, print output with color.
515
+ `Contain 'format_path'`: Record code path, print output with color.
516
+ `Contain 'format_message'`: message content, file output delete ANSI code, print outputwith color.
517
+ filter_ : Filter method. The parameter is the `LogRecord` instance, return is `bool`.
518
+ - `None`: Use default filter method.
519
+ - `Callable`: Use this method.
520
+
521
+ Returns
522
+ -------
523
+ Handler.
524
+ """
525
+
526
+ # Get parameter.
527
+ format_ = get_first_notnull(format_, self.default_format, default='exception')
528
+ path = path or self.name
529
+ filter_ = filter_ or self.get_default_filter_method(format_, 'file')
530
+
531
+ # Create handler.
532
+
533
+ ## Throw exception.
534
+ if (
535
+ mb is not None
536
+ and time is not None
537
+ ):
538
+ raise AssertionError('parameter "mb" and "time" cannot be used together')
539
+
540
+ ## By size split.
541
+ elif mb is not None:
542
+
543
+ ### Check.
544
+ if mb < 1:
545
+ throw(ValueError, mb)
546
+
547
+ byte = int(mb * 1024 * 1024)
548
+ handler = ConcurrentRotatingFileHandler(
549
+ path,
550
+ 'a',
551
+ byte,
552
+ 1_0000_0000,
553
+ delay=True
554
+ )
555
+
556
+ ## By time split.
557
+ elif time is not None:
558
+ match time:
559
+
560
+ ### Interval hours.
561
+ case int() | float():
562
+ second = int(time * 60 * 60)
563
+ handler = ConcurrentTimedRotatingFileHandler(
564
+ path,
565
+ 'S',
566
+ second,
567
+ 1_0000_0000,
568
+ delay=True
569
+ )
570
+
571
+ ### Everyday midnight.
572
+ case 'm':
573
+ handler = ConcurrentTimedRotatingFileHandler(
574
+ path,
575
+ 'MIDNIGHT',
576
+ backupCount=1_0000_0000,
577
+ delay=True
578
+ )
579
+
580
+ ### Weekly midnight
581
+ case 'w0' | 'w1' | 'w2' | 'w3' | 'w4' | 'w5' | 'w6':
582
+ handler = ConcurrentTimedRotatingFileHandler(
583
+ path,
584
+ time,
585
+ backupCount=1_0000_0000,
586
+ delay=True
587
+ )
588
+
589
+ ### Throw exception.
590
+ case _:
591
+ throw(ValueError, time)
592
+
593
+ ## Not split.
594
+ else:
595
+ handler = ConcurrentRotatingFileHandler(
596
+ path,
597
+ 'a',
598
+ delay=True
599
+ )
600
+
601
+ # Set handler.
602
+ handler.setLevel(level)
603
+ formatter = Formatter(format_, self.default_format_date)
604
+ handler.setFormatter(formatter)
605
+ handler_filter = self.get_filter(filter_)
606
+ handler.addFilter(handler_filter)
607
+
608
+ # Add.
609
+ self.logger.addHandler(handler)
610
+
611
+ return handler
612
+
613
+
614
+ def add_queue(
615
+ self,
616
+ queue: Optional[Queue] = None,
617
+ level: int = DEBUG,
618
+ filter_: Optional[Callable[[LogRecord], bool]] = None
619
+ ) -> tuple[QueueHandler, Queue[LogRecord]]:
620
+ """
621
+ Add queue output record handler.
622
+
623
+ Parameters
624
+ ----------
625
+ queue : Queue instance.
626
+ - `None`: Create queue and use.
627
+ - `Queue`: Use this queue.
628
+ level : Handler level.
629
+ filter_ : Filter method. The parameter is the `LogRecord` instance, return is `bool`.
630
+
631
+ Returns
632
+ -------
633
+ Handler and queue.
634
+ """
635
+
636
+ ## Create queue.
637
+ queue = queue or Queue()
638
+
639
+ # Create handler.
640
+ handler = QueueHandler(queue)
641
+
642
+ # Set handler.
643
+ handler.setLevel(level)
644
+ if filter_ is not None:
645
+ handler_filter = self.get_filter(filter_)
646
+ handler.addFilter(handler_filter)
647
+
648
+ # Add.
649
+ self.logger.addHandler(handler)
650
+
651
+ return handler, queue
652
+
653
+
654
+ def add_handler(
655
+ self,
656
+ method: Callable[[LogRecord], Any],
657
+ level: int = DEBUG,
658
+ filter_: Optional[Callable[[LogRecord], bool]] = None
659
+ ) -> None:
660
+ """
661
+ Add method record handler.
662
+
663
+ Parameters
664
+ ----------
665
+ method : Handler method. The parameter is the `LogRecord` instance.
666
+ level : Handler level.
667
+ filter_ : Filter method. The parameter is the `LogRecord` instance, return is `bool`.
668
+ """
669
+
670
+ # Add queue out.
671
+ _, queue = self.add_queue(level=level, filter_=filter_)
672
+
673
+
674
+ # Define.
675
+ @wrap_thread
676
+ def execute() -> None:
677
+ """
678
+ Execute method.
679
+ """
680
+
681
+ while True:
682
+ record = queue.get()
683
+ method(record)
684
+
685
+
686
+ # Execute.
687
+ execute()
688
+
689
+
690
+ def delete_handler(
691
+ self,
692
+ handler: Handler
693
+ ) -> None:
694
+ """
695
+ Delete record handler.
696
+
697
+ Parameters
698
+ ----------
699
+ handler : Handler.
700
+ """
701
+
702
+ # Delete.
703
+ self.logger.removeHandler(handler)
704
+
705
+
706
+ def clear_handler(self) -> None:
707
+ """
708
+ Delete all record handler.
709
+ """
710
+
711
+ # Delete.
712
+ for handle in self.logger.handlers:
713
+ self.logger.removeHandler(handle)
714
+
715
+
716
+ def catch_print(self, printing: bool = True) -> None:
717
+ """
718
+ Catch print to log.
719
+
720
+ Parameters
721
+ ----------
722
+ printing : Whether to still print.
723
+ """
724
+
725
+
726
+ # Define.
727
+ def preprocess(__s: str) -> str:
728
+ """
729
+ Preprocess function.
730
+
731
+ Parameters
732
+ ----------
733
+ __s : Standard ouput text.
734
+
735
+ Returns
736
+ -------
737
+ Preprocessed text.
738
+ """
739
+
740
+ # Log.
741
+ if __s not in ('\n', ' ', ''):
742
+ self(__s, level=self.INFO, catch=False)
743
+
744
+ # Print.
745
+ if printing:
746
+ return __s
747
+
748
+
749
+ # Modify.
750
+ modify_print(preprocess)
751
+
752
+ # Update status.
753
+ self.print_replaced = True
754
+
755
+
756
+ def reset_print(self) -> None:
757
+ """
758
+ Reset log replace print.
759
+ """
760
+
761
+ # Break.
762
+ if not self.print_replaced: return
763
+
764
+ # Reset.
765
+ reset_print()
766
+
767
+ # Update status.
768
+ self.print_replaced = False
769
+
770
+
771
+ def log(
772
+ self,
773
+ *messages: Any,
774
+ level: Optional[int] = None,
775
+ catch: bool = True,
776
+ **params: Any
777
+ ) -> None:
778
+ """
779
+ Record log.
780
+
781
+ Parameters
782
+ ----------
783
+ messages : Record content.
784
+ level : Record level.
785
+ - `None`: Automatic judge.
786
+ `in 'except' syntax`: Use 'ERROR' level.
787
+ `Other`: Use 'INFO' level.
788
+ - `int`: Use this value.
789
+ catch : Whether catch and append exception stack.
790
+ params : Record Format parameters.
791
+ """
792
+
793
+ # Get parameter.
794
+ if (
795
+ level is None
796
+ or catch
797
+ ):
798
+ exc_report, exc_type, *_ = catch_exc()
799
+
800
+ ## Messages.
801
+ messages_len = len(messages)
802
+ if messages_len == 0:
803
+ messages = [None]
804
+
805
+ ## Level.
806
+ if level is None:
807
+ if exc_type is None:
808
+ level = self.INFO
809
+ else:
810
+ level = self.ERROR
811
+
812
+ ## Messages.
813
+ messages = '\n'.join(
814
+ [
815
+ to_text(message, self.default_format_width)
816
+ for message in messages
817
+ ]
818
+ )
819
+ if '\n' in messages:
820
+ messages = '\n' + messages
821
+
822
+ ### Exception.
823
+ if (
824
+ catch
825
+ and exc_type is not None
826
+ ):
827
+ messages = '%s\n%s' % (
828
+ messages,
829
+ exc_report
830
+ )
831
+
832
+ # Record.
833
+ self.logger.log(level, messages, extra=params)
834
+
835
+
836
+ def debug(
837
+ self,
838
+ *messages: Any,
839
+ **params: Any
840
+ ) -> None:
841
+ """
842
+ Record `debug` level log.
843
+
844
+ Parameters
845
+ ----------
846
+ messages : Record content.
847
+ params : Record Format parameters.
848
+ """
849
+
850
+ # Record.
851
+ self.log(*messages, level=self.DEBUG, **params)
852
+
853
+
854
+ def info(
855
+ self,
856
+ *messages: Any,
857
+ **params: Any
858
+ ) -> None:
859
+ """
860
+ Record `info` level log.
861
+
862
+ Parameters
863
+ ----------
864
+ messages : Record content.
865
+ params : Record Format parameters.
866
+ """
867
+
868
+ # Record.
869
+ self.log(*messages, level=self.INFO, **params)
870
+
871
+
872
+ def warning(
873
+ self,
874
+ *messages: Any,
875
+ **params: Any
876
+ ) -> None:
877
+ """
878
+ Record `warning` level log.
879
+
880
+ Parameters
881
+ ----------
882
+ messages : Record content.
883
+ params : Record Format parameters.
884
+ """
885
+
886
+ # Record.
887
+ self.log(*messages, level=self.WARNING, **params)
888
+
889
+
890
+ def error(
891
+ self,
892
+ *messages: Any,
893
+ **params: Any
894
+ ) -> None:
895
+ """
896
+ Record `error` level log.
897
+
898
+ Parameters
899
+ ----------
900
+ messages : Record content.
901
+ params : Record Format parameters.
902
+ """
903
+
904
+ # Record.
905
+ self.log(*messages, level=self.ERROR, **params)
906
+
907
+
908
+ def critical(
909
+ self,
910
+ *messages: Any,
911
+ **params: Any
912
+ ) -> None:
913
+ """
914
+ Record `critical` level log.
915
+
916
+ Parameters
917
+ ----------
918
+ messages : Record content.
919
+ params : Record Format parameters.
920
+ """
921
+
922
+ # Record.
923
+ self.log(*messages, level=self.CRITICAL, **params)
924
+
925
+
926
+ def stop(self) -> None:
927
+ """
928
+ Stop record.
929
+ """
930
+
931
+ # Set level.
932
+ self.logger.setLevel(100)
933
+
934
+ # Update status.
935
+ self.stoped = True
936
+
937
+
938
+ def start(self) -> None:
939
+ """
940
+ Start record.
941
+ """
942
+
943
+ # Set level.
944
+ self.logger.setLevel(self.DEBUG)
945
+
946
+ # Update status.
947
+ self.stoped = False
948
+
949
+
950
+ def __del__(self) -> None:
951
+ """
952
+ Delete handle.
953
+ """
954
+
955
+ # Reset.
956
+ self.reset_print()
957
+
958
+ # Delete handler.
959
+ self.clear_handler()
960
+
961
+
962
+ __call__ = log
963
+
964
+
965
+ class RRecord(object):
966
+ """
967
+ Rey's `record` type.
968
+ """
969
+
970
+
971
+ def __init__(
972
+ self,
973
+ path: Optional[str] = '_rrecord'
974
+ ) -> None:
975
+ """
976
+ Build `record` attributes.
977
+
978
+ Parameters
979
+ ----------
980
+ path : File path.
981
+ - `None`: Record to variable.
982
+ - `path`: Record to file.
983
+ """
984
+
985
+ # Set attribute.
986
+ self.path = path
987
+ if path is None:
988
+ self.records = []
989
+
990
+
991
+ def record(
992
+ self,
993
+ value: Any
994
+ ) -> None:
995
+ """
996
+ Record value.
997
+
998
+ Parameters
999
+ ----------
1000
+ value : Value.
1001
+ """
1002
+
1003
+ # To variable.
1004
+ if self.path is None:
1005
+ self.records.append(value)
1006
+
1007
+ # To file.
1008
+ else:
1009
+ rfile = RFile(self.path)
1010
+
1011
+ ## Convert.
1012
+ if value.__class__ != str:
1013
+ value = str(value)
1014
+ if rfile:
1015
+ value += ':'
1016
+ else:
1017
+ value = ':%s:' % value
1018
+
1019
+ ## Record.
1020
+ rfile(value, True)
1021
+
1022
+
1023
+ def is_record(
1024
+ self,
1025
+ value: Any
1026
+ ) -> bool:
1027
+ """
1028
+ Judge if has been recorded.
1029
+
1030
+ Parameters
1031
+ ----------
1032
+ value : Record value.
1033
+
1034
+ Returns
1035
+ -------
1036
+ Judge result.
1037
+ """
1038
+
1039
+ # To variable.
1040
+ if self.path is None:
1041
+ judge = value in self.records
1042
+
1043
+ # To file.
1044
+ else:
1045
+ rfile = RFile(self.path)
1046
+
1047
+ ## Convert.
1048
+ if value.__class__ != str:
1049
+ value = str(value)
1050
+ value = ':%s:' % value
1051
+
1052
+ ## Judge.
1053
+ judge = value in rfile
1054
+
1055
+ return judge
1056
+
1057
+
1058
+ __call__ = record
1059
+
1060
+
1061
+ __contains__ = is_record