Ich las, während ich dachte: "Nun, ich verstehe." Aber gleichzeitig dachte ich so.
** Wie schreibt man Protokollierung? ** ** ** ** Selbst kostenlos kann ich mich nicht gut in Python anmelden und es braucht Zeit, aber ich kann nicht loggen !! **
Also habe ich beschlossen, diesen Artikel zu schreiben. Der Zweck ist wie folgt.
――Ich möchte mein eigenes Verständnis teilen, das ich als Ergebnis eines verzweifelten Loggers gefunden habe ――Verschwenden Sie keine Zeit mit der Protokollierung, wenn alle Apps mit Python erstellen
Dies ist die Ansicht aus dem Lesen der offiziellen Dokumente und des obigen Qiita-Artikels.
Die Protokollierung hat eine hierarchische Struktur.
└── rootLogger
├── scanLogger
│ ├── scanLogger.txt
│ ├── scanLogger.html
│ └── scanLogger.pdf
└── anotherLogger
Es hat (oder wird erstellt) mehrere untergeordnete Logger mit einem Logger namens rootLogger als übergeordnetem Protokoll.
Zum Beispiel ...
apscheduler.scheduler
verwendet den Logger apscheduler.scheduler
Der Logger für jedes dieser Pakete und Apps heißt "rootLogger".
bad_sample.py
import logging
logging.info("Taking log...")
Dies ist gleichbedeutend mit dem direkten Spielen mit "rootLogger". Hören wir also auf. Es ähnelt der Idee, mit Instanz zu spielen, anstatt mit Klasse zu spielen.
Selbst wenn Sie sich das offizielle Dokument oder das Beispiel ansehen, wissen Sie nicht, wie Sie es schreiben sollen ... Schweiß Wie viel feste Formulierung und wie viel kann ich selbst ändern? (Bereich, den Sie selbst benennen können)
Deshalb werde ich die Konfiguration vorstellen, deren Betrieb bestätigt wurde. Wenn Sie ein Problem haben, kopieren Sie es bitte und verwenden Sie es.
So viel wie möglich werde ich einen Kommentar in die Kommentare aufnehmen, damit Sie ihn einfach anpassen können.
** Ich schreibe die Protokollierungskonfigurationsdatei mit toml. ** ** **
Sie können auf das Toml-Paket mit pip install toml
verweisen.
Die Notationsmethode in yaml lautet hier.
terminal
$ tree
.
├── app
│ ├── logger
│ │ ├── __init__.py
│ │ └── logger.py
│ ├── logging.config.toml
│ └── main.py
└── log
├── application.log
└── error.log
$ cd app/
$ python app.py # app/Ich schreibe ein Beispiel unter der Annahme, dass dieser Befehl im Verzeichnis ausgeführt wird
Geben Sie auch den [Github-Beispielcode] ein (https://github.com/hiphopku/QiitaLoggerToml).
main.py
#Der vorherige Logger ist ein Verzeichnis. Der Logger auf der Rückseite ist eine Datei.
from logger.logger import get_logger
logger = get_logger()
def main():
logger.info("start = main()")
# do something
logger.error("oops! something wrong!!")
# do something
logger.info("end = main()")
if __name__ == "__main__":
main()
logging.config.toml
ist so eingestellt, dass Protokolle auf dem Konsolenbildschirm und in der Datei ausgegeben werden.
toml:logging.config.toml
version = 1
disable_existing_loggers = false #Deaktivieren Sie keine Logger für andere Module
[formatters]
[formatters.basic]
#Legen Sie das Protokollformat fest. Details zum Schreiben werden später beschrieben
format = "%(asctime)s [%(name)s][%(levelname)s] %(message)s (%(filename)s:%(module)s:%(funcName)s:%(lineno)d)"
datefmt = "%Y-%m-%d %H:%M:%S"
[handlers]
[handlers.console]
#Stellen Sie hier die Konsolenausgabe ein
class = "logging.StreamHandler" #Feste Formulierung
level = "DEBUG" #Protokollstufe ist Ihre Wahl
formatter = "basic" #Wählen Sie das unter Formatierer beschriebene Protokollformat aus
stream = "ext://sys.stdout" #Feste Formulierung
[handlers.file]
class = "logging.handlers.TimedRotatingFileHandler" #Feste Formulierung. Details zu TimeRotatingFileHandler werden später beschrieben. Es gibt Notizen
formatter = "basic"
filename = "../log/application.log" #Wenn durch relativen Pfad angegeben$Schreiben Sie aus einem Verzeichnis, in dem Python ausgeführt wird
when = 'D' #log Eine Zeiteinheit zum Wechseln von Dateien. D.= day。
interval = 1 #Da oben Tag ausgewählt ist, wird jeden Tag eine neue Datei erstellt.
backupCount = 31 #Da oben der Tag ausgewählt ist, bleiben Protokolldateien im Wert von 31 Tagen erhalten
[handlers.error]
class = "logging.handlers.TimedRotatingFileHandler"
level = "ERROR"
formatter = "basic"
filename = "../log/error.log"
when = 'D'
interval = 1
backupCount = 31
[loggers]
[loggers.app_name] # app_name ist der Name des von Python aufgerufenen Loggers. Beliebig benannt
level = "INFO"
handlers = [
"console",
"file",
"error",
] #Legen Sie fest, welcher der oben festgelegten Handler verwendet werden soll
propagate = false
[root]
level = "INFO"
handlers = [
"console",
"file",
"error"
] #Einstellung des übergeordneten Stammloggers des Loggers. Ich möchte auch den rootLogger in Konsole und Datei behalten
logger/__init__.py
from . import logger
logger.init_logger('logging.config.toml', 'app_name')
#Erstes Argument: configfile =Der Name der Konfigurationsdatei. Relativer Pfad oder absoluter Pfad des Verzeichnisses, das vom Python-Befehl ausgeführt wird
#Zweites Argument: loggername =Der Name des in der Konfigurationsdatei festgelegten Loggers
logger/logger.py
import os
import toml
from logging import getLogger
from logging.config import dictConfig
CONFIGFILE = "logging.config.toml" #Als globale Variable behandeln. Der Standardwert ist wie links gezeigt. Irgendein
LOGGERNAME = "root" #Als globale Variable behandeln. Der Standardwert ist root. Irgendein
def init_logger(configfile, loggername):
global CONFIGFILE
global LOGGERNAME
CONFIGFILE = configfile
LOGGERNAME = loggername
dictConfig(toml.load(CONFIGFILE))
def get_logger():
return getLogger(LOGGERNAME)
Dies ist flexibler als die Anmeldung in der Konfigurationsdatei. In der Konfigurationsdatei lautet das übergeordnete Verzeichnis "../", wodurch es stärker vom Betriebssystem abhängig ist. Wenn Sie es mit Python Dict festlegen, können Sie es mit "os.path.pardir" angeben.
terminal
$ tree
.
├── app
│ ├── logger
│ │ ├── __init__.py
│ │ └── logger.py
│ └── main.py
└── log
├── application.log
└── error.log
$ cd app/
$ python main.py
Geben Sie auch den [Github-Beispielcode] ein (https://github.com/hiphopku/QiitaLoggerPydict).
main.py
ist [main.py oben](https://qiita.com/uANDi/items/9a2b980262bc43455f2e#3-1-2-%E3%83%95%E3%82%A1%E3%82 Gleich wie% A4% E3% 83% AB).
Außerdem ist der Inhalt von "CONFIG" der gleiche wie der von "logging.config.toml".
logger/__init__.py
from . import logger
logger.init_logger()
logger/logger.py
import os
from logging import getLogger
from logging.config import dictConfig
APP_NAME = os.getenv('APP_NAME', default='app_name')
CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'basic': {
'format': '%(asctime)s [%(name)s][%(levelname)s] %(message)s (%(module)s:%(filename)s:%(funcName)s:%(lineno)d)',
'datefmt': '%Y-%m-%d %H:%M:%S'
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'formatter': 'basic',
'stream': 'ext://sys.stdout'
},
'file': {
'class': 'logging.handlers.TimedRotatingFileHandler',
'formatter': 'basic',
'filename': os.path.join(os.path.pardir, 'log', 'application.log'),
'when': 'D',
'interval': 1,
'backupCount': 31
},
'error': {
'class': 'logging.handlers.TimedRotatingFileHandler',
'level': 'ERROR',
'formatter': 'basic',
'filename': os.path.join(os.path.pardir, 'log', 'error.log'),
'when': 'D',
'interval': 1,
'backupCount': 31
}
},
'loggers': {
APP_NAME: {
'level': 'INFO',
'handlers': [
'console',
'file',
'error'
],
'propagate': False
}
},
'root': {
'level': 'INFO',
'handlers': [
'console',
'file',
'error'
]
}
}
def init_logger():
dictConfig(CONFIG)
def get_logger():
return getLogger(APP_NAME)
Das Ausgabeformat ist für Muster 1 und Muster 2 gleich.
[app_name]
ist der Loggername.
terminal
$ cd app/
$ python main.py
2019-12-21 15:43:28 [app_name][INFO] start = main() (main:main.py:main:8)
2019-12-21 15:43:28 [app_name][ERROR] oops! something wrong!! (main:main.py:main:10)
2019-12-21 15:43:28 [app_name][INFO] end = main() (main:main.py:main:12)
$ cat ../log/application.log
2019-12-21 15:43:28 [app_name][INFO] start = main() (main:main.py:main:8)
2019-12-21 15:43:28 [app_name][ERROR] oops! something wrong!! (main:main.py:main:10)
2019-12-21 15:43:28 [app_name][INFO] end = main() (main:main.py:main:12)
$ cat ../log/error.log
2019-12-21 15:43:28 [app_name][ERROR] oops! something wrong!! (main:main.py:main:10)
3-4-1. TimedRotatingFileHandler
In Official Document TimedRotatingFileHandler wird "Was wird in" Intervall "und" Wann "festgelegt? Schaffst du das? "Ist geschrieben!
Das Ausgabeprotokoll wird zur festgelegten Zeit in einer neuen Datei protokolliert. Im obigen Beispiel "D" wird die Datei täglich neu geschrieben. Frühere Protokolldateien haben das Datums-Surfix im Dateinamen, z. B. "application.log.2019-12-21".
**Hinweis! ** ** **
TimedRotatingFileHandler
funktioniert nicht für Anwendungen, bei denen app.py nicht den ganzen Tag startet.
Es funktioniert gut für Web-Apps wie Django und Flask, funktioniert aber möglicherweise nicht richtig mit cron.
3-4-2. Format
Eine Liste der einzelnen Formate finden Sie im Attribut "Official Documentation LogRecord" (https://docs.python.org/ja/3/library/logging.html#id2). Lassen Sie uns die Artikel arrangieren, die Sie brauchen!
Ich habe zwei Arten der Protokollierung vorgestellt! Lassen Sie uns auf jeden zurückblicken! (Wenn Sie denken, dass es perfekt ist, überspringen Sie es !!)
Imbiss bedeutet übrigens "Punkt, Punkt". take away = Take away → Nehmen Sie dies mit der heutigen Präsentation weg! Es ist so eine Nuance.
Eine Reihe von Protokollierern wurde mit rootLogger als übergeordnetem Element erstellt. Ein Bild wie rootLogger → Klasse, Logger → Instanz.
logging.info ()
wird direkt mit rootLogger in Konflikt geraten, also erstellen wir Ihren eigenen Logger.
Wie macht man es dann konkret?
--Lesen Sie die Protokollierungseinstellungen aus der Konfigurationsdatei
TimedRotatingFileHandler
Ich denke nicht, dass dies der richtige Weg ist, um Protokollierung zu schreiben.
Sie können Ihrer Python-Datei auch mit addHandler
einen Handler hinzufügen.
Wir wären Ihnen dankbar, wenn Sie uns das Einrichten Ihrer empfohlenen Protokollierung beibringen könnten.