1414import uuid
1515from collections import namedtuple , OrderedDict
1616from enum import Enum , auto
17- from typing import Callable , Optional
18-
17+ from typing import Callable , Optional , List
1918
2019import meshroom
2120from meshroom .common import Signal , Variant , Property , BaseObject , Slot , ListModel , DictModel
2221from meshroom .core import desc , plugins , stats , hashValue , nodeVersion , Version , MrNodeType
2322from meshroom .core .attribute import attributeFactory , ListAttribute , GroupAttribute , Attribute
2423from meshroom .core .exception import NodeUpgradeError , UnknownNodeTypeError
24+ from meshroom .core .mtyping import PathLike
2525
2626
2727def getWritingFilepath (filepath : str ) -> str :
@@ -221,9 +221,11 @@ def fromDict(self, d):
221221class LogManager :
222222 dateTimeFormatting = '%H:%M:%S'
223223
224- def __init__ (self , chunk ):
225- self .chunk = chunk
226- self .logger = logging .getLogger (chunk .node .getName ())
224+ def __init__ (self , logger , logFile ):
225+ self .logger : logging .Logger = logger
226+ self .logFile : PathLike = logFile
227+ self ._previousHandlers : List [logging .Handler ] = []
228+ self ._previousLevel : int = 0
227229
228230 class Formatter (logging .Formatter ):
229231 def format (self , record ):
@@ -232,19 +234,29 @@ def format(self, record):
232234 return logging .Formatter .format (self , record )
233235
234236 def configureLogger (self ):
237+ self ._previousLevel = self .logger .level
238+ self ._previousHandlers = []
235239 for handler in self .logger .handlers [:]:
240+ self ._previousHandlers .append (handler )
236241 self .logger .removeHandler (handler )
237- handler = logging .FileHandler (self .chunk . logFile )
242+ handler = logging .FileHandler (self .logFile )
238243 formatter = self .Formatter ('[%(asctime)s.%(msecs)03d][%(levelname)s] %(message)s' ,
239244 self .dateTimeFormatting )
240245 handler .setFormatter (formatter )
241246 self .logger .addHandler (handler )
242247
248+ def restorePreviousLogger (self ):
249+ for h in self .logger .handlers [:]:
250+ self .logger .removeHandler (h )
251+ for h in self ._previousHandlers :
252+ self .logger .addHandler (h )
253+ self .logger .setLevel (self ._previousLevel )
254+
243255 def start (self , level ):
244256 # Clear log file
245- open (self .chunk .logFile , 'w' ).close ()
246-
257+ open (self .logFile , 'w' ).close ()
247258 self .configureLogger ()
259+ self .logger .propagate = False
248260 self .logger .setLevel (self .textToLevel (level ))
249261 self .progressBar = False
250262
@@ -261,15 +273,15 @@ def makeProgressBar(self, end, message=''):
261273 self .currentProgressTics = 0
262274 self .progressBar = True
263275
264- with open (self .chunk . logFile , 'a' ) as f :
276+ with open (self .logFile , 'a' ) as f :
265277 if message :
266278 f .write (message + '\n ' )
267279 f .write ('0% 10 20 30 40 50 60 70 80 90 100%\n ' )
268280 f .write ('|----|----|----|----|----|----|----|----|----|----|\n \n ' )
269281
270282 f .close ()
271283
272- with open (self .chunk . logFile ) as f :
284+ with open (self .logFile ) as f :
273285 content = f .read ()
274286 self .progressBarPosition = content .rfind ('\n ' )
275287
@@ -281,7 +293,7 @@ def updateProgressBar(self, value):
281293
282294 tics = round ((value / self .progressEnd )* 51 )
283295
284- with open (self .chunk . logFile , 'r+' ) as f :
296+ with open (self .logFile , 'r+' ) as f :
285297 text = f .read ()
286298 for i in range (tics - self .currentProgressTics ):
287299 text = text [:self .progressBarPosition ]+ '*' + text [self .progressBarPosition :]
@@ -296,8 +308,10 @@ def completeProgressBar(self):
296308
297309 self .progressBar = False
298310
299- def textToLevel (self , text ):
300- if text == "critical" :
311+ @staticmethod
312+ def textToLevel (text ):
313+ text = text .lower ()
314+ if text in ["critical" , "fatal" ]:
301315 return logging .CRITICAL
302316 elif text == "error" :
303317 return logging .ERROR
@@ -307,6 +321,8 @@ def textToLevel(self, text):
307321 return logging .INFO
308322 elif text == "debug" :
309323 return logging .DEBUG
324+ elif text == "trace" :
325+ return logging .TRACE
310326 else :
311327 return logging .NOTSET
312328
@@ -325,7 +341,7 @@ def __init__(self, node, range, parent=None):
325341 super ().__init__ (parent )
326342 self .node = node
327343 self .range = range
328- self .logManager : LogManager = LogManager ( self )
344+ self ._logManager = None
329345 self ._status : StatusData = StatusData (node .name , node .nodeType , node .packageName ,
330346 node .packageVersion , node .getMrNodeType ())
331347 self .statistics : stats .Statistics = stats .Statistics ()
@@ -344,6 +360,13 @@ def name(self):
344360 return f"{ self .node .name } ({ self .index } )"
345361 else :
346362 return self .node .name
363+
364+ @property
365+ def logManager (self ):
366+ if self ._logManager is None :
367+ logger = logging .getLogger (self .node .getName ())
368+ self ._logManager = LogManager (logger , self .logFile )
369+ return self ._logManager
347370
348371 @property
349372 def statusName (self ):
@@ -660,6 +683,7 @@ def __init__(self, nodeType: str, position: Position = None, parent: BaseObject
660683 self ._uid : str = uid
661684 self ._cmdVars : dict = {}
662685 self ._size : int = 0
686+ self ._logManager : Optional [LogManager ] = None
663687 self ._position : Position = position or Position ()
664688 self ._attributes = DictModel (keyAttrName = 'name' , parent = self )
665689 self ._internalAttributes = DictModel (keyAttrName = 'name' , parent = self )
@@ -705,6 +729,15 @@ def getLabel(self):
705729 return label
706730 return self .getDefaultLabel ()
707731
732+ def getNodeLogLevel (self ):
733+ """
734+ Returns:
735+ str: the user-provided log level used for logging on process launched by this node
736+ """
737+ if self .hasInternalAttribute ("nodeDefaultLogLevel" ):
738+ return self .internalAttribute ("nodeDefaultLogLevel" ).value .strip ()
739+ return "info"
740+
708741 def getColor (self ):
709742 """
710743 Returns:
@@ -1312,6 +1345,24 @@ def postprocess(self):
13121345 # node is completed
13131346 self .nodeDesc .postprocess (self )
13141347
1348+ def getLogHandlers (self ):
1349+ return self ._handlers
1350+
1351+ def prepareLogger (self , iteration = - 1 ):
1352+ # Get file handler path
1353+ logFileName = "log"
1354+ if iteration != - 1 :
1355+ chunk = self .chunks [iteration ]
1356+ logFileName = str (chunk .index ) + ".log"
1357+ logFile = os .path .join (self .graph .cacheDir , self .internalFolder , logFileName )
1358+ # Setup logger
1359+ rootLogger = logging .getLogger ()
1360+ self ._logManager = LogManager (rootLogger , logFile )
1361+ self ._logManager .start (self .getNodeLogLevel ())
1362+
1363+ def restoreLogger (self ):
1364+ self ._logManager .restorePreviousLogger ()
1365+
13151366 def updateOutputAttr (self ):
13161367 if not self .nodeDesc :
13171368 return
0 commit comments