Veuillez arrêter l'impression et importer la journalisation pour la sortie du journal - Qiita
Je lisais en pensant: "Eh bien, je vois." Mais en même temps, je pensais comme ça.
** Alors, comment écrivez-vous la journalisation? ** ** ** Même gratuitement, je ne suis pas doué pour me connecter en Python et cela prend du temps, mais je ne peux pas faire de logger !! **
J'ai donc décidé d'écrire cet article. Le but est le suivant.
――Je veux partager ma propre compréhension que j'ai trouvée à la suite de la confrontation désespérée d'un bûcheron
C'est le point de vue de la lecture du Document officiel et de l'article Qiita ci-dessus.
la journalisation a une structure hiérarchique.
└── rootLogger
├── scanLogger
│ ├── scanLogger.txt
│ ├── scanLogger.html
│ └── scanLogger.pdf
└── anotherLogger
Il a (ou est créé) plusieurs enregistreurs enfants avec un enregistreur appelé rootLogger comme parent.
Par exemple ...
utilise le logger nommé ʻurllib
utilise le journal ʻapscheduler.scheduler
L'enregistreur de chacun de ces packages et applications est appelé rootLogger
.
bad_sample.py
import logging
logging.info("Taking log...")
Cela revient à jouer directement avec rootLogger
, alors arrêtons.
C'est similaire à l'idée de jouer avec l'instance au lieu de jouer avec la classe.
Même si vous regardez le document officiel ou l'échantillon, vous ne savez pas comment l'écrire ... Combien de mots fixes et combien puis-je changer par moi-même? (Gamme que vous pouvez nommer vous-même)
C'est pourquoi je présenterai la configuration dont le fonctionnement a été confirmé. Si vous rencontrez un problème, veuillez le copier et l'utiliser.
Autant que possible, je vais inclure un commentaire dans les commentaires afin que vous puissiez facilement le personnaliser.
** J'écris le fichier de configuration de journalisation avec toml. ** **
Vous pouvez vous référer au paquet toml avec pip install toml
.
La méthode de notation en yaml est ici.
terminal
$ tree
.
├── app
│ ├── logger
│ │ ├── __init__.py
│ │ └── logger.py
│ ├── logging.config.toml
│ └── main.py
└── log
├── application.log
└── error.log
$ cd app/
$ python app.py # app/J'écris un exemple en supposant que cette commande est exécutée dans le répertoire
Mettez également l 'exemple de code Github.
main.py
#L'enregistreur précédent est un répertoire. L'enregistreur à l'arrière est un fichier.
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
est configuré pour afficher les journaux sur l'écran et le fichier de la console.
toml:logging.config.toml
version = 1
disable_existing_loggers = false #Ne désactivez pas les enregistreurs pour les autres modules
[formatters]
[formatters.basic]
#Définissez le format du journal. Les détails de l'écriture seront décrits plus tard
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]
#Définissez la sortie de la console ici
class = "logging.StreamHandler" #Libellé fixe
level = "DEBUG" #le niveau de journalisation est votre choix
formatter = "basic" #Sélectionnez le format de journal décrit dans les formateurs
stream = "ext://sys.stdout" #Libellé fixe
[handlers.file]
class = "logging.handlers.TimedRotatingFileHandler" #Libellé fixe. Les détails de TimeRotatingFileHandler seront décrits plus loin. Il y a des notes
formatter = "basic"
filename = "../log/application.log" #Si spécifié par le chemin relatif$Écrire à partir d'un répertoire qui exécute python
when = 'D' #log Une unité de synchronisation pour changer de fichier. ré= day。
interval = 1 #Le jour étant sélectionné ci-dessus, un nouveau fichier est créé chaque jour.
backupCount = 31 #Le jour étant sélectionné ci-dessus, 31 jours de fichiers journaux sont conservés
[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 est le nom du logger appelé depuis python. Nommé arbitrairement
level = "INFO"
handlers = [
"console",
"file",
"error",
] #Définir les gestionnaires définis ci-dessus à utiliser
propagate = false
[root]
level = "INFO"
handlers = [
"console",
"file",
"error"
] #Réglage de l'enregistreur racine parent de l'enregistreur. Je souhaite également conserver le rootLogger dans la console et le fichier
logger/__init__.py
from . import logger
logger.init_logger('logging.config.toml', 'app_name')
#Premier argument: configfile =Le nom du fichier de configuration. Chemin relatif ou chemin absolu du répertoire exécuté par la commande python
#Deuxième argument: loggername =Le nom de l'enregistreur défini dans le fichier de configuration
logger/logger.py
import os
import toml
from logging import getLogger
from logging.config import dictConfig
CONFIGFILE = "logging.config.toml" #Traitez comme une variable globale. La valeur par défaut est celle illustrée à gauche. Tout
LOGGERNAME = "root" #Traitez comme une variable globale. La valeur par défaut est root. Tout
def init_logger(configfile, loggername):
global CONFIGFILE
global LOGGERNAME
CONFIGFILE = configfile
LOGGERNAME = loggername
dictConfig(toml.load(CONFIGFILE))
def get_logger():
return getLogger(LOGGERNAME)
C'est plus flexible que la journalisation dans le fichier de configuration. Dans le fichier Config, le répertoire parent est «../», ce qui le rend plus dépendant du système d'exploitation. Si vous le définissez avec python dict, vous pouvez le spécifier avec ʻos.path.pardir`.
terminal
$ tree
.
├── app
│ ├── logger
│ │ ├── __init__.py
│ │ └── logger.py
│ └── main.py
└── log
├── application.log
└── error.log
$ cd app/
$ python main.py
Mettez également l 'exemple de code Github.
main.py
est [main.py ci-dessus](https://qiita.com/uANDi/items/9a2b980262bc43455f2e#3-1-2-%E3%83%95%E3%82%A1%E3%82 Identique à% A4% E3% 83% AB).
De plus, le contenu de «CONFIG» est le même que celui de «logging.config.toml» ci-dessus.
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)
Le format de sortie est le même pour le motif 1 et le motif 2.
[app_name]
est le nom de l'enregistreur.
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
Document officiel TimedRotatingFileHandler "Ce qui est défini sur ʻinterval et
when`" Pouvez-vous le faire? »Est écrit!
Sort le journal dans un nouveau fichier à l'heure définie.
Dans l'exemple ci-dessus, `` D', c'est-à-dire que le fichier est réécrit quotidiennement. Les anciens fichiers journaux ont une date surfix dans le nom du fichier, comme ʻapplication.log.2019-12-21
.
**Mise en garde! ** **
TimedRotatingFileHandler
ne fonctionnera pas pour les applications où app.py ne démarre pas toute la journée.
Cela fonctionne bien pour les applications Web comme Django et Flask, mais peut ne pas fonctionner correctement avec cron.
3-4-2. Format
Une liste de chaque format peut être trouvée dans l'attribut LogRecord de la documentation officielle (https://docs.python.org/en/3/library/logging.html#id2). Organisons les articles dont vous avez besoin!
J'ai présenté deux types de procédure de journalisation! Regardons chacun en arrière! (Si vous pensez que c'est parfait, sautez-le !!)
À propos, Takeaways signifie «point, point». à emporter = À emporter → À emporter avec la présentation d'aujourd'hui! C'est une telle nuance.
Un certain nombre d'enregistreurs ont été créés avec rootLogger comme parent. Une image comme rootLogger → Classe, enregistreur → instance.
logging.info ()
va directement jouer avec rootLogger, alors créons votre propre logger.
Alors comment le faites-vous concrètement?
--Si vous ne définissez pas disable_existing_loggers = False
, l'enregistreur de gunicorn
sera cassé.
--Lors de la création d'un fichier de configuration
$ python
TimedRotatingFileHandler
Je ne pense pas que ce soit la bonne façon d'écrire la journalisation. Vous pouvez également ajouter un gestionnaire à votre fichier python avec ʻaddHandler`. Nous vous serions reconnaissants de bien vouloir nous apprendre comment configurer la journalisation recommandée.