Einfachere Möglichkeit, die ausführliche Protokollierung zu aktivieren

Antworten:

87

Sie müssen die Weisheit des Argparse-Tutorials mit Pythons Protokollierungs-HOWTO kombinieren . Hier ist ein Beispiel ...

> cat verbose.py 
#!/usr/bin/env python

import argparse
import logging

parser = argparse.ArgumentParser(
    description='A test script for http://stackoverflow.com/q/14097061/78845'
)
parser.add_argument("-v", "--verbose", help="increase output verbosity",
                    action="store_true")

args = parser.parse_args()
if args.verbose:
    logging.basicConfig(level=logging.DEBUG)

logging.debug('Only shown in debug mode')

Führen Sie die Hilfe aus:

> ./verbose.py -h
usage: verbose.py [-h] [-v]

A test script for http://stackoverflow.com/q/14097061/78845

optional arguments:
  -h, --help     show this help message and exit
  -v, --verbose  increase output verbosity

Laufen im ausführlichen Modus:

> ./verbose.py -v
DEBUG:root:Only shown in debug mode

Leise laufen:

> ./verbose.py   
> 
Johnsyweb
quelle
1
//, ich kann bestätigen, dass dies funktioniert. Wie "store_true" funktioniert, ist jedoch immer noch ein Rätsel.
Nathan Basanese
122

Ich finde sowohl --verbose(für Benutzer) als auch --debug(für Entwickler) nützlich. So mache ich das mit loggingund argparse:

import argparse
import logging

parser = argparse.ArgumentParser()
parser.add_argument(
    '-d', '--debug',
    help="Print lots of debugging statements",
    action="store_const", dest="loglevel", const=logging.DEBUG,
    default=logging.WARNING,
)
parser.add_argument(
    '-v', '--verbose',
    help="Be verbose",
    action="store_const", dest="loglevel", const=logging.INFO,
)
args = parser.parse_args()    
logging.basicConfig(level=args.loglevel)

Wenn --debugalso festgelegt ist, wird die Protokollierungsstufe auf festgelegt DEBUG. Wenn --verbose, ist die Protokollierung auf eingestellt INFO. Wenn dies nicht der Fall ist, setzt das Fehlen von --debugdie Protokollierungsstufe auf den Standardwert von WARNING.

Matthew Leingang
quelle
11
Ich mag diesen Ansatz wirklich sehr ... lass argparse die ganze Arbeit machen, nein ifs oder irgendetwas nach parse_args
MestreLion
3
@MestreLion: danke. Es fühlt sich für mich sehr pythonisch an.
Matthew Leingang
Es gibt einen Haken: loggingStandardmäßig druckt alles auf stderr und Sie wirklich wollen , um Ihre normalen Programmausgabe (die von Trennung --verbose) von dev-only (dem aus --debug)
Michał Góral
1
@ MichałGóral: Das klingt nach einem guten Punkt. Kann ich Nachrichten an stdout und Nachrichten an stderr logging.basicConfigsenden ? info()debug()
Matthew Leingang
2
@MatthewLeingang: Dazu müssen Sie mehrere Handler verwenden . Hier ist das Beispiel: stackoverflow.com/a/16066513/1088577
Michał Góral
56

Hier ist eine präzisere Methode, die die Grenzen überprüft und gültige Werte in der Hilfe auflistet:

parser = argparse.ArgumentParser(description='This is a demo.')
parser.add_argument("-l", "--log", dest="logLevel", choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], help="Set the logging level")

args = parser.parse_args()
if args.logLevel:
    logging.basicConfig(level=getattr(logging, args.logLevel))

Verwendung:

demo.py --log DEBUG
Stickley
quelle
18
Es besteht keine Notwendigkeit für getattr:level=logging.getLevelName(args.logLevel)
xmedeko
13
Noch besser ist hinzuzufügen default='INFO'zu add_argument()sowie (default: %(default)s)auf den Hilfetext, dann ist die löschen if args.logLevelund rufen immer basicConfig(). Dann kann der Benutzer wissen, wie der Standardwert immer sein wird.
jfritz42
30

Eine andere Variante wäre, die Anzahl zu zählen -vund die Anzahl als Index für das a listmit den tatsächlichen Ebenen von zu verwenden logging:

import argparse
import logging

parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose', action='count', default=0)
args = parser.parse_args()

levels = [logging.WARNING, logging.INFO, logging.DEBUG]
level = levels[min(len(levels)-1,args.verbose)]  # capped to number of levels

logging.basicConfig(level=level,
                    format="%(asctime)s %(levelname)s %(message)s")

logging.debug("a debug message")
logging.info("a info message")
logging.warning("a warning message")

Dies funktioniert für -vvvv, -vvv, -vv, -v, -v -v, usw., wenn nicht -vdann logging.WARNINGgewählt, wenn mehr -vvorgesehen sind es zu betreten wird INFOundDEBUG

RubenLaguna
quelle
4
Nett! Denken Sie daran, Ihr Skript als auszuführen python script.py -v [other_args]. Wenn Sie python -v script.py( -vvor dem Skript) ausführen , erhalten Sie viele Debug-Ausgaben vom Python-Interpreter selbst, jedoch nicht von Ihrem Skript. (Kein Problem mit der Antwort, nur eine Warnung an andere Leser).
Logc
Gut! Dies ist die flexibelste (mehrere Protokollierungsstufen), saubere (verschmutzt den Argument-Namespace nicht) und intuitive (von mehreren anderen Tools verwendet) Lösung.
M000
8

Sie können eine Ebene explizit als Ganzzahl nach dem -vFlag angeben :

parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", const=1, default=0, type=int, nargs="?",
                    help="increase verbosity: 0 = only warnings, 1 = info, 2 = debug. No number means info. Default is no verbosity.")
args = parser.parse_args()

logger = logging.getLogger()
if args.verbose == 0:
    logger.setLevel(logging.WARN) 
elif args.verbose == 1:
    logger.setLevel(logging.INFO) 
elif args.verbose == 2:
    logger.setLevel(logging.DEBUG) 
Stephan
quelle
2

Hier ist eine weitere Möglichkeit, wie Argparse die Option -v zählt, um die Ausführlichkeit von der Standardwarnung WARNING über INFO (-v) bis DEBUG (-vv) um zwei Stufen zu erhöhen. Dies wird nicht den durch definierten Konstanten zugeordnet, loggingsondern berechnet den Wert direkt und begrenzt die Eingabe:

print( "Verbosity / loglevel:", args.v )
logging.basicConfig( level=10*(3-max(0,min(args.v,3))) )
logging.debug("debug") # 10
logging.info("info") # 20
logging.warning("warning") # 30 - The default level is WARNING, which means that only events of this level and above will be tracked
logging.error("error") # 40
logging.critical("critical") # 50
Griff
quelle
Beachten Sie, dass das Argument argparse benötigt wird default=0, sonst erhalten Sie TypeError: '<' not supported between instances of 'int' and 'NoneType'. Oder fügen Sie args.v or 0oben hinzu ...
behandeln Sie den
1

Wenn Sie die Protokollierungsstufe DEBUG für ein Skript aktivieren möchten, das Sie nicht bearbeiten möchten (oder können), können Sie Ihren Start anpassen:

jcomeau@aspire:~$ python -c "import site; site._script()"
[snip]...
USER_BASE: '/home/jcomeau/.local' (exists)
USER_SITE: '/home/jcomeau/.local/lib/python2.7/site-packages' (exists)
ENABLE_USER_SITE: True
jcomeau@aspire:~$ mkdir -p ~/.local/lib/python2.7/site-packages
jcomeau@aspire:~$ vi ~/.local/lib/python2.7/site-packages/usercustomize.py

gib das Folgende ein:

import os, logging
if os.getenv('DEBUGGING'):
    logging.basicConfig(level = logging.DEBUG)

dann kannst du einfach:

jcomeau@aspire:~$ mkdir -p /tmp/some/random/
jcomeau@aspire:~$ echo 'import logging; logging.debug("test")' >> /tmp/some/random/script.py
jcomeau@aspire:~$ DEBUGGING=1 python /tmp/some/random/script.py 
DEBUG:root:test

von Paul Ollis unter http://nedbatchelder.com/blog/201001/running_code_at_python_startup.html


2017-07-18: Ich habe seitdem zu einer anderen Methode gewechselt:

logging.basicConfig(level=logging.DEBUG if __debug__ else logging.INFO)

Wenn Sie ohne Optimierung (wie in python script.py) arbeiten, erhalten Sie das Zeug auf DEBUG-Ebene, während python -OO script.pySie es nicht tun , wenn Sie mit laufen . Keine Umgebungsvariablen zu setzen.

jcomeau_ictx
quelle