Question

Aujourd'hui, je pensais à un projet Python je l'ai écrit au sujet d'un an en arrière où je logging assez largement. Je me souviens d'avoir à commenter beaucoup d'appels d'exploitation forestière dans des scénarios comme interne en boucle (code de 90%) en raison de la surcharge (hotshot a indiqué qu'il était l'un de mes plus grands goulets d'étranglement).

Je me demande maintenant s'il y a une manière canonique pour dépouiller des appels programme d'exploitation forestière dans les applications Python sans commenter et décommentant tout le temps. Je pense que vous pouvez utiliser l'inspection / recompilation ou la manipulation de bytecode faire quelque chose comme ça et cible uniquement les objets de code qui sont des goulots d'étranglement provoquent . De cette façon, vous pouvez ajouter un manipulateur comme une étape de post-compilation et utiliser un fichier de configuration centralisée, comme suit:

[Leave ERROR and above]
my_module.SomeClass.method_with_lots_of_warn_calls

[Leave WARN and above]
my_module.SomeOtherClass.method_with_lots_of_info_calls

[Leave INFO and above]
my_module.SomeWeirdClass.method_with_lots_of_debug_calls

Bien sûr, vous voudriez l'utiliser avec parcimonie et probablement avec une granularité par fonction - uniquement pour les objets de code qui ont montré logging être un goulot d'étranglement. Tout le monde sait de quoi que ce soit comme ça?

Remarque: Il y a quelques petites choses qui font de ce plus difficile à faire d'une manière performante en raison de typage dynamique et liaison tardive. Par exemple, tous les appels à une méthode appelée debug peut être enveloppé avec un if not isinstance(log, Logger). Dans tous les cas, je suppose que tous les petits détails peuvent être surmontés, que ce soit par l'accord d'un homme ou d'une vérification de l'exécution. : -)

Était-ce utile?

La solution

Qu'en est-il en utilisant exploitation forestière. désactiver ?

J'ai aussi trouvé que je devais utiliser .isEnabledFor si le message d'enregistrement est coûteux à créer.

Autres conseils

Utilisez pypreprocessor

Ce qui peut également consulter sur PyPI (pypi) et être récupérées en utilisant pip.

Voici un exemple d'utilisation de base:

from pypreprocessor import pypreprocessor

pypreprocessor.parse()

#define nologging

#ifdef nologging
...logging code you'd usually comment out manually...
#endif

Code Essentiellement, le préprocesseur commente la façon dont vous le faites manuellement avant. Il ne vient à la volée en fonction de certaines conditions que vous définissez.

Vous pouvez également supprimer toutes les directives de préprocesseur et commenté le code à partir du code post-traité en ajoutant « pypreprocessor.removeMeta = True » entre l'importation et analyser les états ().

Le fichier de sortie bytecode (.pyc) contiendra la sortie optimisée.

sidenote. Pypreprocessor est compatible avec python2x et python3k

Disclaimer:. Je suis l'auteur de pypreprocessor

Je l'ai aussi vu assert utilisé de cette façon.

assert logging.warn('disable me with the -O option') is None

(je devine qui mettent en garde retourne toujours rien .. sinon, vous obtiendrez un AssertionError

Mais vraiment c'est juste une drôle de façon de faire ceci:

if __debug__: logging.warn('disable me with the -O option')

Lorsque vous exécutez un script avec cette ligne avec l'option -O, la ligne sera retirée du code .pyo optimisé. Si, au contraire, vous aviez votre propre variable, comme dans ce qui suit, vous aurez une condition qui est toujours exécutée (quelle que soit la valeur de la variable est), bien qu'une condition devrait exécuter plus rapidement qu'un appel de fonction:

my_debug = True
...
if my_debug: logging.warn('disable me by setting my_debug = False')

donc si ma compréhension de debug est correcte, il semble comme une bonne façon de se débarrasser des appels de journalisation inutiles. Le revers est qu'il désactive également tous vos AFFIRME, il est donc un problème si vous avez besoin qu'affirment.

Comme un raccourci imparfait, que diriez-vous moquant des logging dans des modules spécifiques en utilisant quelque chose comme MiniMock ?

Par exemple, si my_module.py était:

import logging
class C(object):
    def __init__(self, *args, **kw):
        logging.info("Instantiating")

Vous remplacerez votre utilisation de my_module avec:

from minimock import Mock
import my_module
my_module.logging = Mock('logging')
c = my_module.C()

Vous souhaitez que devez le faire une fois, avant l'importation initiale du module.

Obtenir le niveau comportement spécifique serait assez simple en se moquant des méthodes spécifiques, ou ayant logging.getLogger retourner un objet simulé avec des méthodes impuissantes et d'autres déléguer au réel module logging.

Dans la pratique, vous voudrez probablement remplacer MiniMock quelque chose plus simple et plus rapide; à tout le moins quelque chose qui n'imprime pas l'utilisation de stdout! Bien sûr, cela ne traite pas le problème du module A logging importation du module B (et donc une importation aussi la granularité du journal B) ...

Ce ne sera jamais aussi vite que n'exécutant les instructions du journal du tout, mais devrait être beaucoup plus rapide que d'aller tout le chemin dans les profondeurs du module d'enregistrement pour découvrir ce disque ne doit pas être connecté après tout.

Vous pouvez essayer quelque chose comme ceci:

# Create something that accepts anything
class Fake(object):
    def __getattr__(self, key):
        return self
    def __call__(self, *args, **kwargs):
        return True

# Replace the logging module
import sys
sys.modules["logging"] = Fake()

Il remplace essentiellement (ou remplit initialement) l'espace pour le module d'enregistrement avec une instance de Fake qui prend simplement en quoi que ce soit. Vous devez exécuter le code ci-dessus (juste une fois!) Avant que le module d'enregistrement est tenté d'utiliser partout. Voici un test:

import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(levelname)-8s %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S',
                    filename='/temp/myapp.log',
                    filemode='w')
logging.debug('A debug message')
logging.info('Some information')
logging.warning('A shot across the bows')

Avec ce qui précède, rien du tout a été consigné, comme on pouvait s'y attendre.

J'utilise une décoratrice d'enregistrement de fantaisie, ou un groupe d'entre eux:

def doLogging(logTreshold):
    def logFunction(aFunc):
        def innerFunc(*args, **kwargs):
            if LOGLEVEL >= logTreshold:
                print ">>Called %s at %s"%(aFunc.__name__, time.strftime("%H:%M:%S"))
                print ">>Parameters: ", args, kwargs if kwargs else "" 
            try:
                return aFunc(*args, **kwargs)
            finally:
                print ">>%s took %s"%(aFunc.__name__, time.strftime("%H:%M:%S"))
        return innerFunc
    return logFunction

Il suffit de déclarer constante LOGLEVEL dans chaque module (ou tout simplement dans le monde et juste importer dans tous les modules) et vous pouvez l'utiliser comme ceci:

@doLogging(2.5)
def myPreciousFunction(one, two, three=4):
    print "I'm doing some fancy computations :-)"
    return

Et si LOGLEVEL est pas moins de 2,5, vous obtiendrez la sortie comme ceci:

>>Called myPreciousFunction at 18:49:13
>>Parameters:  (1, 2) 
I'm doing some fancy computations :-)
>>myPreciousFunction took 18:49:13

Comme vous pouvez le voir, un travail est nécessaire pour une meilleure manipulation des kwargs, de sorte que les valeurs par défaut seront imprimées si elles sont présentes, mais c'est une autre question.

Vous devriez probablement utiliser une logger module au lieu de cru print déclarations, mais je voulais me concentrer sur l'idée de décorateur et éviter de faire du code trop longtemps.

Quoi qu'il en soit - avec ce décorateur vous obtenez la journalisation fonction de niveau, arbitrairement plusieurs niveaux de journaux, la facilité d'application de la nouvelle fonction, et de désactiver la connexion vous suffit de définir LOGLEVEL. Et vous pouvez définir différents flux de sortie / fichiers pour chaque fonction si vous le souhaitez. Vous pouvez écrire doLogging comme:

 def doLogging(logThreshold, outStream=sys.stdout):
      .....
      print >>outStream, ">>Called %s at %s" etc.

et d'utiliser les fichiers journaux définis sur une base par fonction.

Ceci est un problème dans mon projet et -. Termine l'enregistrement en assez régulièrement sur les rapports profileurs

I ai utilisé le module de _ast avant dans une fourchette de pyflakes ( http://github.com/kevinw / pyflakes ) ... et il est certainement possible de faire ce que vous suggérez dans votre question - d'inspecter et d'injecter des gardes avant les appels aux méthodes d'exploitation forestière (avec votre mise en garde a reconnu que vous auriez à faire un certain type d'exécution vérification). Voir http://pyside.blogspot.com/2008/03 /ast-compilation-from-python.html pour un exemple simple.

Modifier Je viens de remarquer MetaPython sur mon flux de planetpython.org -. le cas d'utilisation exemple supprime les déclarations du journal au moment de l'importation

Peut-être la meilleure solution serait pour quelqu'un à l'exploitation forestière réimplémenter en tant que module C, mais je ne serais pas le premier à sauter sur une telle occasion ...: p

:-) Nous appelions qu'un préprocesseur et bien que préprocesseur de C avait quelques-uns de ces capablities, le « roi de la colline » était le préprocesseur pour mainframe IBM PL / I. Il a fourni un soutien linguistique étendue dans le préprocesseur (affectations complètes, conditionals, en boucle, etc.) et il est possible d'écrire des « programmes qui ont écrit des programmes » en utilisant seulement le PL / I PP.

J'ai écrit de nombreuses applications avec programme sophistiqué complet et données de suivi (nous ne disposions pas d'un débogueur décent pour un processus back-end à ce moment-là) pour une utilisation dans le développement et le test qui a ensuite, lorsqu'il est compilé avec le approprié " drapeau d'exécution » tout simplement dépouillé tout le code de traçage à proprement sans impact sur les performances.

Je pense que l'idée de décorateur est un bon. Vous pouvez écrire un décorateur pour envelopper les fonctions qui ont besoin de l'exploitation forestière. Ensuite, pour la distribution d'exécution, le décorateur est transformé en un « non-op », qui élimine les instructions de débogage.

Jon R

Je fais un projet qui utilise actuellement l'exploitation forestière extensive pour les temps logiques de test et d'exécution pour une API d'analyse de données en utilisant la bibliothèque Pandas.

J'ai trouvé cette chaîne avec une préoccupation similaire - par exemple ce qui est la surcharge sur les états de logging.debug même si le niveau logging.basicConfig est réglé sur le niveau = logging.WARNING

J'ai eu recours à écrire le script suivant pour commenter ou décommenter l'enregistrement de débogage avant le déploiement:

import os
import fileinput

comment = True

# exclude files or directories matching string
fil_dir_exclude = ["__","_archive",".pyc"]

if comment :
    ## Variables to comment
    source_str = 'logging.debug'
    replace_str = '#logging.debug'
else :
    ## Variables to uncomment
    source_str = '#logging.debug'
    replace_str = 'logging.debug'

# walk through directories
for root, dirs, files in os.walk('root/directory') :
    # where files exist
    if files:
        # for each file
        for file_single in files :
            # build full file name
            file_name = os.path.join(root,file_single)
            # exclude files with matching string
            if not any(exclude_str in file_name for exclude_str in fil_dir_exclude) :
                # replace string in line
                for line in fileinput.input(file_name, inplace=True):
                    print "%s" % (line.replace(source_str, replace_str)),

Ceci est un récursion fichier qui exclut les fichiers basés sur une liste de critères et effectue un en place remplaçons basé sur une réponse trouvée ici: Rechercher et remplacer une ligne dans un fichier en Python

J'aime la solution « si __debug_ », sauf que la mise en face de chaque appel est un peu distrayant et laid. J'ai eu ce même problème et a surmonté en écrivant un script qui analyse automatiquement vos fichiers source et remplace les déclarations de journalisation avec les déclarations de passe (et commentaires des copies des déclarations d'exploitation forestière). Il peut également annuler cette conversion.

Je l'utilise quand je déploie un nouveau code à un environnement de production quand il y a beaucoup de déclarations d'exploitation forestière que je ne ai pas besoin dans un contexte de production et ils ont une incidence sur la performance.

Vous trouverez ici le script: http://dound.com/2010 / 02 / python-logging performance /

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top