Question 1: How to obtain caller (file name, row number, function name )? When a new log record is added, the _ log method of the Logger class is called. This method creates a LogRecord object first. The LogRecord object requires (filename, lineno, funcname) parameter information. This is obtained through the following statement: [python] fn, lno, func = self. findCaller () findCaller: [python] f = currentframe () # f is a frame object. Each method call generates a frame object and stores it in the program stack. If f is not None: f = f. f_back rv = "(unknown file)", 0, "(unknown function)" while hasattr (f, "f_code"): co = f. f_code # Get the code object, which contains the filename attribute and funcname attribute filename = OS. path. normcase (co. co_filename) if filename = _ srcfile: # _ srcfile is the file name of this module File. When the file name is no longer the same, f = f. f_back # obtain the frame of the external caller. This is required. Continue rv = (filename, f. f_lineno, co. co_name) break return rv currentframe Function Definition: [python] def currentframe (): "" Return the frame object for the caller's stack frame. "try: raise Exception # if an Exception is thrown, A traceback object is generated, including a frame object. Counter T: # sys. exc_traceback.tb_frame the current frame, f_back calls the frame return sys. exc_traceback.tb_frame.f_back # sys. _ getframe (3) does not return the current frame. 3 should be calculated. Reduce the number of cycles and return logger. frame if hasattr (sys, '_ getframe') of error (): currentframe = lambda: sys. _ getframe (3) Question 2: How does the parent-child relationship implement the hierarchy of the Logger object? First, in the logging module, the logger hierarchy is a tree structure, and the root of the relationship is 'root '. [Python] root = RootLogger (WARNING) # The RootLogger class is a sub-class of Logger. It has no special functions but is defined as 'root '. Logger. root = root Logger. manager = Manager (Logger. root) when you call logging. getLogger () to obtain a Logger, if the parameter is null, 'root' is returned '. Otherwise, call the getLogger () method of the Manager to obtain the Logger. [Python] def getLogger (name = None): "" Return a logger with the specified name, creating it if necessary. if no name is specified, return the root logger. "if name: return Logger. manager. getLogger (name) else: return root Manager getLogger () is defined as follows: [python] def getLogger (self, name ): "Get a logger with the specified name (channel name), creating it if it doesn' t yet exist. this name is a dot-sepa Rated hierarchical name, such as "a", ". B ",". b. c "or similar. if a PlaceHolder existed for the specified name [I. e. the logger didn't exist but a child of it did], replace it with the created logger and fix up the parent/child references which pointed to the placeholder to now point to the logger. "rv = None _ acquireLock () try: if name in self. loggerDict: rv = self. loggerDict [name] if isinst Ance (rv, PlaceHolder): ph = rv = _ loggerClass (name) rv. manager = self. loggerDict [name] = rv self. _ fixupChildren (ph, rv) self. _ fixupParents (rv) else: rv = _ loggerClass (name) rv. manager = self. loggerDict [name] = rv self. _ fixupParents (rv) finally: _ releaseLock () return the loggerDict dictionary in the rv Manager object, which stores the ing relationship between the logger name and the logger object. The PlaceHolder class is a container. For example, for a PlaceHolder object named 'logger', the logger of 'logging' does not exist. Therefore, logger starting with 'logging' has a reference in this object, for example, 'samples. food ', 'Food. cloth. china and other existing logger objects. When getLogger () is called to obtain an existing logger, for example, the name is 'level1. level2', first create a name named 'level1. the logger object of level2' exists in loggerDict. Then, call _ fixupParents (). _ FixupParents (): Find the first logger object in the hierarchy of the name, take it as the parent, and return it. The chain is not the name of the logger object. Create a PlaceHolder object (if not created) and add yourself to it. For example, add 'level1. logger of level2.level3. Call _ fixupParents to create a logger named 'level1. the PlaceHolder object of level2, create a PlaceHolder object named 'level1', and set 'level1. level2.level3 'logger is added to the above two PlaceHolder object containers, and its father is set to 'root '. In short, _ fixupParents directs the logger object to the real Father's Day (logger object) and adds the logger itself to all the upper-layer PlaceHolder object containers. If a name already exists in loggerDict, and the name corresponds to a previously created PlaceHolder object. First, create a logger object with the corresponding name. Then, call _ fixupChild () to correct the parent of the downstream logger object contained in this PlaceHolder object. Finally, call _ fixupParent () for the same purpose as the previous step. The parent-child hierarchy is used to send each logRecord to the parent logger for processing when the propagate attribute value of the logger object is 1 (default. In this way, you only need to define the 'root' root logger object, and define other logger names based on the module name and class name, and then bind a NullHandler. Finally, all logrecords will be handed over to the 'root' for unified processing. This is how multiple modules generate a uniform log format.