lyrpy 0.0.2__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.
lyr/LULog.py ADDED
@@ -0,0 +1,2249 @@
1
+ """LULog.py"""
2
+ # -*- coding: UTF-8 -*-
3
+ __annotations__ ="""
4
+ =======================================================
5
+ Copyright (c) 2023-2024
6
+ Author:
7
+ Lisitsin Y.R.
8
+ Project:
9
+ LU_PY
10
+ Python (LU)
11
+ Module:
12
+ LULog.py
13
+
14
+ =======================================================
15
+ """
16
+
17
+ #------------------------------------------
18
+ # БИБЛИОТЕКИ python
19
+ #------------------------------------------
20
+ import os
21
+ import sys
22
+ import enum
23
+ import datetime
24
+ import copy
25
+ import logging
26
+ import logging.config
27
+ import yaml
28
+ import json
29
+
30
+ import inspect
31
+ import traceback
32
+
33
+ #------------------------------------------
34
+ # БИБЛИОТЕКИ сторонние
35
+ #------------------------------------------
36
+ import pythonjsonlogger
37
+ import pythonjsonlogger.jsonlogger
38
+ #------------------------------------------
39
+ import PySide6.QtWidgets
40
+
41
+ #------------------------------------------
42
+ # БИБЛИОТЕКА LU
43
+ #------------------------------------------
44
+ import lyr
45
+ import lyr.LUConst as LUConst
46
+ import lyr.LUFile as LUFile
47
+ import lyr.LUConsole as LUConsole
48
+ import lyr.LUDateTime as LUDateTime
49
+ import lyr.LUos as LUos
50
+ import lyr.LUParserINI as LUParserINI
51
+ import lyr.LUDict as LUDict
52
+ import lyr.LUSupport as LUSupport
53
+
54
+ # ===========================================================================
55
+ # CONST
56
+ # ===========================================================================
57
+ import rich
58
+ import rich.console as console
59
+ GConsoleRich = rich.console.Console ()
60
+
61
+ STATLogging = True
62
+
63
+ """CONST"""
64
+ ctlsNOTSET = ' '
65
+ ctlsDEBUG = 'D'
66
+ ctlsINFO = 'I'
67
+ ctlsWARNING = 'W'
68
+ ctlsERROR = 'E'
69
+ ctlsCRITICAL = 'C'
70
+ ctlsBEGIN = '>'
71
+ ctlsEND = '<'
72
+ ctlsPROCESS = 'P'
73
+ ctlsDEBUGTEXT = 'T'
74
+ ctlsTEXT = ''
75
+
76
+ TruncLog = 1
77
+ LogPath = ''
78
+ Log = 30
79
+ LogDir = ''
80
+ LogFile = ''
81
+
82
+ # ДОБАВИТЬ LEVEL
83
+ DEBUGTEXT = 11
84
+ BEGIN = 21
85
+ END = 22
86
+ PROCESS = 23
87
+ TEXT = 24
88
+
89
+ # строка формата сообщения
90
+ # Cstrfmt_04 = '%(asctime)s %(msecs)03d [%(name)s] %(levelno)02d %(levelname)-8s %(module)s %(message)s'
91
+ Cstrfmt_01 = '%(asctime)s [%(name)s] [%(module)-15s] %(levelno)02d %(levelname)-10s %(lineno)04d %(message)s'
92
+ Cstrfmt_02 = '%(asctime)s %(name)s %(levelname)-10s %(message)s'
93
+ # строка формата времени
94
+ Cdatefmt_01 = '%d/%m/%Y %H:%M:%S'
95
+ # style
96
+ Cstyle_01 = '%'
97
+ Cstyle_02 = '{'
98
+ Cstyle_03 = '$'
99
+ # defaults
100
+ Cdefaults = {"ip": '_ip_'}
101
+
102
+ def AddLevelName():
103
+ #beginfunction
104
+ logging.addLevelName(DEBUGTEXT, 'DEBUGTEXT')
105
+ logging.addLevelName(BEGIN, 'BEGIN')
106
+ logging.addLevelName(END, 'END')
107
+ logging.addLevelName(PROCESS, 'PROCESS')
108
+ logging.addLevelName(TEXT, 'TEXT')
109
+ #endfunction
110
+
111
+ CDefaultFileLogINI = 'logging.ini'
112
+ CDefaultFileLogCONFIG = 'logging.CONFIG'
113
+ CDefaultFileLogYAML = 'logging.YAML'
114
+
115
+ CDefaultFileLog = 'LOGGING.log'
116
+ CDefaultFileLogFILEINI = 'LOGGING_FILEINI.log'
117
+ CDefaultFileLogFILEINI_json = 'LOGGING_FILEINI_json.log'
118
+
119
+ CDefaultFileLogFILECONFIG = 'LOGGING_CONFIG.log'
120
+ CDefaultFileLogFILECONFIG_json = 'LOGGING_FILECONFIG_json.log'
121
+
122
+ CDefaultFileLogFILEBASIC = 'LOGGING_BASIC.log'
123
+
124
+ # ===========================================================================
125
+ # type
126
+ # ===========================================================================
127
+ @enum.unique
128
+ class TTypeSETUPLOG (enum.Enum):
129
+ """TTypeSETUPLOG"""
130
+ tslCONFIG = 0
131
+ tslYAML = 1
132
+ tslINI = 2
133
+ @classmethod
134
+ def Empty (cls):
135
+ ...
136
+ #endclass
137
+
138
+ @enum.unique
139
+ class TTypeLogString(enum.Enum):
140
+ """TTypeLogString"""
141
+ tlsNOTSET = ctlsNOTSET
142
+ tlsDEBUG = ctlsDEBUG
143
+ tlsINFO = ctlsINFO
144
+ tlsWARNING = ctlsWARNING
145
+ tlsERROR = ctlsERROR
146
+ tlsCRITICAL = ctlsCRITICAL
147
+ tlsBEGIN = ctlsBEGIN
148
+ tlsEND = ctlsEND
149
+ tlsPROCESS = ctlsPROCESS
150
+ tlsDEBUGTEXT = ctlsDEBUGTEXT
151
+ tlsTEXT = ctlsTEXT
152
+ @classmethod
153
+ def Empty(cls):
154
+ ...
155
+ #endclass
156
+
157
+ @enum.unique
158
+ class TTypeLogCODE (enum.Enum):
159
+ """TTypeLogCODE"""
160
+ tlcOEM = 0
161
+ tlcANSI = 1
162
+ @classmethod
163
+ def Empty (cls):
164
+ ...
165
+ #endclass
166
+
167
+ @enum.unique
168
+ class TLogOutput (enum.Enum):
169
+ loStandard = 0
170
+ loTextFile = 1
171
+ @classmethod
172
+ def Empty (cls):
173
+ ...
174
+ #endclass
175
+
176
+ Cbold = 'bold '
177
+ Cblue = 'blue'
178
+ Cwhite = 'white'
179
+ Cyellow = 'yellow'
180
+ Cred = 'red'
181
+ Cgreen = 'green'
182
+ Con = 'on'
183
+
184
+ Cbold_blue = Cbold+Cblue
185
+ Cbold_white = Cbold+Cwhite
186
+ Cbold_yellow = Cbold+Cyellow
187
+ Cbold_red = Cbold+Cred
188
+ Cbold_red_blue = Cbold+Cred+' on '+Cblue
189
+ Cbold_green = Cbold+Cred
190
+
191
+ COLORS_tls = {
192
+ TTypeLogString.tlsNOTSET: lyr.LUConsole.cFG8_BLUE+lyr.LUConsole.sEND,
193
+ TTypeLogString.tlsDEBUG: lyr.LUConsole.cFG8_BLUE+lyr.LUConsole.sEND,
194
+ TTypeLogString.tlsINFO: lyr.LUConsole.cFG8_WHITE+lyr.LUConsole.sEND,
195
+ TTypeLogString.tlsWARNING: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_YELLOW+lyr.LUConsole.sEND,
196
+ TTypeLogString.tlsERROR: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_RED+lyr.LUConsole.sEND,
197
+ TTypeLogString.tlsCRITICAL: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_BLACK + ';' + lyr.LUConsole.cBG8_RED+lyr.LUConsole.sEND,
198
+ TTypeLogString.tlsBEGIN: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_GREEN + lyr.LUConsole.sEND,
199
+ TTypeLogString.tlsEND: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_GREEN + lyr.LUConsole.sEND,
200
+ TTypeLogString.tlsPROCESS: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_GREEN + lyr.LUConsole.sEND,
201
+ TTypeLogString.tlsDEBUGTEXT: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_BLUE+lyr.LUConsole.sEND,
202
+ TTypeLogString.tlsTEXT: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_YELLOW+lyr.LUConsole.sEND
203
+ }
204
+
205
+ COLORS_tls_rich = {
206
+ TTypeLogString.tlsNOTSET: Cbold_blue,
207
+ TTypeLogString.tlsDEBUG: Cbold_blue,
208
+ TTypeLogString.tlsINFO: Cbold_white,
209
+ TTypeLogString.tlsWARNING: Cbold_yellow,
210
+ TTypeLogString.tlsERROR: Cbold_red,
211
+ TTypeLogString.tlsCRITICAL: Cbold_red_blue,
212
+ TTypeLogString.tlsBEGIN: Cbold_green,
213
+ TTypeLogString.tlsEND: Cbold_green,
214
+ TTypeLogString.tlsPROCESS: Cbold_green,
215
+ TTypeLogString.tlsDEBUGTEXT: Cbold_blue,
216
+ TTypeLogString.tlsTEXT: Cbold_yellow
217
+ }
218
+
219
+ COLORS = {
220
+ logging.NOTSET: lyr.LUConsole.cFG8_BLUE+lyr.LUConsole.sEND,
221
+ logging.DEBUG: lyr.LUConsole.cFG8_BLUE+lyr.LUConsole.sEND,
222
+ logging.INFO: lyr.LUConsole.cFG8_WHITE+lyr.LUConsole.sEND,
223
+ logging.WARNING: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_YELLOW+lyr.LUConsole.sEND,
224
+ logging.ERROR: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_RED+lyr.LUConsole.sEND,
225
+ logging.CRITICAL: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_BLACK + ';' + lyr.LUConsole.cBG8_RED+lyr.LUConsole.sEND,
226
+ BEGIN: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_GREEN + lyr.LUConsole.sEND,
227
+ END: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_GREEN + lyr.LUConsole.sEND,
228
+ PROCESS: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_GREEN + lyr.LUConsole.sEND,
229
+ DEBUGTEXT: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_BLUE+lyr.LUConsole.sEND,
230
+ TEXT: lyr.LUConsole.cS_BOLD + ';' + lyr.LUConsole.cFG8_YELLOW+lyr.LUConsole.sEND
231
+ }
232
+
233
+ COLORS_rich = {
234
+ logging.NOTSET: Cbold_blue,
235
+ logging.DEBUG: Cbold_blue,
236
+ logging.INFO: Cbold_white,
237
+ logging.WARNING: Cbold_yellow,
238
+ logging.ERROR: Cbold_red,
239
+ logging.CRITICAL: Cbold_red_blue,
240
+ BEGIN: Cbold_green,
241
+ END: Cbold_green,
242
+ PROCESS: Cbold_green,
243
+ DEBUGTEXT: Cbold_blue,
244
+ TEXT: Cbold_yellow
245
+ }
246
+
247
+ #TLogOutputs = set of TLogOutput;
248
+
249
+ class TFileMemoLog (object):
250
+ """TFileMemoLog"""
251
+ luClassName = "TFileMemoLog"
252
+
253
+ #--------------------------------------------------
254
+ # constructor
255
+ #--------------------------------------------------
256
+ def __init__(self):
257
+ """Constructor"""
258
+ #beginfunction
259
+ super().__init__()
260
+ self.__FCountLogStrings: int = 200
261
+ self.__FFileName: str = ''
262
+ self.__FStandardOut: bool = True
263
+ self.__FLogCODE: TTypeLogCODE = TTypeLogCODE.tlcANSI
264
+ self.__FLogEnabled: bool = True
265
+ self.__FTruncateDays: int = 3
266
+ self.__FLogStringOEM: str = ''
267
+ self.__FLogStringAnsi: str = ''
268
+ self.__FMemoLog = None #TMemo
269
+ self.__FLogStrings: list = list() #TStringList;
270
+ self.__FLogSave: list = list() #TStringList;
271
+ self.__FLogCODE = lyr.LUFile.cDefaultEncoding
272
+ self.__FConsoleRich = rich.console.Console()
273
+
274
+ # self.__FLogger: logging.Logger = CreateLoggerFILEINI (CDefaultFileLogINI, 'root')
275
+
276
+ self.Clear ()
277
+ #endfunction
278
+
279
+ #--------------------------------------------------
280
+ # destructor
281
+ #--------------------------------------------------
282
+ def __del__(self):
283
+ """destructor"""
284
+ #beginfunction
285
+ del self.__FLogStrings
286
+ del self.__FLogSave
287
+ LClassName = self.__class__.__name__
288
+ s = '{} уничтожен'.format (LClassName)
289
+ #print (s)
290
+ #endfunction
291
+
292
+ def Clear(self):
293
+ """Clear"""
294
+ #beginfunction
295
+ ...
296
+ #endfunction
297
+
298
+ #--------------------------------------------------
299
+ # @property LogCODE
300
+ #--------------------------------------------------
301
+ @property
302
+ # getter
303
+ def LogCODE (self) -> int:
304
+ #beginfunction
305
+ return self.__FLogCODE
306
+ #endfunction
307
+ @LogCODE.setter
308
+ def LogCODE (self, Value: int):
309
+ #beginfunction
310
+ self.__FLogCODE = Value
311
+ #endfunction
312
+
313
+ #--------------------------------------------------
314
+ # @property CountLogStrings
315
+ #--------------------------------------------------
316
+ @property
317
+ # getter
318
+ def CountLogStrings (self) -> int:
319
+ #beginfunction
320
+ return self.__FCountLogStrings
321
+ #endfunction
322
+ @CountLogStrings.setter
323
+ def CountLogStrings (self, Value: int):
324
+ #beginfunction
325
+ self.__FCountLogStrings = Value
326
+ #endfunction
327
+
328
+ #--------------------------------------------------
329
+ # @property LogEnabled
330
+ #--------------------------------------------------
331
+ # getter
332
+ @property
333
+ def LogEnabled (self) -> bool:
334
+ #beginfunction
335
+ return self.__FLogEnabled
336
+ #endfunction
337
+ @LogEnabled.setter
338
+ def LogEnabled (self, Value: bool):
339
+ #beginfunction
340
+ self.__FLogEnabled = Value
341
+ #endfunction
342
+
343
+ #--------------------------------------------------
344
+ # @property Filename
345
+ #--------------------------------------------------
346
+ # getter
347
+ @property
348
+ def FileName (self) -> str:
349
+ #beginfunction
350
+ return self.__FFileName
351
+ #endfunction
352
+ @FileName.setter
353
+ def FileName (self, Value: str):
354
+ #beginfunction
355
+ self.__FFileName = Value
356
+ if len(self.__FFileName) > 0 and LUFile.FileExists (self.__FFileName):
357
+ # FMemoLog.Lines.LoadFromFile (self.__FFileName);
358
+ ...
359
+ #endif
360
+ #endfunction
361
+
362
+ #--------------------------------------------------
363
+ # @property StandardOut
364
+ #--------------------------------------------------
365
+ # getter
366
+ @property
367
+ def StandardOut (self) -> bool:
368
+ #beginfunction
369
+ return self.__FStandardOut
370
+ #endfunction
371
+ @StandardOut.setter
372
+ def StandardOut (self, Value: bool):
373
+ #beginfunction
374
+ self.__FStandardOut = Value
375
+ #endfunction
376
+
377
+ #--------------------------------------------------
378
+ # @property MemoLog
379
+ #--------------------------------------------------
380
+ # getter
381
+ @property
382
+ def MemoLog (self):
383
+ #beginfunction
384
+ return self.__FMemoLog
385
+ #endfunction
386
+ @MemoLog.setter
387
+ def MemoLog (self, Value):
388
+ #beginfunction
389
+ self.__FMemoLog = Value
390
+ # if FMemoLog <> nil then
391
+ # begin
392
+ # with FMemoLog do
393
+ # begin
394
+ # Clear;
395
+ # Align := alClient;
396
+ # readonly := True;
397
+ # TabStop := False;
398
+ # WantReturns := False;
399
+ # WantTabs := False;
400
+ # WordWrap := False;
401
+ # ParentColor := True;
402
+ # ScrollBars := ssVertical;
403
+ # ScrollBars := ssBoth;
404
+ # end;
405
+ # if (Filename <> '') and FileExists (Filename) then
406
+ # begin
407
+ # try
408
+ # FMemoLog.Lines.LoadFromFile (Filename);
409
+ # except
410
+ # end;
411
+ # end;
412
+ # end;
413
+ ...
414
+ #endfunction
415
+
416
+ def _SetMemoLog (self, Value): #TMemo
417
+ """_SetMemoLog"""
418
+ #beginfunction
419
+ ...
420
+ #endfunction
421
+
422
+ @staticmethod
423
+ def _LogDateStr (ATimeOnly: bool) -> str:
424
+ """LogDateStr"""
425
+ #beginfunction
426
+ LToday: datetime.datetime = LUDateTime.Now ()
427
+ if ATimeOnly:
428
+ LResult = ' '*15 + LUDateTime.DateTimeStr (ATimeOnly, LToday, LUDateTime.cFormatDateTimeLog01, True)
429
+ else:
430
+ LResult = LUDateTime.DateTimeStr (ATimeOnly, LToday, LUDateTime.cFormatDateTimeLog01, True)
431
+ #endif
432
+ return LResult
433
+ #endfunction
434
+
435
+ def _GetLogSave (self, Filename: str) -> list: #TStringList
436
+ """_GetLogSave"""
437
+ #beginfunction
438
+ ...
439
+ #endfunction
440
+
441
+ def _GetLogSaveCurrent (self) -> list: #TStringList;
442
+ #beginfunction
443
+ """_GetLogSaveCurrent"""
444
+ LResult = self._GetLogSave (self.__FFileName)
445
+ return LResult
446
+ #endfunction
447
+
448
+ def TruncateLog (self):
449
+ """TruncateLog"""
450
+ #beginfunction
451
+ # Filename
452
+ ts: list = list()
453
+ if LUFile.FileExists (self.__FFileName):
454
+ # Открыть для чтения
455
+ LEncoding = LUFile.GetFileEncoding (self.__FFileName)
456
+ LFile = open (self.__FFileName, 'r', encoding = LEncoding)
457
+ try:
458
+ # работа с файлом
459
+ for s in LFile:
460
+ ts.append (s)
461
+ #file.next() возвращает следующую строку файла
462
+ #endfor
463
+ except:
464
+ s = f'TruncateLog: Неправильная кодировка журнала!'
465
+ LoggerTOOLS.error (s)
466
+ finally:
467
+ LFile.close ()
468
+ # TruncateMemo (ts)
469
+ # try
470
+ # ts.SaveToFile (Filename)
471
+ # except:
472
+ # #endtry
473
+ #endif
474
+ del ts
475
+
476
+ # Memo
477
+ ts: list = list()
478
+ if self.__FMemoLog is not None:
479
+ ts.clear()
480
+ # ts.Assign (FMemoLog.Lines)
481
+ # TruncateMemo (ts)
482
+ # FMemoLog.Clear
483
+ # FMemoLog.Lines.Assign (ts)
484
+ #endif
485
+ del ts
486
+ #endfunction
487
+
488
+ def _HandlerCONSOLE (self, T: TTypeLogString):
489
+ """_HandlerCONSOLE"""
490
+ #beginfunction
491
+
492
+ # if self.FUseColor:
493
+
494
+ self.__FLogStrings.clear ()
495
+ self.__FLogStrings.append (self.__FLogStringAnsi)
496
+ for s in self.__FLogStrings:
497
+ if T == TTypeLogString.tlsTEXT:
498
+ _s = s
499
+ else:
500
+ _s = self._LogDateStr (False) + ' ' + str(T.value) + ' ' + s
501
+ if not LUSupport.ISTerminal ():
502
+ LCOLOR = COLORS_tls.get (T)
503
+ if LCOLOR is not None:
504
+ LFmt = LUConsole.sBEGIN_oct + LCOLOR + _s + LUConsole.sRESET
505
+ else:
506
+ LFmt = _s
507
+ LUConsole.WriteLN (LFmt)
508
+ else:
509
+ LCOLOR = COLORS_tls_rich.get (T)
510
+ LFmt = _s
511
+ if len(LCOLOR) > 0:
512
+ LFmt = '[' + LCOLOR + ']' + _s
513
+ else:
514
+ LFmt = s
515
+ self.__FConsoleRich.print (LFmt)
516
+ #endif
517
+ #endfor
518
+ #endfunction
519
+
520
+ def _HandlerFILE (self, T: TTypeLogString):
521
+ """_HandlerFILE"""
522
+ #beginfunction
523
+ s = LUFile.ExpandFileName (self.__FFileName)
524
+ s = LUFile.ExtractFileDir (s)
525
+ if len (s) > 0:
526
+ if not LUFile.DirectoryExists (s):
527
+ LUFile.ForceDirectories (s)
528
+ #endif
529
+ #endif
530
+
531
+ self.__FLogStrings.clear ()
532
+ self.__FLogStrings.append (self.__FLogStringAnsi)
533
+
534
+ for s in self.__FLogStrings:
535
+ if T == TTypeLogString.tlsTEXT:
536
+ _s = s
537
+ else:
538
+ _s = self._LogDateStr (False) + ' ' + str(T.value) + ' ' + s
539
+ #endif
540
+ try:
541
+ LEncoding = self.__FLogCODE
542
+
543
+ # LEncoding = LUFile.GetFileEncoding (self.__FFileName)
544
+ # if LEncoding == '':
545
+ # LEncoding = LUFile.cDefaultEncoding
546
+
547
+ LEncoding = LUFile.cDefaultEncoding
548
+ with open (self.__FFileName, 'a+', encoding = LEncoding) as LFile:
549
+
550
+ # _s = str (s.encode ('utf-8'), 'cp1251')
551
+ # _s = str (s.encode ('cp1251'), 'cp1251')
552
+
553
+ # _s = str (_s.encode (self.__FLogCODE), self.__FLogCODE)
554
+
555
+ LFile.write (_s + '\n')
556
+
557
+ #endwith
558
+ except:
559
+ s = f'_HandlerFILE: Неправильная кодировка журнала!'
560
+ LoggerTOOLS.error (s)
561
+ #endtry
562
+ #endfor
563
+ #endfunction
564
+
565
+ def _Execute (self, T: TTypeLogString):
566
+ """_Execute"""
567
+ #beginfunction
568
+ # StandardOut
569
+ if self.__FStandardOut: # and isConsole:
570
+ self._HandlerCONSOLE (T)
571
+ #endif
572
+ # Filename
573
+ if self.__FFileName != '':
574
+ self._HandlerFILE (T)
575
+ #endif
576
+ # Memo
577
+ if self.__FMemoLog is not None:
578
+ self.__FLogStrings.clear()
579
+ self.__FLogStrings.append(self.__FLogStringAnsi)
580
+ """
581
+ for s in self.__FLogStrings:
582
+ self.__FMemoLog.add
583
+ #endfor
584
+ """
585
+ #endif
586
+ #endfunction
587
+
588
+ #--------------------------------------------------
589
+ #
590
+ #--------------------------------------------------
591
+ def AddLog (self, T: TTypeLogString, Value: str):
592
+ """AddLog"""
593
+ #beginfunction
594
+ self.__FLogStringOEM = Value
595
+ self.__FLogStringAnsi = Value
596
+ if self.LogEnabled:
597
+ self._Execute(T)
598
+ #endfunction
599
+
600
+ def AddLogFile (self, AFileName: str):
601
+ """AddLogFile"""
602
+ #beginfunction
603
+ if LUFile.FileExists (AFileName):
604
+ LEncoding = LUFile.GetFileEncoding (AFileName)
605
+ if LEncoding == '':
606
+ LEncoding = LUFile.cDefaultEncoding
607
+ try:
608
+ # работа с файлом
609
+ with open (AFileName, 'r', encoding = LEncoding) as LFile:
610
+ for s in LFile:
611
+ self.AddLog (TTypeLogString.tlsTEXT, s.rstrip('\n'))
612
+ #endfor
613
+ #endwith
614
+ except:
615
+ self.AddLog (TTypeLogString.tlsERROR, AFileName)
616
+ s = f'AddLogFile: Неправильная кодировка журнала!'
617
+ LoggerTOOLS.error (s)
618
+
619
+ #endif
620
+ #endfunction
621
+ #endclass
622
+
623
+ #----------------------------------------------
624
+ # TLogging
625
+ #----------------------------------------------
626
+
627
+ #-------------------------------------------------
628
+ # TLogRecord(logging.LogRecord):
629
+ #-------------------------------------------------
630
+ class TLogRecord(logging.LogRecord):
631
+ """TLogRecord"""
632
+ luClassName = "TLogRecord"
633
+ #--------------------------------------------------
634
+ # constructor
635
+ #--------------------------------------------------
636
+ #class logging.LogRecord(name, level, pathname, lineno, msg, args, exc_info, func=None, sinfo=None)
637
+ def __init__(self, **kwargs):
638
+ """Constructor"""
639
+ #beginfunction
640
+ logging.LogRecord.__init__(self, **kwargs)
641
+ #endfunction
642
+ #endclass
643
+
644
+ #-------------------------------------------------
645
+ # THandler(logging.Handler):
646
+ #-------------------------------------------------
647
+ class THandler(logging.Handler):
648
+ """THandler"""
649
+ luClassName = "THandler"
650
+ #--------------------------------------------------
651
+ # constructor
652
+ #--------------------------------------------------
653
+ #class logging.Handler
654
+ def __init__(self, parent, **kwargs):
655
+ # def __init__ (self, parent):
656
+ """Constructor"""
657
+ #beginfunction
658
+ logging.Handler.__init__(self, **kwargs)
659
+ # super ().__init__ ()
660
+
661
+ self.__Fwidget = None
662
+ # self.__Fwidget = PySide6.QtWidgets.QPlainTextEdit (parent)
663
+ # self.__Fwidget.setReadOnly (True)
664
+ #endfunction
665
+
666
+ #--------------------------------------------------
667
+ # @property widget
668
+ #--------------------------------------------------
669
+ # getter
670
+ @property
671
+ def widget (self):
672
+ #beginfunction
673
+ return self.__Fwidget
674
+ #endfunction
675
+ @widget.setter
676
+ def widget (self, Value):
677
+ #beginfunction
678
+ self.__Fwidget = Value
679
+ #endfunction
680
+
681
+
682
+ def emit(self, record):
683
+ if self.widget is None:
684
+ super (THandler, self).emit (record)
685
+ else:
686
+ msg = self.format (record)
687
+ self.widget.appendPlainText (msg)
688
+ #endif
689
+ #endclass
690
+
691
+ #-------------------------------------------------
692
+ # class TStreamHandler(logging.StreamHandler):
693
+ #-------------------------------------------------
694
+ class TStreamHandler(logging.StreamHandler):
695
+ """TStreamHandler"""
696
+ luClassName = "TStreamHandler"
697
+ #--------------------------------------------------
698
+ # constructor
699
+ #--------------------------------------------------
700
+ def __init__(self, *args, **kwargs):
701
+ """Constructor"""
702
+ #beginfunction
703
+ logging.StreamHandler.__init__(self, *args, **kwargs)
704
+ self.name = 'CONSOLE'
705
+ self.FAPPGUI = False
706
+
707
+ self.__FConsoleRich = rich.console.Console()
708
+
709
+ self.__FWidget:PySide6.QtWidgets.QPlainTextEdit = None
710
+ # self.__Fwidget = PySide6.QtWidgets.QPlainTextEdit (parent)
711
+ # self.__Fwidget = PySide6.QtWidgets.QPlainTextEdit ()
712
+ # self.__Fwidget.setReadOnly (True)
713
+
714
+ #endfunction
715
+
716
+ #--------------------------------------------------
717
+ # @property widget
718
+ #--------------------------------------------------
719
+ # getter
720
+ @property
721
+ def Widget (self):
722
+ #beginfunction
723
+ return self.__FWidget
724
+ #endfunction
725
+ @Widget.setter
726
+ def Widget (self, Value):
727
+ #beginfunction
728
+ self.__FWidget = Value
729
+ #endfunction
730
+
731
+ #--------------------------------------------------
732
+ # emit
733
+ #--------------------------------------------------
734
+ def emit(self, record):
735
+ """emit"""
736
+ #beginfunction
737
+ if type(self.formatter) is TFormatter:
738
+ LFormatter: TFormatter = self.formatter
739
+ # widget
740
+ if not self.Widget is None:
741
+ b = LFormatter.FUseColor
742
+ LFormatter.FUseColor = False
743
+ msg = LFormatter.format (record)
744
+ self.Widget.appendPlainText (msg)
745
+ # self.widget.document().end()
746
+ self.Widget.verticalScrollBar().setValue (self.Widget.verticalScrollBar().maximum ())
747
+ LFormatter.FUseColor = b
748
+ #endif
749
+
750
+ if LFormatter.FUseColor:
751
+ # self.emit(record)
752
+ try:
753
+ msg = self.format(record)
754
+ stream = self.stream
755
+ # issue 35046: merged two stream.writes into one.
756
+ if not lyr.LUSupport.IsTerminal ():
757
+ stream.write(msg + self.terminator)
758
+ self.flush()
759
+ else:
760
+ msg = self.format (record)
761
+ if not self.FAPPGUI:
762
+ #rich.print (msg)
763
+ self.__FConsoleRich.print(msg)
764
+ #endif
765
+ #endif
766
+ except RecursionError: # See issue 36272
767
+ raise
768
+ except Exception:
769
+ self.handleError(record)
770
+ #endtry
771
+ #endif
772
+ else:
773
+ super(TStreamHandler, self).emit(record)
774
+ #endif
775
+ #endfunction
776
+ #endclass
777
+
778
+ #-------------------------------------------------
779
+ # TFilter(logging.Filter):
780
+ #-------------------------------------------------
781
+ class TFilter(logging.Filter):
782
+ """TFilter"""
783
+ luClassName = "TFilter"
784
+ COLOR = {
785
+ "DEBUG": "BLUE",
786
+ "INFO": "WHITE",
787
+ "WARNING": "YELLOW",
788
+ "ERROR": "RED",
789
+ "CRITICAL": "RED",
790
+ "DEBUGTEXT": "RED",
791
+ "BEGIN": "RED",
792
+ "END": "RED",
793
+ "PROCESS": "RED",
794
+ "TEXT": "RED"
795
+ }
796
+ #--------------------------------------------------
797
+ # constructor
798
+ #--------------------------------------------------
799
+ #class logging.Filter (name='')
800
+ def __init__(self, **kwargs):
801
+ """Constructor"""
802
+ #beginfunction
803
+ logging.Filter.__init__(self, **kwargs)
804
+ #endfunction
805
+
806
+ def filter(self, record):
807
+ #beginfunction
808
+ record.color = self.COLOR[record.levelname]
809
+ #print(record.color)
810
+ return True
811
+ #endfunction
812
+ #endclass
813
+
814
+ # #-------------------------------------------------
815
+ # # TFilter(logging.Filter):
816
+ # #-------------------------------------------------
817
+ # # Фильтр, который вводит контекстную информацию в журнал.
818
+ # # Вместо того, чтобы использовать фактическую контекстуальную информацию, мы
819
+ # # просто используем случайные данные в этой демонстрации.
820
+ # from random import choice
821
+ #
822
+ # class TFilter (logging.Filter):
823
+ # """TFilter"""
824
+ # luClassName = "TFilter"
825
+ # USERS = ['jim', 'fred', 'sheila']
826
+ # IPS = ['123.231.231.123', '127.0.0.1', '192.168.0.1']
827
+ # def filter(self, record):
828
+ # #beginfunction
829
+ # record.ip = choice(TFilter.IPS)
830
+ # record.user = choice(TFilter.USERS)
831
+ # return True
832
+ # #endfunction
833
+ # #endclass
834
+ #
835
+ # def Test ():
836
+ # #beginfunction
837
+ # levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
838
+ # logging.basicConfig(level=logging.DEBUG,
839
+ # format='%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s')
840
+ # a1 = logging.getLogger('a.b.c')
841
+ # a2 = logging.getLogger('d.e.f')
842
+ # f = TFilter()
843
+ # a1.addFilter(f)
844
+ # a2.addFilter(f)
845
+ # a1.debug('A debug message')
846
+ # a1.info('An info message with %s', 'some parameters')
847
+ # for x in range(10):
848
+ # lvl = choice(levels)
849
+ # lvlname = logging.getLevelName(lvl)
850
+ # a2.log(lvl, 'A message at %s level with %d %s', lvlname, 2, 'parameters')
851
+ # #endfunction
852
+
853
+ #-------------------------------------------------
854
+ # TAdapter(logging.LoggerAdapter):
855
+ #-------------------------------------------------
856
+ class TAdapter(logging.LoggerAdapter):
857
+ """TAdapter"""
858
+ luClassName = "TAdapter"
859
+ #--------------------------------------------------
860
+ # constructor
861
+ #--------------------------------------------------
862
+ #class logging.LoggerAdapter(logger, extra)
863
+ def __init__(self, **kwargs):
864
+ """Constructor"""
865
+ #beginfunction
866
+ # logging.LoggerAdapter.__init__(self, logger = None, extra = None)
867
+ logging.LoggerAdapter.__init__(self, **kwargs)
868
+ #endfunction
869
+
870
+ def process(self, msg, kwargs):
871
+ my_context = kwargs.pop('id', self.extra['id'])
872
+ return '[%s] %s' % (my_context, msg), kwargs
873
+ #endclass
874
+
875
+ #-------------------------------------------------
876
+ # TFormatter(logging.Formatter):
877
+ #-------------------------------------------------
878
+ class TFormatter(logging.Formatter):
879
+ """TFormatter"""
880
+ luClassName = "TFormatter"
881
+
882
+ #--------------------------------------------------
883
+ # constructor
884
+ #--------------------------------------------------
885
+ def __init__ (self, AUseColor = True, **kwargs):
886
+ """Constructor"""
887
+ #beginfunction
888
+ #class logging.Formatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)
889
+ logging.Formatter.__init__(self, **kwargs)
890
+ self.FUseColor = AUseColor
891
+ #endfunction
892
+
893
+ def _SetColor(self, AFmt: str, ALevelNo: int) -> str:
894
+ """_SetColor"""
895
+ #beginfunction
896
+ if self.FUseColor:
897
+ if not lyr.LUSupport.IsTerminal ():
898
+ LCOLOR = COLORS.get (ALevelNo)
899
+ LFmt = lyr.LUConsole.sBEGIN_oct + LCOLOR + AFmt + lyr.LUConsole.sRESET
900
+ return LFmt
901
+ else:
902
+ LCOLOR = COLORS_rich.get (ALevelNo)
903
+ _s = AFmt
904
+ LFmt = _s
905
+ LFmt = '[' + LCOLOR + ']' + _s
906
+ if len(LCOLOR) > 0:
907
+ LFmt = '[' + LCOLOR + ']' + _s
908
+ else:
909
+ LFmt = _s
910
+ return LFmt
911
+ else:
912
+ return AFmt
913
+ #endif
914
+ #endfunction
915
+
916
+ def format(self, record):
917
+ """format"""
918
+ #beginfunction
919
+ # отдельный атрибут
920
+ # LLevelname = record.levelname
921
+ # record.levelname = '_'+LLevelname+'_'
922
+
923
+ Ldatefmt = self.datefmt
924
+ if self.FUseColor:
925
+ if record.levelno == TEXT:
926
+ # установить новый fmt
927
+ Lfmt = self._SetColor ('%(message)s', record.levelno)
928
+ else:
929
+ Lfmt = self._SetColor (self._fmt, record.levelno)
930
+ #endif
931
+ else:
932
+ if record.levelno == TEXT:
933
+ # установить новый fmt
934
+ Lfmt = '%(message)s'
935
+ else:
936
+ Lfmt = self._fmt
937
+ #endif
938
+ #endif
939
+ # установить новый fmt
940
+ Lformatter = logging.Formatter (Lfmt, Ldatefmt)
941
+ s = Lformatter.format (record)
942
+ return s
943
+ #endfunction
944
+ #endclass
945
+
946
+ #-------------------------------------------------
947
+ # TFormatterJSON(jsonlogger.JsonFormatter):
948
+ #-------------------------------------------------
949
+ class TFormatterJSON(pythonjsonlogger.jsonlogger.JsonFormatter):
950
+ """TFormatterJSON"""
951
+ luClassName = "TFormatterJSON"
952
+
953
+ #--------------------------------------------------
954
+ # constructor
955
+ #--------------------------------------------------
956
+ #class jsonlogger.JsonFormatter(*args, **kwargs)
957
+ def __init__(self, *args, **kwargs):
958
+ """Constructor"""
959
+ #beginfunction
960
+ super(TFormatterJSON, self).__init__(*args, **kwargs)
961
+ self.json_ensure_ascii = False
962
+ ...
963
+ #endfunction
964
+
965
+ def format(self, record):
966
+ """format"""
967
+ #beginfunction
968
+ return super().format(record)
969
+ #endfunction
970
+ #endclass
971
+
972
+ def AddHandlerCONSOLE (ALogger: logging.Logger, ALevel: int, Astrfmt: str, Adatefmt: str,
973
+ Astyle: str, Adefaults):
974
+ """AddFileHandler"""
975
+
976
+ #beginfunction
977
+ LHandler = TStreamHandler ()
978
+ LHandler.setLevel (ALevel)
979
+ LHandler.set_name ('CONSOLE')
980
+ LHandler.setStream (sys.stdout)
981
+ LFormater = TFormatter (fmt=Astrfmt, datefmt=Adatefmt,
982
+ style=Astyle, validate=True, defaults=Adefaults)
983
+ LHandler.setFormatter (LFormater)
984
+ ALogger.addHandler (LHandler)
985
+ #endfunction
986
+
987
+ def AddHandlerFILE (ALogger: logging.Logger, AFileName: str, ALevel: int, Astrfmt: str, Adatefmt: str,
988
+ Astyle: str, Adefaults):
989
+ """AddFileHandler"""
990
+ #beginfunction
991
+ LHandler = logging.FileHandler (AFileName, mode='a+',
992
+ encoding=LUFile.cDefaultEncoding, delay=False, errors=None)
993
+ LHandler.setLevel (ALevel)
994
+ LHandler.set_name ('FILE')
995
+ LFormater = TFormatter (fmt=Astrfmt, datefmt=Adatefmt,
996
+ style=Astyle, validate=True, defaults=Adefaults)
997
+ LHandler.setFormatter (LFormater)
998
+ ALogger.addHandler (LHandler)
999
+ #endfunction
1000
+
1001
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1002
+ # class TLogging (logging.Logger):
1003
+ # class ColoredLogger(logging.Logger):
1004
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1005
+
1006
+ # ??????????????????????????????????????????????
1007
+ # class TLogger (logging.getLoggerClass()):
1008
+ # ??????????????????????????????????????????????
1009
+
1010
+ class TLogger (logging.Logger):
1011
+ """TLogging"""
1012
+ luClassName = "TLogger"
1013
+
1014
+ #--------------------------------------------------
1015
+ # constructor
1016
+ #--------------------------------------------------
1017
+ def __init__(self, ALogerName: str):
1018
+ """Constructor"""
1019
+ #beginfunction
1020
+ super().__init__(ALogerName)
1021
+ self.__FFileName: str = ''
1022
+ # Formater
1023
+ self.__Fstrfmt = Cstrfmt_01
1024
+ self.__Fdatefmt = Cdatefmt_01
1025
+ self.__Fstyle = Cstyle_01
1026
+ self.__Fdefaults = Cdefaults
1027
+ # LEVEL
1028
+ self.LEVEL = logging.DEBUG
1029
+ # propagate
1030
+ self.propagate = True
1031
+ self.propagate = False
1032
+ AddHandlerCONSOLE (self, self.LEVEL, self.__Fstrfmt, self.__Fdatefmt, self.__Fstyle, self.__Fdefaults)
1033
+ self.Clear ()
1034
+ #endfunction
1035
+
1036
+ #--------------------------------------------------
1037
+ # destructor
1038
+ #--------------------------------------------------
1039
+ def __del__(self):
1040
+ """destructor"""
1041
+ #beginfunction
1042
+ LClassName = self.__class__.__name__
1043
+ s = '{} уничтожен'.format(LClassName)
1044
+ #print (s)
1045
+ #endfunction
1046
+
1047
+ def Clear(self):
1048
+ """Clear"""
1049
+ #beginfunction
1050
+ ...
1051
+ #endfunction
1052
+
1053
+ #--------------------------------------------------
1054
+ # @property FileName
1055
+ #--------------------------------------------------
1056
+ # getter
1057
+ @property
1058
+ def FileName (self) -> str:
1059
+ #beginfunction
1060
+ return self.__FFileName
1061
+ #endfunction
1062
+ @FileName.setter
1063
+ def FileName (self, Value: str):
1064
+ #beginfunction
1065
+ self.__FFileName = Value
1066
+ if len(self.__FFileName) > 0 and LUFile.FileExists (self.__FFileName):
1067
+ ...
1068
+ #endif
1069
+ #endfunction
1070
+
1071
+ #--------------------------------------------------
1072
+ # @property LEVEL
1073
+ #--------------------------------------------------
1074
+ # getter
1075
+ @property
1076
+ def LEVEL (self):
1077
+ #beginfunction
1078
+ return self.level
1079
+ #endfunction
1080
+ @LEVEL.setter
1081
+ def LEVEL (self, Value):
1082
+ #beginfunction
1083
+ self.setLevel (Value)
1084
+ #endfunction
1085
+
1086
+ def AddHandlerCONSOLE(self, ALevel: int):
1087
+ #beginfunction
1088
+ # AddHandlerCONSOLE (self, ALevel, self.__Fstrfmt, self.__Fdatefmt, self.__Fstyle, self.__Fdefaults)
1089
+ # LHandler = logging.StreamHandler ()
1090
+ # LHandler.setLevel (ALevel)
1091
+ # LHandler.set_name ('CONSOLE')
1092
+ # LHandler.setStream (sys.stdout)
1093
+ # LFormater = TFormatter (fmt=self.__Fstrfmt, datefmt=self.__Fdatefmt,
1094
+ # style=self.__Fstyle, validate=True, defaults=self.__Fdefaults)
1095
+ # LHandler.setFormatter (LFormater)
1096
+ # self.addHandler (LHandler)
1097
+ ...
1098
+ #endfunction
1099
+
1100
+ def AddHandlerFILE(self, AFileName: str, ALevel: int):
1101
+ #beginfunction
1102
+ LHandler = logging.FileHandler (AFileName, mode='a+',
1103
+ encoding=LUFile.cDefaultEncoding, delay=False, errors=None)
1104
+ LHandler.setLevel (ALevel)
1105
+ LHandler.set_name ('FILE')
1106
+ LFormater = TFormatter (AUseColor = False,
1107
+ fmt=self.__Fstrfmt, datefmt=self.__Fdatefmt,
1108
+ style=self.__Fstyle, validate=True, defaults=self.__Fdefaults)
1109
+ LHandler.setFormatter (LFormater)
1110
+ self.addHandler (LHandler)
1111
+ #endfunction
1112
+
1113
+ def AddHandlerFILE_JSON(self, AFileName: str, ALevel):
1114
+ #beginfunction
1115
+ LHandler = logging.FileHandler (AFileName, mode='a+',
1116
+ encoding=LUFile.cDefaultEncoding, delay=False, errors=None)
1117
+ LHandler.setLevel (ALevel)
1118
+ LHandler.set_name ('FILE_JSON')
1119
+ LFormater = TFormatterJSON (AUseColor = False,
1120
+ fmt=self.__Fstrfmt, datefmt=self.__Fdatefmt,
1121
+ style=self.__Fstyle, validate=True, defaults=self.__Fdefaults)
1122
+ LFormater.json_ensure_ascii = False
1123
+ LHandler.setFormatter (LFormater)
1124
+ self.addHandler (LHandler)
1125
+ #endfunction
1126
+ #endclass
1127
+
1128
+ """
1129
+ # logger
1130
+ logger = logging.getLogger(__name__)
1131
+
1132
+ # LEVEL
1133
+ NOTSET
1134
+ DEBUG - уровень отладочной информации, зачастую помогает при разработке приложения на машине программиста.
1135
+ INFO - уровень вспомогательной информации о ходе работы приложения/скрипта.
1136
+ WARNING - уровень предупреждения. Например, мы можем предупреждать о том, что та или иная функция будет удалена в будущих версиях вашего приложения.
1137
+ ERROR - с таким уровнем разработчики пишут логи с ошибками, например, о том, что внешний сервис недоступен.
1138
+ CRITICAL - уровень сообщений после которых работа приложения продолжаться не может.
1139
+
1140
+ # УСТАНОВИТЬ LEVEL
1141
+ logger.setLevel(logging.DEBUG)
1142
+ logger.setLevel(logging.INFO)
1143
+
1144
+ # ЗАПИСЬ в logger
1145
+ logger.debug('debug')
1146
+ logger.info('info')
1147
+ logger.warning('warning')
1148
+ logger.error('error')
1149
+ logger.exception('error')
1150
+ logger.critical('critical')
1151
+
1152
+ # handler
1153
+ Задача класса Handler и его потомков обрабатывать запись сообщений/логов. Т.е. Handler отвечает за то куда будут записаны сообщения. В базовом наборе logging предоставляет ряд готовых классов-обработчиков:
1154
+ SteamHandler - запись в поток, например, stdout или stderr.
1155
+ handler = StreamHandler(stream=sys.stdout)
1156
+ FileHandler - запись в файл, класс имеет множество производных классов с различной функциональностью
1157
+ ротация файлов логов по размеру, времени и т.д.)
1158
+ handler = StreamHandler(stream=)
1159
+ BaseRotatingHandler
1160
+ handler = BaseRotatingHandler(filename, mode, encoding=None, delay=False, errors=None
1161
+ RotatingFileHandler
1162
+ handler = RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None,
1163
+ delay=False, errors=None
1164
+ TimedRotatingFileHandler
1165
+ handler = TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None,
1166
+ delay=False, utc=False, atTime=None, errors=None)
1167
+
1168
+ SocketHandler - запись сообщений в сокет по TCP
1169
+ handler = SocketHandler(host, port)
1170
+ DatagramHandler - запись сообщений в сокет по UDP
1171
+ handler = DatagramHandler(host, port)
1172
+ SysLogHandler - запись в syslog
1173
+ handler = SysLogHandler(address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER, socktype=socket.SOCK_DGRAM)
1174
+ HTTPHandler - запись по HTTP
1175
+ handler = HTTPHandler(host, url, method='GET', secure=False, credentials=None, context=None)
1176
+ NullHandler = NullHandler
1177
+ handler = StreamHandler(stream=)
1178
+ WatchedFileHandler
1179
+ handler = WatchedFileHandler(filename, mode='a', encoding=None, delay=False, errors=None)
1180
+ NTEventLogHandler
1181
+ handler = NTEventLogHandler(appname, dllname=None, logtype='Application')
1182
+ SMTPHandler
1183
+ handler = SMTPHandler(mailhost, fromaddr, toaddrs, subject, credentials=None, secure=None, timeout=1.0)
1184
+ MemoryHandler
1185
+ handler = BufferingHandler(capacity)¶
1186
+ QueueHandler
1187
+ handler = QueueHandler(queue)
1188
+ QueueListener
1189
+ handler = QueueListener(queue, *handlers, respect_handler_level=False)¶
1190
+
1191
+ # ДОБАВИТЬ handler
1192
+ logger.addHandler(handler)
1193
+
1194
+ # Formatter
1195
+ #class logging.Formatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)
1196
+ formster = logging.Formatter (fmt=self.strfmt_03, datefmt=self.datefmt_02, style = '%', validate = True,
1197
+ defaults = {"ip": None} )
1198
+ handler.setFormatter(Formatter(fmt='[%(asctime)s: %(levelname)s] %(message)s'))
1199
+
1200
+ Параметр defaults может быть словарем со значениями по умолчанию для использования в настраиваемых полях.
1201
+ Например: logging.Formatter('%(ip)s %(message)s', defaults={"ip": None})
1202
+
1203
+ # Filter
1204
+ def filter_python(record: LogRecord) -> bool:
1205
+ return record.getMessage().find('python') != -1
1206
+ logger.addFilter(filter_python)
1207
+
1208
+ # LoggerAdapter
1209
+ class CustomLoggerAdapter(LoggerAdapter):
1210
+ def process(self, msg, kwargs):
1211
+ return f'{msg} from {self.extra["username"]}', kwargs
1212
+
1213
+ logger2 = logging.getLogger('adapter')
1214
+ logger2.setLevel(logging.DEBUG)
1215
+
1216
+ handler = StreamHandler(stream=sys.stdout)
1217
+ handler.setFormatter(Formatter(fmt='[%(asctime)s: %(levelname)s] %(message)s'))
1218
+
1219
+ adapter = CustomLoggerAdapter(logger2, {'username': 'adilkhash'})
1220
+
1221
+ logger2.addHandler(handler)
1222
+ adapter.error('failed to save')
1223
+
1224
+ # extra и не только
1225
+ logger.debug('debug info', extra={"response": response.text})
1226
+ Formatter(fmt='[%(asctime)s: %(levelname)s] %(message)s, response: %(response)s')
1227
+
1228
+ # Конфигурация logging
1229
+ Официальная документация рекомендует конфигурировать logging через python-словарь.
1230
+ Для этого необходимо вызвать функцию logging.config.dictConfig и передать ей специальный словарь.
1231
+ Схема словаря описана здесь. Я лишь вкратце пробегусь по основным ключам:
1232
+ version -
1233
+ ключ указывает версию конфига, рекомендуется наличие этого ключа со значением 1, нужно для обратной совместимости в случае, если в будущем появятся новые версии конфигов.
1234
+ disable_existing_loggers -
1235
+ запрещает или разрешает настройки для существующих логеров (на момент запуска), по умолчанию равен True
1236
+ formatters -
1237
+ настройки форматов логов
1238
+ handlers -
1239
+ настройки для обработчиков логов
1240
+ loggers -
1241
+ настройки существующих логеров
1242
+
1243
+ LOGGING_CONFIG = {
1244
+ 'version': 1,
1245
+ 'disable_existing_loggers': False,
1246
+
1247
+ 'formatters': {
1248
+ 'default_formatter': {
1249
+ 'format': '[%(levelname)s:%(asctime)s] %(message)s'
1250
+ },
1251
+ },
1252
+
1253
+ 'handlers': {
1254
+ 'stream_handler': {
1255
+ 'class': 'logging.StreamHandler',
1256
+ 'formatter': 'default_formatter',
1257
+ },
1258
+ },
1259
+
1260
+ 'loggers': {
1261
+ 'my_logger': {
1262
+ 'handlers': ['stream_handler'],
1263
+ 'level': 'DEBUG',
1264
+ 'propagate': True
1265
+ }
1266
+ }
1267
+ }
1268
+
1269
+ logging.config.dictConfig(LOGGING_CONFIG)
1270
+ logger = logging.getLogger('my_logger')
1271
+ logger.debug('debug log')
1272
+
1273
+ # Наследование в logging
1274
+ Ещё одним удобным механизмом в logging является "наследование" настроек корневого логера
1275
+ его потомками. Наследование задаётся через символ . в названии логера.
1276
+ То есть логер с названием my_package.logger1 унаследует все настройки, заданные для my_package.
1277
+ Давайте обновим пример выше, добавив в LOGGING_CONFIG настройку для my_package
1278
+
1279
+ LOGGING_CONFIG['loggers'].update (
1280
+ {
1281
+ 'my_package': {
1282
+ 'handlers': ['stream_handler'],
1283
+ 'level': 'DEBUG',
1284
+ 'propagate': False
1285
+ }
1286
+ }
1287
+ )
1288
+
1289
+ Available format attributes:
1290
+ args You shouldn’t need to format this yourself.
1291
+ The tuple of arguments merged into msg to produce message, or a dict whose values are used for the merge (when there is only one argument, and it is a dictionary).
1292
+ exc_info You shouldn’t need to format this yourself.
1293
+ Exception tuple (à la sys.exc_info) or, if no exception has occurred, None.
1294
+ msg You shouldn’t need to format this yourself.
1295
+ The format string passed in the original logging call. Merged with args to produce message, or an arbitrary object (see Using arbitrary objects as messages).
1296
+ stack_info You shouldn’t need to format this yourself.
1297
+ Stack frame information (where available) from the bottom of the stack in the current thread, up to and including the stack frame of the logging call which resulted in the creation of this record.
1298
+
1299
+ %(msg)s Message passed to logging call (same as %(message)s)
1300
+ %(hostname)s System hostname
1301
+ %(username)s System username
1302
+ %(programname)s System programname
1303
+
1304
+ %(asctime)s Time as human-readable string, when logging call was issued
1305
+ %(created)f Time as float when logging call was issued
1306
+ %(filename)s File name
1307
+ %(funcName)s Name of function containing the logging call
1308
+ %(levelname)s Text logging level
1309
+ %(levelno)s Integer logging level
1310
+ %(lineno)d Line number where the logging call was issued
1311
+ %(message)s Message passed to logging call (same as %(msg)s)
1312
+ %(module)s File name without extension where the logging call was issued
1313
+ %(msecs)d Millisecond part of the time when logging call was issued
1314
+ %(name)s Logger name
1315
+ %(pathname)s Full pathname to file containing the logging call
1316
+ %(process)d Process ID
1317
+ %(processName)s Process name
1318
+ %(relativeCreated)d - Time as integer in milliseconds when logging call was issued, relative to the time when logging module was loaded
1319
+ %(thread)d Thread ID
1320
+ %(threadName)s Thread name
1321
+
1322
+ %(asctime)s Human-readable time when the LogRecord was created. By default this is of the form ‘2003-07-08 16:49:45,896’ (the numbers after the comma are millisecond portion of the time).
1323
+ %(created)f Time when the LogRecord was created (as returned by time.time()).
1324
+ %(filename)s Filename portion of pathname.
1325
+ %(funcName)s Name of function containing the logging call.
1326
+ %(levelname)s Text logging level for the message ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL').
1327
+ %(levelno)s Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL).
1328
+ %(lineno)d Source line number where the logging call was issued (if available).
1329
+ %(message)s The logged message, computed as msg % args. This is set when Formatter.format() is invoked.
1330
+ %(module)s Module (name portion of filename).
1331
+ %(msecs)d Millisecond portion of the time when the LogRecord was created.
1332
+ %(name)s Name of the logger used to log the call.
1333
+ %(pathname)s Full pathname of the source file where the logging call was issued (if available).
1334
+ %(process)d Process ID (if available).
1335
+ %(processName)s Process name (if available).
1336
+ %(relativeCreated)d Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.
1337
+ %(thread)d Thread ID (if available).
1338
+ %(threadName)s Thread name (if available).
1339
+
1340
+ Emoji
1341
+ You can use colors for text as others mentioned in their answers to have colorful text with a background or foreground color.
1342
+ But you can use emojis instead! for example, you can use ⚠️ for warning messages and 🛑 for error messages.
1343
+ Or simply use these notebooks as a color:
1344
+
1345
+ print("📕: error message")
1346
+ print("📙: warning message")
1347
+ print("📗: ok status message")
1348
+ print("📘: action message")
1349
+ print("📓: canceled status message")
1350
+ print("📔: Or anything you like and want to recognize immediately by color")
1351
+
1352
+ 🎁 Bonus:
1353
+ This method also helps you to quickly scan and find logs directly in the source code.
1354
+
1355
+ How to open emoji picker?
1356
+ mac os: control + command + space
1357
+ windows: win + .
1358
+ linux: control + . or control + ;
1359
+
1360
+ # Отправляем логи в Telegram
1361
+
1362
+ """
1363
+ #endclass
1364
+
1365
+ #-------------------------------------------------
1366
+ # Функциональное исполнение
1367
+ #-------------------------------------------------
1368
+
1369
+ def GetLogDirLogon () -> str:
1370
+ """GetLogDirLogon"""
1371
+ #beginfunction
1372
+ """
1373
+ if @LDomain <> ''
1374
+ LogDir = LogDir + '\\'+@LDomain
1375
+ endif
1376
+ s = AddCharR ('_', $USERID, 15)
1377
+ LogFile = s + "_" + UCase (@WKSTA)+'.log'
1378
+ """
1379
+ return ''
1380
+ #endfunction
1381
+
1382
+ def GetLogFileName () -> str:
1383
+ """GetLogFileName"""
1384
+ #beginfunction
1385
+ LResult = LUDateTime.Now ().strftime ('%Y%m%d') + '.log'
1386
+ return LResult
1387
+ #endfunction
1388
+
1389
+ def GetLogFileNameSufix (ASufix: str) -> str:
1390
+ """GetLogFileNameSufix"""
1391
+ #beginfunction
1392
+ LResult = LUDateTime.Now ().strftime ('%Y%m%d') + ASufix + '.log'
1393
+ return LResult
1394
+ #endfunction
1395
+
1396
+ #-------------------------------------------------
1397
+ # LogFileName(ALog: str, ALogDir: str, ALogFile: str) -> str:
1398
+ #-------------------------------------------------
1399
+ def LogFileName(ALog: int, ALogDir: str, ALogFile: str) -> str:
1400
+ """LogFileName"""
1401
+ #beginfunction
1402
+ LToday: datetime = LUDateTime.Now ()
1403
+ match ALog:
1404
+ case 1|3|10|30:
1405
+ LLogDir = ALogDir
1406
+ if len (ALogDir) == 0:
1407
+ # LLogDir = os.environ['TEMP']
1408
+ LLogDir = LUFile.GetTempDir()
1409
+ #endif
1410
+ LLogFile = ALogFile
1411
+ if ALogFile == '':
1412
+ s = LUDateTime.DateTimeStr (False, LToday, LUDateTime.cFormatDateYYMMDD_01, False)
1413
+ LLogFile = s+'.log'
1414
+ #endif
1415
+ LLogFileName = s.path.join([LLogDir, LLogFile])
1416
+ if ALog == 10 or ALog == 30:
1417
+ if LUFile.FileExists(LLogFileName):
1418
+ try:
1419
+ LUFile.FileDelete(LLogFileName)
1420
+ except:
1421
+ # except LUErrors.LUFileError_FileERROR as ERROR:
1422
+ s = f'Ошибка при удалении файла {LLogFileName}'
1423
+ LoggerTOOLS.error (s)
1424
+ else:
1425
+ ...
1426
+ #endif
1427
+ #endif
1428
+ case _:
1429
+ LLogFileName = ""
1430
+ #endmatch
1431
+ return LLogFileName
1432
+ #endfunction
1433
+
1434
+ #--------------------------------------------------------------------------------
1435
+ # LogAdd (ALog: int, ALogFile: str, AOpt: str, AMessage: str,
1436
+ # AStyles = '', AFG8 = '', ABG8 = '', AFG256 = '', ABG256 = '', AESC = ''):
1437
+ #--------------------------------------------------------------------------------
1438
+ def LogAdd (ALog: int, ALogFile: str, AOpt: TTypeLogString, AMessage: str):
1439
+ """LogAdd"""
1440
+
1441
+ def _WriteConsole(_s, T):
1442
+ #beginfunction
1443
+ if not LUSupport.ISTerminal ():
1444
+ LCOLOR = COLORS_tls.get (T)
1445
+ if LCOLOR is not None:
1446
+ LFmt = LUConsole.sBEGIN_oct + LCOLOR + _s + LUConsole.sRESET
1447
+ else:
1448
+ LFmt = _s
1449
+ LUConsole.WriteLN (LFmt)
1450
+ else:
1451
+ LCOLOR = COLORS_tls_rich.get (T)
1452
+ LFmt_default = _s
1453
+ LFmt = '[' + LCOLOR + ']' + _s
1454
+ GConsoleRich.print (LFmt)
1455
+ #endif
1456
+ #endfunction
1457
+
1458
+ def _WriteFile(_s):
1459
+ #beginfunction
1460
+
1461
+ LEncoding = LUFile.GetFileEncoding (ALogFile)
1462
+ if LEncoding == '':
1463
+ LEncoding = LUFile.cDefaultEncoding
1464
+
1465
+ LEncoding = LUFile.cDefaultEncoding
1466
+ # sWIN = s.encode (encoding = 'UTF-8').decode(encoding = 'ANSI')
1467
+ # sWIN = s.encode (encoding = 'WINDOWS-1251').decode(encoding = 'UTF-8')
1468
+ # Это работает !!!!!!!!!!!!!!!!!
1469
+ try:
1470
+ with open (ALogFile, 'a+', encoding = LEncoding) as LFile:
1471
+ LFile.write (_s+'\n')
1472
+ except:
1473
+ _s = f'_WriteFile: Неправильная кодировка журнала!'
1474
+ LoggerTOOLS.error (_s)
1475
+ #endtry
1476
+ #endfunction
1477
+
1478
+ #beginfunction
1479
+ LToday = LUDateTime.Now ()
1480
+ if AOpt == TTypeLogString.tlsTEXT:
1481
+ s = AMessage
1482
+ else:
1483
+ s = LUDateTime.DateTimeStr(False, LToday, LUDateTime.cFormatDateTimeLog01, True)+' '+\
1484
+ AOpt.value+' '+AMessage
1485
+ match ALog:
1486
+ case 1|10:
1487
+ _WriteConsole (s, AOpt)
1488
+ _WriteFile (s)
1489
+ case 2:
1490
+ _WriteConsole (s, AOpt)
1491
+ case 3|30:
1492
+ _WriteConsole (s, AOpt)
1493
+ _WriteFile (s)
1494
+ #endmatch
1495
+ #endfunction
1496
+
1497
+ #--------------------------------------------------------------------------------
1498
+ # LogAddFile (ALog: int, ALogFile: str, AOpt: str, AFileName: str,
1499
+ # AStyles='', AFG8='', ABG8='', AFG256='', ABG256='', AESC=''):
1500
+ #--------------------------------------------------------------------------------
1501
+ def LogAddFile (ALog: int, ALogFile: str, AOpt: TTypeLogString, AFileName: str):
1502
+ """LogAddFile"""
1503
+ #beginfunction
1504
+ if LUFile.FileExists (AFileName):
1505
+ try:
1506
+ LEncoding = LUFile.GetFileEncoding (AFileName)
1507
+ if LEncoding == '':
1508
+ LEncoding = LUFile.cDefaultEncoding
1509
+ with open (AFileName, 'r', encoding = LEncoding) as LFile:
1510
+ for s in LFile:
1511
+ Ls = s.split ('\n')[0]
1512
+ LogAdd (ALog, ALogFile, AOpt, Ls)
1513
+ #endfor
1514
+ #endwith
1515
+ except:
1516
+ s = f'LogAddFile: Неправильная кодировка журнала!'+AFileName
1517
+ LoggerTOOLS.error (s)
1518
+ #endtry
1519
+ #endif
1520
+ #endfunction
1521
+
1522
+ #-------------------------------------------------
1523
+ # GLOBAL
1524
+ #-------------------------------------------------
1525
+ def SetFormatterForLogger (ALogger: logging.Logger):
1526
+ """SetFormatterForLogger"""
1527
+ #beginfunction
1528
+ for item in ALogger.handlers:
1529
+ if type (item.formatter) is pythonjsonlogger.jsonlogger.JsonFormatter:
1530
+ item.formatter.json_ensure_ascii = False
1531
+ #endif
1532
+ if type (item) is logging.StreamHandler or type (item) is TStreamHandler:
1533
+ # if LUSupport.ISTerminal ():
1534
+ # TStreamHandler(item).setLevel(logging.CRITICAL)
1535
+ # # print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
1536
+ # ...
1537
+ # #endif
1538
+ Lfmt = item.formatter._fmt
1539
+ Ldatefmt = item.formatter.datefmt
1540
+ LFormaterConsole = TFormatter (fmt = Lfmt, datefmt = Ldatefmt)
1541
+ item.setFormatter (LFormaterConsole)
1542
+ #endif
1543
+ #enfor
1544
+ #endfunction
1545
+
1546
+ def PrintHandlers (ALogger: logging.Logger):
1547
+ """Printhandlers"""
1548
+ #beginfunction
1549
+ for item in ALogger.root.handlers:
1550
+ s = f'{item.name}={item}'
1551
+ # LoggerTOOLS.info(s)
1552
+ if type(item) is logging.StreamHandler:
1553
+ LoggerTOOLS.info ('logging.StreamHandler='+s)
1554
+ # Lfmt = item.formatter._fmt
1555
+ # Ldatefmt = item.formatter.datefmt
1556
+ # LFormaterConsole = TFormatter (AUseColor=True, fmt=Lfmt, datefmt=Ldatefmt)
1557
+ ...
1558
+ #endif
1559
+ if type (item) is TStreamHandler:
1560
+ LoggerTOOLS.info ('TStreamHandler='+s)
1561
+ # Lfmt = item.formatter._fmt
1562
+ # Ldatefmt = item.formatter.datefmt
1563
+ # LFormaterConsole = TFormatter (AUseColor=True, fmt=Lfmt, datefmt=Ldatefmt)
1564
+ ...
1565
+ #endif
1566
+ #enfor
1567
+ #endfunction
1568
+
1569
+ def GetHandler (ALogger: logging.Logger, ANameHandler: str):
1570
+ """Printhandlers"""
1571
+ #beginfunction
1572
+ for item in ALogger.root.handlers:
1573
+ s = f'{item.name}={item}'
1574
+ # LoggerTOOLS.info(s)
1575
+ if item.name == ANameHandler:
1576
+ return item
1577
+ #endif
1578
+ #enfor
1579
+ #endfunction
1580
+
1581
+ def WinToUnix (Astr: str) -> str:
1582
+ """WinToUnix"""
1583
+ #beginfunction
1584
+ LOSInfo = LUos.TOSInfo ()
1585
+ print ('HostName = ' + LOSInfo.node)
1586
+ print ('OS = ' + LOSInfo.system)
1587
+ # print ('OS = ' + LOSInfo.uname.system)
1588
+ match LOSInfo.system.upper ():
1589
+ case 'LINUX':
1590
+ Lstr = os.path.abspath (Astr).replace ("\\", "/")
1591
+ case 'WINDOWS':
1592
+ Lstr = Astr
1593
+ case _:
1594
+ # print ('INFO: Only LINUX or WINDOWS')
1595
+ Lstr = Astr
1596
+ #endmatch
1597
+ #endfunction
1598
+
1599
+ #-------------------------------------------------
1600
+ # LOGGING_CONFIG
1601
+ #-------------------------------------------------
1602
+ LOGGING_CONFIG = \
1603
+ {
1604
+ 'version': 1,
1605
+ 'disable_existing_loggers': 1,
1606
+ 'loggers': {
1607
+ 'root': {
1608
+ 'handlers': [
1609
+ 'CONSOLE',
1610
+ 'FILE_01'
1611
+ ],
1612
+ 'level': 'DEBUG',
1613
+ 'propagate': 1
1614
+ },
1615
+ 'log01': {
1616
+ 'handlers': [
1617
+ 'FILE_01'
1618
+ ],
1619
+ 'level': 'DEBUG',
1620
+ 'propagate': 0,
1621
+ 'qualname': 'log01'
1622
+ },
1623
+ 'log02': {
1624
+ 'handlers': [
1625
+ 'FILE_02'
1626
+ ],
1627
+ 'level': 'DEBUG',
1628
+ 'propagate': 0,
1629
+ 'qualname': 'log02'
1630
+ }
1631
+ },
1632
+ 'handlers': {
1633
+ 'CONSOLE': {
1634
+ # 'class': 'logging.StreamHandler',
1635
+ 'class': 'LULog.TStreamHandler',
1636
+ 'level': 'DEBUG',
1637
+ 'formatter': 'FORMAT_01',
1638
+ 'stream': 'ext://sys.stdout'
1639
+ },
1640
+ 'FILE_01': {
1641
+ 'class': 'logging.handlers.RotatingFileHandler',
1642
+ 'level': 'DEBUG',
1643
+ 'formatter': 'FORMAT_01',
1644
+ 'maxBytes': 10000000,
1645
+ 'backupCount': 5,
1646
+ 'filename': 'LOGGING_CONFIG.log'
1647
+ },
1648
+ 'FILE_02': {
1649
+ # 'class': 'logging.handlers.TimedRotatingFileHandler',
1650
+ 'class': 'logging.handlers.RotatingFileHandler',
1651
+ 'level': 'INFO',
1652
+ 'formatter': 'FORMAT_json',
1653
+ # 'interval': 'M',
1654
+ 'maxBytes': 1024,
1655
+ 'backupCount': 5,
1656
+ 'filename': 'LOGGING_CONFIG_json.log'
1657
+ }
1658
+ },
1659
+ 'formatters': {
1660
+ 'FORMAT_01': {
1661
+ 'format': Cstrfmt_01,
1662
+ 'datefmt': Cdatefmt_01
1663
+ },
1664
+ 'FORMAT_json': {
1665
+ 'class': 'pythonjsonlogger.jsonlogger.JsonFormatter',
1666
+ 'format': Cstrfmt_01,
1667
+ 'datefmt': Cdatefmt_01
1668
+ }
1669
+ },
1670
+ }
1671
+
1672
+ #-------------------------------------------------
1673
+ # CreateLoggerCONFIG
1674
+ #-------------------------------------------------
1675
+ def CreateLoggerCONFIG (AFileNameCONFIG: str, ALogerName: str,
1676
+ ADirectoryLOG: str, AFileNameLOG: str, AFileNameLOGjson: str) -> logging.Logger:
1677
+ """CreateLoggerCONFIG"""
1678
+ #beginfunction
1679
+ global CONFIG
1680
+ CONFIG = {}
1681
+
1682
+ LPath = LUFile.ExtractFileDir(__file__)
1683
+ LFileNameCONFIG = os.path.join (LPath, AFileNameCONFIG)
1684
+ if LUFile.FileExists(LFileNameCONFIG):
1685
+ # читаем конфигурацию из файла
1686
+ try:
1687
+ with open (LFileNameCONFIG, 'r') as FileCONFIG:
1688
+ CONFIG = json.load(FileCONFIG)
1689
+ #endwith
1690
+ except FileNotFoundError as ERROR:
1691
+ print ('Невозможно открыть файл', ERROR)
1692
+ GLULogger.error('Невозможно открыть файл')
1693
+ #endtry
1694
+ else:
1695
+ CONFIG = copy.deepcopy (LOGGING_CONFIG)
1696
+ #endif
1697
+ #-------------------------------------------------------------------
1698
+ # CONFIG = copy.deepcopy (LOGGING_CONFIG)
1699
+ #-------------------------------------------------------------------
1700
+
1701
+ if AFileNameLOG == '':
1702
+ LOptionValue_01 = CONFIG['handlers']['FILE_01']['filename']
1703
+ print ('LOptionValue_01:',LOptionValue_01)
1704
+ LFileNameLOG = LUFile.ExtractFileName (LOptionValue_01)
1705
+ else:
1706
+ LFileNameLOG = LUFile.ExtractFileName (AFileNameLOG)
1707
+ #endif
1708
+ print('LFileNameLOG:',LFileNameLOG)
1709
+
1710
+ if AFileNameLOGjson == '':
1711
+ LOptionValue_02 = CONFIG['handlers']['FILE_02']['filename']
1712
+ print ('LOptionValue_02:',LOptionValue_02)
1713
+ LFileNameLOGjson = LUFile.ExtractFileName (LOptionValue_02)
1714
+ else:
1715
+ LFileNameLOGjson = LUFile.ExtractFileName (AFileNameLOGjson)
1716
+ #endif
1717
+ print('LFileNameLOGjson:',LFileNameLOGjson)
1718
+
1719
+ if ADirectoryLOG == '':
1720
+ # log будет создан в текущем каталоге (по умолчанию)
1721
+ LDirectoryLOG = LUos.GetCurrentDir ()
1722
+ else:
1723
+ # log будет создан в ADirectoryLOG
1724
+ LDirectoryLOG = LUFile.ExpandFileName (ADirectoryLOG)
1725
+ #endif
1726
+ # print('LDirectoryLOG:',LDirectoryLOG)
1727
+ if not LUFile.DirectoryExists (LDirectoryLOG):
1728
+ LUFile.ForceDirectories(LDirectoryLOG)
1729
+ #endif
1730
+
1731
+ # установить имена log файлов в CONFIG
1732
+ LOptionValue_01 = os.path.join (LDirectoryLOG, LFileNameLOG)
1733
+ print('LOptionValue_01:', LOptionValue_01)
1734
+ CONFIG ['handlers'] ['FILE_01'] ['filename'] = LOptionValue_01
1735
+
1736
+ LOptionValue_02 = os.path.join (LDirectoryLOG, LFileNameLOGjson)
1737
+ print('LOptionValue_02:', LOptionValue_02)
1738
+ CONFIG ['handlers'] ['FILE_02'] ['filename'] = LOptionValue_02
1739
+
1740
+ if len(CONFIG) > 0:
1741
+ #-------------------------------------------------------------------
1742
+ LFileNameCONFIG = os.path.join (LUos.GetCurrentDir (), CDefaultFileLogCONFIG)
1743
+ LUDict.SaveDictSTR (CONFIG, LFileNameCONFIG)
1744
+ #-------------------------------------------------------------------
1745
+ # читаем конфигурацию из словаря
1746
+ logging.config.dictConfig (CONFIG)
1747
+ # создаем регистратор
1748
+ LResult = logging.getLogger (ALogerName)
1749
+ # установить форматер
1750
+ SetFormatterForLogger (LResult)
1751
+ return LResult
1752
+ else:
1753
+ return None
1754
+ #endfunction
1755
+
1756
+ #-------------------------------------------------
1757
+ # CreateLoggerYAML
1758
+ #-------------------------------------------------
1759
+ def CreateLoggerYAML (AFileNameYAML: str, ALogerName: str, ADirectoryLOG: str, AFileNameLOG: str, AFileNameLOGjson: str) -> logging.Logger:
1760
+ """CreateLoggerFILEYAML"""
1761
+ #beginfunction
1762
+ global CONFIG_YAML
1763
+ CONFIG_YAML = {}
1764
+
1765
+ LPath = LUFile.ExtractFileDir(__file__)
1766
+ LFileNameYAML = os.path.join (LPath, AFileNameYAML)
1767
+ if LUFile.FileExists (LFileNameYAML):
1768
+ # читаем конфигурацию из файла
1769
+ try:
1770
+ with (open (LFileNameYAML, 'r') as FileCONFIG_YAML):
1771
+ CONFIG_YAML = yaml.load(FileCONFIG_YAML, Loader=yaml.FullLoader)
1772
+ #endwith
1773
+ except FileNotFoundError as ERROR:
1774
+ print ('Невозможно открыть файл', ERROR)
1775
+ GLULogger.error ('Невозможно открыть файл')
1776
+ #endtry
1777
+ else:
1778
+ CONFIG_YAML = copy.deepcopy (LOGGING_CONFIG)
1779
+ #endif
1780
+ #-------------------------------------------------------------------
1781
+ # CONFIG_YAML = copy.deepcopy (LOGGING_CONFIG)
1782
+ #-------------------------------------------------------------------
1783
+
1784
+ if AFileNameLOG == '':
1785
+ LOptionValue_01 = CONFIG_YAML ['handlers'] ['FILE_01'] ['filename']
1786
+ print ('LOptionValue_01:', LOptionValue_01)
1787
+ LFileNameLOG = LUFile.ExtractFileName (LOptionValue_01)
1788
+ else:
1789
+ LFileNameLOG = LUFile.ExtractFileName (AFileNameLOG)
1790
+ #endif
1791
+ print ('LFileNameLOG:', LFileNameLOG)
1792
+
1793
+ if AFileNameLOGjson == '':
1794
+ LOptionValue_02 = CONFIG_YAML ['handlers'] ['FILE_02'] ['filename']
1795
+ print ('LOptionValue_02:', LOptionValue_02)
1796
+ LFileNameLOGjson = LUFile.ExtractFileName (LOptionValue_02)
1797
+ else:
1798
+ LFileNameLOGjson = LUFile.ExtractFileName (AFileNameLOGjson)
1799
+ #endif
1800
+ print ('LFileNameLOGjson:', LFileNameLOGjson)
1801
+
1802
+ if ADirectoryLOG == '':
1803
+ # log будет создан в текущем каталоге (по умолчанию)
1804
+ LDirectoryLOG = LUos.GetCurrentDir ()
1805
+ else:
1806
+ # log будет создан в ADirectoryLOG
1807
+ LDirectoryLOG = LUFile.ExpandFileName (ADirectoryLOG)
1808
+ #endif
1809
+ # print('LDirectoryLOG:',LDirectoryLOG)
1810
+ if not LUFile.DirectoryExists (LDirectoryLOG):
1811
+ LUFile.ForceDirectories(LDirectoryLOG)
1812
+ #endif
1813
+
1814
+ # установить имена log файлов в CONFIG
1815
+ LOptionValue_01 = os.path.join (LDirectoryLOG, LFileNameLOG)
1816
+ print('LOptionValue_01:', LOptionValue_01)
1817
+ CONFIG_YAML ['handlers'] ['FILE_01'] ['filename'] = LOptionValue_01
1818
+
1819
+ LOptionValue_02 = os.path.join (LDirectoryLOG, LFileNameLOGjson)
1820
+ print('LOptionValue_02:', LOptionValue_02)
1821
+ CONFIG_YAML ['handlers'] ['FILE_02'] ['filename'] = LOptionValue_02
1822
+
1823
+ if len (CONFIG_YAML) > 0:
1824
+ #-------------------------------------------------------------------
1825
+ LFileNameYAML = os.path.join (LUos.GetCurrentDir (), CDefaultFileLogYAML)
1826
+ LUDict.SaveDictSTR (CONFIG_YAML, LFileNameYAML)
1827
+ #-------------------------------------------------------------------
1828
+ # читаем конфигурацию из словаря
1829
+ logging.config.dictConfig (CONFIG_YAML)
1830
+ # создаем регистратор
1831
+ LResult = logging.getLogger (ALogerName)
1832
+ # установить форматер
1833
+ SetFormatterForLogger (LResult)
1834
+ return LResult
1835
+ else:
1836
+ return None
1837
+ #endif
1838
+
1839
+ #endfunction
1840
+
1841
+ #-------------------------------------------------
1842
+ # CreateLoggerFILEINI
1843
+ #-------------------------------------------------
1844
+ def CreateLoggerFILEINI (AFileNameINI: str, ALogerName: str,
1845
+ ADirectoryLOG: str, AFileNameLOG: str, AFileNameLOGjson: str) -> logging.Logger:
1846
+ """CreateLoggerFILEINI"""
1847
+ #beginfunction
1848
+ LDirectoryLOG = ADirectoryLOG
1849
+
1850
+ # читаем конфигурацию из файла INI
1851
+ LFileNameINI = lyr.LUFile.ExpandFileName (AFileNameINI)
1852
+ if lyr.LUFile.FileExists (LFileNameINI):
1853
+ # существует файл, который можно редактировать
1854
+ SetEditINI = True
1855
+ LPathINI = LUFile.ExtractFileDir (LFileNameINI)
1856
+ LFileNameINI = os.path.join (LPathINI, LUFile.ExtractFileName (AFileNameINI))
1857
+ else:
1858
+ LPathINI = lyr.LUos.GetCurrentDir ()
1859
+ LFileNameINI = os.path.join (LPathINI, lyr.LUFile.ExtractFileName (AFileNameINI))
1860
+ # print ('LFileNameINI: ', LFileNameINI)
1861
+ if lyr.LUFile.FileExists (LFileNameINI):
1862
+ # существует файл в текущем каталоге, который можно редактировать
1863
+ SetEditINI = True
1864
+ else:
1865
+ # берем имя файла из проекта, если оно есть
1866
+ LPathINI = lyr.LUFile.ExtractFileDir (__file__)
1867
+ LFileNameINI = os.path.join (LPathINI, lyr.LUFile.ExtractFileName (AFileNameINI))
1868
+ SetEditINI = False
1869
+ #endif
1870
+ #endif
1871
+ # print ('LPathINI:',LPathINI)
1872
+ # print ('LFileNameINI:',LFileNameINI)
1873
+
1874
+ if not SetEditINI:
1875
+ pass
1876
+ else:
1877
+ LINIFile = LUParserINI.TINIFile ()
1878
+ LINIFile.FileNameINI = LFileNameINI
1879
+ LOptionName = 'args'
1880
+ if AFileNameLOG == '':
1881
+ LSectionName_01 = 'handler_FILE_01'
1882
+ LOptionValue_01 = LINIFile.GetOption(LSectionName_01, LOptionName)
1883
+ print ('LOptionValue_01:',LOptionValue_01)
1884
+ LFileNameLOG = LUFile.ExtractFileName (LOptionValue_01.split([',', '('])[0])
1885
+ else:
1886
+ LFileNameLOG = LUFile.ExtractFileName (AFileNameLOG)
1887
+ #endif
1888
+ # print('LFileNameLOG:',LFileNameLOG)
1889
+ if AFileNameLOGjson == '':
1890
+ LSectionName_02 = 'handler_FILE_02'
1891
+ LOptionValue_02 = LINIFile.GetOption(LSectionName_02, LOptionName)
1892
+ print ('LOptionValue_02:',LOptionValue_02)
1893
+ LFileNameLOGjson = LUFile.ExtractFileName (LOptionValue_02.split([',', '('])[0])
1894
+ else:
1895
+ LFileNameLOGjson = LUFile.ExtractFileName (AFileNameLOGjson)
1896
+ #endif
1897
+ # print('LFileNameLOGjson:',LFileNameLOGjson)
1898
+
1899
+ # установить имена log файлов в ini
1900
+ LOptionValue_01 = "('" + os.path.join (LDirectoryLOG, LFileNameLOG) + "',)"
1901
+ if LUos.GOSInfo.system == 'Windows':
1902
+ LOptionValue_01 = LOptionValue_01.replace ('\\', "\\\\")
1903
+ #endif
1904
+ if LUos.GOSInfo.system == 'Linux':
1905
+ raise 'Linux не поддерживается'
1906
+ #endif
1907
+ # print(LOptionValue_01)
1908
+
1909
+ LINIFile.SetOption ('handler_FILE_01', LOptionName, LOptionValue_01)
1910
+ LOptionValue_02 = "('" + os.path.join (LDirectoryLOG, LFileNameLOGjson) + "',)"
1911
+
1912
+ if LUos.GOSInfo.system == 'Windows':
1913
+ LOptionValue_02 = LOptionValue_02.replace ("\\", "\\\\")
1914
+ #endif
1915
+ if LUos.GOSInfo.system == 'Linux':
1916
+ raise 'Linux не поддерживается'
1917
+ #endif
1918
+ # print(LOptionValue_02)
1919
+
1920
+ LINIFile.SetOption ('handler_FILE_02', LOptionName, LOptionValue_02)
1921
+ LINIFile.UpdateFileINI ()
1922
+ #endif
1923
+
1924
+ if ADirectoryLOG == '':
1925
+ # log будет создан в текущем каталоге (по умолчанию)
1926
+ LDirectoryLOG = LUos.GetCurrentDir ()
1927
+ else:
1928
+ # log будет создан в ADirectoryLOG
1929
+ LDirectoryLOG = lyr.LUFile.ExpandFileName (ADirectoryLOG)
1930
+ #endif
1931
+ # print('LDirectoryLOG:',LDirectoryLOG)
1932
+ if not lyr.LUFile.DirectoryExists (LDirectoryLOG):
1933
+ lyr.LUFile.ForceDirectories(LDirectoryLOG)
1934
+ #endif
1935
+
1936
+ print(LFileNameINI)
1937
+ logging.config.fileConfig (LFileNameINI, disable_existing_loggers=True, encoding=lyr.LUFile.cDefaultEncoding)
1938
+ # logging.config.fileConfig (LFileNameINI, disable_existing_loggers=True, encoding='cp1251')
1939
+
1940
+ # создаем регистратор
1941
+ LResult = logging.getLogger (ALogerName)
1942
+ # установить форматер
1943
+ SetFormatterForLogger (LResult)
1944
+
1945
+ return LResult
1946
+ #endfunction
1947
+
1948
+ #-------------------------------------------------
1949
+ # CreateLoggerBASIC
1950
+ #-------------------------------------------------
1951
+ def CreateLoggerBASIC (ALevel, AFileNameLOG: str, ALogerName: str) -> logging.Logger:
1952
+ """CreateTLoggingCONFIG"""
1953
+ #beginfunction
1954
+ # читаем конфигурацию из
1955
+ if len(AFileNameLOG) > 0:
1956
+ logging.basicConfig (level = ALevel, filename = AFileNameLOG, style='%',
1957
+ datefmt = Cdatefmt_01, format = Cstrfmt_01)
1958
+ else:
1959
+ logging.basicConfig (level = ALevel, stream=sys.stdout, style='%',
1960
+ datefmt = Cdatefmt_01, format = Cstrfmt_01)
1961
+ # создаем регистратор
1962
+ LResult = logging.getLogger (ALogerName)
1963
+ # установить форматер
1964
+ SetFormatterForLogger (LResult)
1965
+ return LResult
1966
+ #endfunction
1967
+
1968
+ #-------------------------------------------------
1969
+ # LoggerTLogger
1970
+ #-------------------------------------------------
1971
+ def CreateTLogger (ALogerName: str) -> TLogger:
1972
+ """CreateTLogging"""
1973
+ #beginfunction
1974
+ logging.setLoggerClass (TLogger)
1975
+ # создаем регистратор
1976
+ LResult = TLogger(ALogerName)
1977
+ SetFormatterForLogger (LResult)
1978
+ return LResult
1979
+ #endfunction
1980
+
1981
+ #-------------------------------------------------
1982
+ # FileMemoLog
1983
+ #-------------------------------------------------
1984
+ def CreateTFileMemoLog () -> TFileMemoLog:
1985
+ """CreateTFileMemoLog"""
1986
+ #beginfunction
1987
+ LFileMemoLog = TFileMemoLog ()
1988
+ return LFileMemoLog
1989
+ #endfunction
1990
+
1991
+ #-------------------------------------------------
1992
+ # Инициализация системы logging
1993
+ #-------------------------------------------------
1994
+ GLoggerFILEINI = None
1995
+ GLoggerCONFIG = None
1996
+ LoggerTOOLS = None
1997
+ LoggerAPPS = None
1998
+ LoggerTLogger = None
1999
+ FileMemoLog = None
2000
+
2001
+ def STARTLogging (T: TTypeSETUPLOG, ADirectoryLOG: str, AFileNameLOG: str, AFileNameLOGjson: str) -> None:
2002
+ """STARTLogging"""
2003
+ #beginfunction
2004
+ global STATLogging
2005
+ STATLogging = False
2006
+ # print (sys._getframe (0).f_code.co_name, '...')
2007
+
2008
+ # print (inspect.currentframe().f_code.co_name, '...')
2009
+ # print (inspect.stack () [0] [3], '...')
2010
+ # print (traceback.extract_stack () [-1].name, '...')
2011
+
2012
+ global GLoggerFILEINI
2013
+ global GLoggerCONFIG
2014
+ global LoggerTOOLS
2015
+ global LoggerAPPS
2016
+ global LoggerTLogger
2017
+ global FileMemoLog
2018
+
2019
+ AddLevelName ()
2020
+
2021
+ match T:
2022
+ case TTypeSETUPLOG.tslCONFIG:
2023
+ GLoggerCONFIG = CreateLoggerCONFIG (CDefaultFileLogCONFIG, 'root', ADirectoryLOG, AFileNameLOG,
2024
+ AFileNameLOGjson)
2025
+ case TTypeSETUPLOG.tslYAML:
2026
+ GLoggerYAML = CreateLoggerYAML (CDefaultFileLogYAML, 'root', ADirectoryLOG, AFileNameLOG,
2027
+ AFileNameLOGjson)
2028
+ case TTypeSETUPLOG.tslINI:
2029
+ GLoggerFILEINI = CreateLoggerFILEINI (CDefaultFileLogINI, 'root', ADirectoryLOG, AFileNameLOG,
2030
+ AFileNameLOGjson)
2031
+ case _:
2032
+ ...
2033
+ #endmatch
2034
+
2035
+ #-------------------------------------------------
2036
+ # GLoggerBASIC = CreateLoggerBASIC (logging.DEBUG, 'LOG\\' + CDefaultFileLogFILEBASIC, 'root')
2037
+ # GLoggerBASIC = CreateLoggerBASIC (logging.DEBUG, '', 'root')
2038
+ #-------------------------------------------------
2039
+
2040
+ #-------------------------------------------------
2041
+ # LoggerTOOLS
2042
+ #-------------------------------------------------
2043
+ CLoggerTOOLS = 'TOOLS__'
2044
+ LoggerTOOLS = logging.getLogger (CLoggerTOOLS)
2045
+ LoggerTOOLS.disabled = False
2046
+ # print('LoggerTOOLS' in vars () or 'LoggerTOOLS' in globals ())
2047
+ # print('LoggerTOOLS' in vars ())
2048
+ # print('LoggerTOOLS' in globals ())
2049
+ # if not ('LoggerTOOLS' in vars () or 'LoggerTOOLS' in globals ()):
2050
+ # CLoggerTOOLS = 'TOOLS__'
2051
+ # LoggerTOOLS = logging.getLogger (CLoggerTOOLS)
2052
+ # #endif
2053
+
2054
+ #-------------------------------------------------
2055
+ # LoggerAPPS
2056
+ #-------------------------------------------------
2057
+ CLoggerAPPS = 'APPS___'
2058
+ LoggerAPPS = logging.getLogger(CLoggerAPPS)
2059
+
2060
+ #-------------------------------------------------
2061
+ # LoggerTLogger
2062
+ #-------------------------------------------------
2063
+ CTLogger = 'TLOGGER'
2064
+ LoggerTLogger = CreateTLogger (CTLogger)
2065
+
2066
+ #-------------------------------------------------
2067
+ # FileMemoLog
2068
+ #-------------------------------------------------
2069
+ FileMemoLog = CreateTFileMemoLog ()
2070
+
2071
+ #-------------------------------------------------
2072
+ # Отключить журнал 'chardet.charsetprober'
2073
+ #-------------------------------------------------
2074
+ logger = logging.getLogger('chardet.charsetprober')
2075
+ logger.setLevel(logging.INFO)
2076
+ logger = logging.getLogger('chardet.universaldetector')
2077
+ logger.setLevel(logging.INFO)
2078
+ #-------------------------------------------------
2079
+ # Отключить журнал 'pytube.extract'
2080
+ #-------------------------------------------------
2081
+ logger = logging.getLogger('pytube.extract')
2082
+ logger.setLevel(logging.INFO)
2083
+ #-------------------------------------------------
2084
+ # Отключить журнал 'pytube.streams'
2085
+ #-------------------------------------------------
2086
+ logger = logging.getLogger('pytube.streams')
2087
+ logger.setLevel(logging.INFO)
2088
+ #-------------------------------------------------
2089
+ # Отключить журнал 'pytube.cipher'
2090
+ #-------------------------------------------------
2091
+ logger = logging.getLogger('pytube.cipher')
2092
+ logger.setLevel(logging.INFO)
2093
+ #-------------------------------------------------
2094
+ # Отключить журнал 'pytube.helpers'
2095
+ #-------------------------------------------------
2096
+ logger = logging.getLogger('pytube.helpers')
2097
+ logger.setLevel(logging.INFO)
2098
+
2099
+ STATLogging = True
2100
+ #endfunction
2101
+
2102
+ #-------------------------------------------------
2103
+ # Выключить систему logging
2104
+ #-------------------------------------------------
2105
+ def STOPLogging () -> None:
2106
+ """STOPLogging"""
2107
+ #beginfunction
2108
+ global STATLogging
2109
+ global LoggerTOOLS
2110
+ STATLogging = False
2111
+ # LoggerTOOLS.disabled = True# Выключить систему logging для логгирования
2112
+ #endfunction
2113
+
2114
+ #-------------------------------------------------
2115
+ # LoggerTOOLS_AddLevel
2116
+ #-------------------------------------------------
2117
+ def LoggerTOOLS_AddLevel (ALevel, Astr):
2118
+ #beginfunction
2119
+ if STATLogging:
2120
+ try:
2121
+ LoggerTOOLS.log(ALevel, Astr)
2122
+ except:
2123
+ ...
2124
+ #endtry
2125
+ else:
2126
+ print("INFO: система не включена для записи логов")
2127
+ #endif
2128
+ #endfunction
2129
+
2130
+ #-------------------------------------------------
2131
+ # LoggerTOOLS_AddDebug
2132
+ #-------------------------------------------------
2133
+ def LoggerTOOLS_AddDebug (Astr):
2134
+ #beginfunction
2135
+ if STATLogging:
2136
+ try:
2137
+ LoggerTOOLS.debug(Astr)
2138
+ except:
2139
+ ...
2140
+ #endtry
2141
+ else:
2142
+ print("INFO: система не включена для записи логов")
2143
+ #endif
2144
+ #endfunction
2145
+
2146
+ #-------------------------------------------------
2147
+ # LoggerTOOLS_AddError
2148
+ #-------------------------------------------------
2149
+ def LoggerTOOLS_AddError (Astr):
2150
+ #beginfunction
2151
+ if STATLogging:
2152
+ try:
2153
+ LoggerTOOLS.error(Astr)
2154
+ except:
2155
+ ...
2156
+ #endtry
2157
+ else:
2158
+ print("INFO: система не включена для записи логов")
2159
+ #endif
2160
+ #endfunction
2161
+
2162
+ #-------------------------------------------------
2163
+ # LoggerAPPS_AddLevel
2164
+ #-------------------------------------------------
2165
+ #LULog.LoggerAPPS.log
2166
+ def LoggerAPPS_AddLevel (ALevel, Astr):
2167
+ #beginfunction
2168
+ if STATLogging:
2169
+ try:
2170
+ LoggerAPPS.log(ALevel, Astr)
2171
+ except:
2172
+ ...
2173
+ #endtry
2174
+ else:
2175
+ print("INFO: система не включена для записи логов")
2176
+ #endif
2177
+ #endfunction
2178
+
2179
+ #-------------------------------------------------
2180
+ # LoggerAPPS_AddInfo
2181
+ #-------------------------------------------------
2182
+ #LULog.LoggerAPPS.info
2183
+ def LoggerAPPS_AddInfo (Astr):
2184
+ #beginfunction
2185
+ if STATLogging:
2186
+ try:
2187
+ LoggerAPPS.info(Astr)
2188
+ except:
2189
+ ...
2190
+ #endtry
2191
+ else:
2192
+ print("INFO: система не включена для записи логов")
2193
+ #endif
2194
+ #endfunction
2195
+
2196
+ #-------------------------------------------------
2197
+ # LoggerAPPS_AddError
2198
+ #-------------------------------------------------
2199
+ #LULog.LoggerAPPS.error
2200
+ def LoggerAPPS_AddError (Astr):
2201
+ #beginfunction
2202
+ if STATLogging:
2203
+ try:
2204
+ LoggerAPPS.error(Astr)
2205
+ except:
2206
+ ...
2207
+ #endtry
2208
+ else:
2209
+ print("INFO: система не включена для записи логов")
2210
+ #endif
2211
+ #endfunction
2212
+
2213
+ #-------------------------------------------------
2214
+ # LoggerAPPS_AddDebug
2215
+ #-------------------------------------------------
2216
+ #LULog.LoggerAPPS.debug
2217
+ def LoggerAPPS_AddDebug (Astr):
2218
+ #beginfunction
2219
+ if STATLogging:
2220
+ try:
2221
+ LoggerAPPS.debug(Astr)
2222
+ except:
2223
+ ...
2224
+ #endtry
2225
+ else:
2226
+ print("INFO: система не включена для записи логов")
2227
+ #endif
2228
+ #endfunction
2229
+
2230
+ #-------------------------------------------------
2231
+ # main
2232
+ #-------------------------------------------------
2233
+ def main ():
2234
+ #beginfunction
2235
+ print('main LULog.py...')
2236
+ #endfunction
2237
+
2238
+ #------------------------------------------
2239
+ #
2240
+ #------------------------------------------
2241
+ #beginmodule
2242
+
2243
+ if __name__ == "__main__":
2244
+ main()
2245
+ else:
2246
+ STOPLogging ()
2247
+ #endif
2248
+
2249
+ #endmodule