J'ai un script Python qui utilise 'Print' pour l'impression sur stdout. J'ai récemment ajouté la journalisation via Python Logger et j'aimerais le faire afin que ces instructions d'impression soient envoyées à l'enregistreur si la journalisation est activée. Je ne souhaite pas modifier ou supprimer ces instructions d'impression.
Je peux me connecter en faisant 'log.info ("quelques infos msg")'. Je veux pouvoir faire quelque chose comme ça:
if logging_enabled:
sys.stdout=log.info
print("test")
Si la journalisation est activée, "test" doit être enregistré comme si je l'avais fait log.info ("test"). Si la journalisation n'est pas activée, "test" doit simplement être imprimé à l'écran.
Est-ce possible? Je sais que je peux diriger stdout vers un fichier de la même manière (voir: rediriger les impressions vers le fichier journal )
Vous avez deux options:
Ouvrez un fichier journal et remplacez sys.stdout par lui, pas une fonction:
log = open("myprog.log", "a")
sys.stdout = log
>>> print("Hello")
>>> # nothing is printed because it goes to the log file instead.
Remplacez print par votre fonction de journal:
# If you're using python 2.x, uncomment the next line
#from __future__ import print_function
print = log.info
>>> print("Hello!")
>>> # nothing is printed because log.info is called instead of print
Une autre méthode consiste à encapsuler l'enregistreur dans un objet qui traduit les appels en write
en la méthode log
de l'enregistreur.
Ferry Boender fait exactement cela, fourni sous la licence GPL dans n message sur son site Web . Le code ci-dessous est basé sur cela mais résout deux problèmes avec l'original:
import logging
import sys
class StreamToLogger(object):
"""
Fake file-like stream object that redirects writes to a logger instance.
"""
def __init__(self, logger, log_level=logging.INFO):
self.logger = logger
self.log_level = log_level
self.linebuf = ''
def write(self, buf):
temp_linebuf = self.linebuf + buf
self.linebuf = ''
for line in temp_linebuf.splitlines(True):
# From the io.TextIOWrapper docs:
# On output, if newline is None, any '\n' characters written
# are translated to the system default line separator.
# By default sys.stdout.write() expects '\n' newlines and then
# translates them so this is still cross platform.
if line[-1] == '\n':
self.logger.log(self.log_level, line.rstrip())
else:
self.linebuf += line
def flush(self):
if self.linebuf != '':
self.logger.log(self.log_level, self.linebuf.rstrip())
self.linebuf = ''
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s:%(levelname)s:%(name)s:%(message)s',
filename="out.log",
filemode='a'
)
stdout_logger = logging.getLogger('STDOUT')
sl = StreamToLogger(stdout_logger, logging.INFO)
sys.stdout = sl
stderr_logger = logging.getLogger('STDERR')
sl = StreamToLogger(stderr_logger, logging.ERROR)
sys.stderr = sl
Cela vous permet de router facilement toutes les sorties vers un enregistreur de votre choix. Si nécessaire, vous pouvez enregistrer sys.stdout
et/ou sys.stderr
comme mentionné par d'autres dans ce fil avant de le remplacer si vous devez le restaurer plus tard.
Bien sûr, vous pouvez à la fois imprimer sur la sortie standard et l'ajouter à un fichier journal, comme ceci:
# Uncomment the line below for python 2.x
#from __future__ import print_function
import logging
logging.basicConfig(level=logging.INFO, format='%(message)s')
logger = logging.getLogger()
logger.addHandler(logging.FileHandler('test.log', 'a'))
print = logger.info
print('yo!')
Une option beaucoup plus simple,
import logging, sys
logging.basicConfig(filename='path/to/logfile', level=logging.DEBUG)
logger = logging.getLogger()
sys.stderr.write = logger.error
sys.stdout.write = logger.info
Une fois que vous avez défini votre enregistreur, utilisez-le pour rediriger l'impression vers l'enregistreur même avec plusieurs paramètres d'impression.
print = lambda *tup : logger.info(str(" ".join([str(x) for x in tup])))
Vous devriez vraiment le faire dans l'autre sens: en ajustant votre configuration de journalisation pour utiliser des instructions print
ou autre chose, selon les paramètres. N'écrasez pas le comportement de print
, car certains des paramètres qui pourraient être introduits à l'avenir (par exemple, par vous ou par quelqu'un d'autre utilisant votre module) peuvent en fait le renvoyer vers le stdout
et vous aura des problèmes.
Il existe un gestionnaire qui est censé rediriger vos messages de journal vers le flux approprié (fichier, stdout
ou autre chose semblable à un fichier). Il s'appelle StreamHandler
et il est fourni avec le module logging
.
Donc, à mon avis, vous devriez faire ce que vous avez déclaré ne pas vouloir faire: remplacer les instructions print
par la journalisation réelle.
Ci-dessous, il fonctionne parfaitement dans mon code PySpark. Si quelqu'un a besoin de compréhension ->
import os
import sys
import logging
import logging.handlers
log = logging.getLogger(__name_)
handler = logging.FileHandler("spam.log")
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
log.addHandler(handler)
sys.stderr.write = log.error
sys.stdout.write = log.info
(enregistrera chaque erreur dans "spam.log" dans le même répertoire, rien ne sera sur console/stdout)
(enregistrera toutes les informations dans "spam.log" dans le même répertoire, rien ne sera sur console/stdout)
pour imprimer les informations/erreurs de sortie dans les deux fichiers ainsi que dans la console, supprimez au-dessus de deux lignes.
Happy Coding Cheers !!!