Python bietet mit dem Paket logging eine sehr gute Lösung um den eigenen Source entsprechend in alle Richtungen auszuwerten. In meinem Beispiel habe ich zur Auswertung von Unittest unterschiedliche Logger genutzt. Zum einen einen Logger True der mir das Ergebniss eines positiven oder eines negativen Testverlauf mitloggt.

Zum anderen, einen Logger test_create_user_ENFORCE.log der mir jeglichen Output beim erstellen von Usern auf einem Hardwareswitch ausgiebt. Das heißt im einzelnen:

– Jede Information vom Einloggen via SSH auf den jeweiligen Hardwareswitch

– Jede Information die ich auf der CLI / Prompt ausführe

Der Aufbau

Dieser Ausschnitt zeigt meinen Logger Aufbau den ich in einigen meiner Unittest so nutze. Da wir gerade in den einzelnen Test verschiedene Logging Level benötigen um verschiedenste Ausgaben auszuwerten ist es also wichtig das man entsprechende Logger definiert:

#################################################
# Logging configuration
#################################################

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

#formatter = logging.Formatter('%(message)s:%(thread)d:%(levelname)s:%(levelno)s:%(lineno)d:%(module)s:    '
                               #'%(pathname)s:')

formatter = logging.Formatter('%(message)s:%(thread)d:%(levelname)s:%(lineno)d:%(module)s:')

file_handler = logging.FileHandler('test_create_user_different_privileg_enforceOFF.log')
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

################### True Logger - Parser ###################
logger_true = logging.getLogger('parser_logger')
logger_true.setLevel(logging.INFO)

formatter_true = logging.Formatter('%(asctime)s %(module)s %(levelname)s %(message)s')

file_handler_true = logging.FileHandler('True.log')
# file_handler_true.setLevel(logging.INFO)
file_handler_true.setFormatter(formatter_true)
logger_true.addHandler(file_handler_true)

################### Admin_Delete Logger ###################
logger_admin = logging.getLogger('admin_logger')
logger_admin.setLevel(logging.DEBUG)

formatter_admin = logging.Formatter('%(asctime)s %(module)s %(levelname)s %(message)s')

file_handler_admin = logging.FileHandler('Admin_Delete.log')
# file_handler_true.setLevel(logging.INFO)
file_handler_admin.setFormatter(formatter_admin)
logger_admin.addHandler(file_handler_admin)

######################### Stream Logger ######################
stream_handler = logging.StreamHandler()
logger.addHandler(stream_handler)
stream_handler.setFormatter(formatter)
stream_handler.setLevel(logging.DEBUG)

stream_handler = logging.Formatter('%(message)s:%(thread)d:%(levelname)s:%(lineno)d:%(module)s:')


#logging.basicConfig(filename='test_create_user.log', level=logging.DEBUG,
                    #format='%(asctime)s:%(created)f:%(levelno)s:%(message)s:%(process)d:%(funcName)s:%(lineno)d')


Den Logger definieren

Hiermit wird der Logger definiert und ein entsprechender Name vergeben. Wichtiger Hinweis: “Nutzt immer einen anderen Namen für den Logger.”, ihr braucht das um eben verschiedene Logger in euren Test zu definieren.

logger = logging.getLogger(__name__)

Der Loglevel

In diesem Beispiel definiere ich den Loglevel 1. Ein sehr gutes Basic Tutorial könnt ihr euch unterhalb anschauen:

Wie in diesem Beispiel zu sehen könnt ihr verschiedenste LogLevel einstellen.

logger.setLevel(logging.INFO)

In dieser Übersicht seht ihr die einzelnen LogLevel. 2

Level Numeric value
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0

 

Die Log Formatter

In dieser Übersicht könnt ihr eure Logs entsprechend mit den benötigten Ausgaben definieren, die ihr für euch benötigt.

formatter = logging.Formatter('%(message)s:%(thread)d:%(levelname)s:%(lineno)d:%(module)s:')

Welche Attribute man in den einzelnen Logs entsprechend nutzen kann, hier in diesem Beispiel einmal aufgeschlüsselt: 3

Attribute name Format Description
args You shouldn’t need to format this yourself. 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).
asctime %(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).
created %(created)f Time when the LogRecord was created (as returned by time.time()).
exc_info You shouldn’t need to format this yourself. Exception tuple (à la sys.exc_info) or, if no exception has occurred, None.
filename %(filename)s Filename portion of pathname.
funcName %(funcName)s Name of function containing the logging call.
levelname %(levelname)s Text logging level for the message ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL').
levelno %(levelno)s Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL).
lineno %(lineno)d Source line number where the logging call was issued (if available).
message %(message)s The logged message, computed as msg %
args
. This is set when Formatter.format() is invoked.
module %(module)s Module (name portion of filename).
msecs %(msecs)d Millisecond portion of the time when the LogRecord was created.
msg You shouldn’t need to format this yourself. 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).
name %(name)s Name of the logger used to log the call.
pathname %(pathname)s Full pathname of the source file where the logging call was issued (if available).
process %(process)d Process ID (if available).
processName %(processName)s Process name (if available).
relativeCreated %(relativeCreated)d Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.
stack_info You shouldn’t need to format this yourself. 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.
thread %(thread)d Thread ID (if available).
threadName %(threadName)s Thread name (if available).

Auch hier ein weiteres sehr gutes Tutorial welches auch auf die einzelnen Attribute beim Logging eingeht.

Kommen wir aber zurück zu meinem Aufbau. In diesem Punkt wird der jeweilige file_handler definiert.

file_handler = logging.FileHandler('test_create_user_different_privileg_enforceOFF.log')

Einige sehr gute und weitere Beispiele könnt ihr hier abrufen: 4

 

 

 

 

 

 

 

 

 

  1. Weitere Informationen zum Python Loglevel könnt ihr hier abrufen. Python 3.7 https://docs.python.org/3/library/logging.html und Python 2.7 https://docs.python.org/2/library/logging.html[]
  2. Weitere Informationen könnt ihr u.a. auch hier beziehen: https://fangpenlin.com/posts/2012/08/26/good-logging-practice-in-python/[]
  3. Weitere Informationen zum Thema LogRecord attributes findet ihr hier https://docs.python.org/3/library/logging.html[]
  4. Einige sehr gute Beispiele zum Thema file_handler könnt ihr hier abrufen https://www.programcreek.com/python/example/472/logging.FileHandler[]