J'ai essayé de développer un formateur qui génère des journaux Python en JSON

J'utilise généralement Python principalement, mais les données structurelles sont plus pratiques lors du développement d'une plate-forme de collecte / analyse de journaux avec un service cloud, n'est-ce pas? Cependant, si vous utilisez le formateur normal de Logging, qui est une bibliothèque Python standard, vous devez le convertir une fois en données structurelles. Il ne sert à rien de créer une autre application uniquement pour la conversion. Par conséquent, j'ai développé un formateur qui génère des journaux en JSON.

environnement

--OS (fonctionnement garanti) - MacOS Catalina - Ubuntu 18.04 --Langue - Python ^3.7

--Directeur chargé d'emballage - Poetry

Livrables

Le formateur développé peut être trouvé sur homoluctus / json-pyformatter. Il est également publié sur PyPI-> json-pyformatter 0.1.0

comment utiliser

1. Installation

pip install json-pyformatter

2. Exemple

Le champ qui peut être affiché est logrecord-attributes de la bibliothèque standard Logging. Les champs par défaut générés par le formateur développé sont ʻasctime, levelname et message`.

import logging
from json_pyformmatter import JsonFormatter

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
fields = ('levelname', 'filename', 'message')
formatter = JsonFormatter(fields=fields)
handler.setFormatter(formatter)
logger.addHandler(hander)

logger.info('hello')

Lorsque cela est exécuté, le journal JSON sera généré comme indiqué ci-dessous.

{"levelname": "INFO", "filename": "test_formatter.py", "message": "hello"}

De plus, il sera plus facile de voir si vous spécifiez ʻindent = 2` dans l'argument de JsonFormatter.

{
  "levelname": "INFO",
  "filename": "test_formatter.py",
  "message": "hello"
}

Bien sûr, vous pouvez également générer un suivi. Puisqu'il est difficile de voir s'il s'agit d'une seule ligne, elle est organisée dans un tableau.

{
  'asctime': '2019-12-01 13:58:34',
  'levelname': 'ERROR',
  'message': 'error occurred !!',
  'traceback': [
    'Traceback (most rec...ll last):',
    'File "/example/test..._exc_info',
    'raise TypeError(message)',
    'TypeError: error occurred !!'
  ]
}

Explication du code source

De là, je vais expliquer le code source.

Tout le code source

J'écrirai tout le code source pour le moment.

import json
from collections import OrderedDict
from logging import Formatter


class JsonFormatter(Formatter):
    default_fields = ('asctime', 'levelname', 'message')

    def __init__(self, fields=None, datefmt=None, indent=None):
        """
        Args:
            fields (tuple, list)
            datefmt (str)
            indent (str, int)
        """

        self.fields = (
            self.get_or_none(fields, (list, tuple)) or self.default_fields
        )
        # default time format is %Y-%m-%d %H:%M:%S
        self.datefmt = (
            self.get_or_none(datefmt, str) or self.default_time_format
        )
        self._indent = self.get_or_none(indent, (str, int))

    def get_or_none(self, target, types):
        """Check whether target value is expected type.
        If target type does not match expected type, returns None.

        Args:
            target (any)
            types (class, tuple)

        Returns:
            target or None
        """

        if isinstance(target, types):
            return target
        return None

    def getMessage(self, record):
        if isinstance(record.msg, (list, tuple, dict)):
            return record.msg
        return record.getMessage()

    def _format_json(self, record):
        return json.dumps(record, ensure_ascii=False, indent=self._indent)

    def _format(self, record):
        log = OrderedDict()

        try:
            for field in self.fields:
                log[field] = getattr(record, field)
            return log
        except AttributeError as err:
            raise ValueError(f'Formatting field not found in log record {err}')

    def format(self, record):
        record.message = self.getMessage(record)
        record.asctime = self.formatTime(record, self.datefmt)
        formatted_record = self._format(record)
        if record.exc_info:
            if not record.exc_text:
                record.exc_text = self.formatException(record.exc_info)
        if record.exc_text:
            formatted_record['traceback'] = [
                msg.strip() for msg in record.exc_text.strip().split('\n')
            ]
        if record.stack_info:
            formatted_record['stack'] = record.stack_info.strip()

        return self._format_json(formatted_record)

get_or_none Cette méthode est utilisée lors de la création d'une instance. S'il ne s'agit pas du type attendu, utilisez la valeur par défaut ou utilisez-la pour affecter None tel quel.

def get_or_none(self, target, types):
    """Check whether target value is expected type.
    If target type does not match expected type, returns None.

    Args:
       target (any)
       types (class, tuple)

    Returns:
        target or None
    """

    if isinstance(target, types):
        return target
    return None

getMessage Cette méthode est utilisée pour récupérer le message de l'argument de logger.info (). La valeur de retour de ce getMessage est définie dans le champ JSON message. Si le type d'instance de record.msg est l'un des (list, tuple, dict), il est renvoyé tel quel, sinon, la valeur de retour de la méthode getMessage de l'instance d'enregistrement est renvoyée. Ce faisant, lorsque l'un des éléments (liste, tuple, dict) est passé, le journal peut être généré en tant que tableau / objet JSON.

def getMessage(self, record):
    if isinstance(record.msg, (list, tuple, dict)):
        return record.msg
    return record.getMessage()

_format_json Cette méthode vide l'enregistrement d'argument en tant que JSON.

def _format_json(self, record):
    return json.dumps(record, ensure_ascii=False, indent=self._indent)

_format Une méthode pour convertir un enregistrement en dictionnaire Python. Le champ spécifié par l'utilisateur n'existe pas nécessairement dans l'attribut d'enregistrement, il s'agit donc de "try-except". Le journal des valeurs de retour est défini sur ʻOrderedDict` pour garantir la commande.

def _format(self, record):
    log = OrderedDict()

    try:
        for field in self.fields:
            log[field] = getattr(record, field)
        return log
    except AttributeError as err:
        raise ValueError(f'Formatting field not found in log record {err}')

format Cette méthode est appelée à partir de logging.Handler. Je ne pense pas que vous créerez votre propre Formatter et utiliserez le format de la classe parent (logging.Formatter), alors assurez-vous d'écraser cette méthode. Je formate record.exc_text pour afficher le traçage sous forme de tableau. Après avoir supprimé les espaces supplémentaires, il est divisé avec un saut de ligne pour créer un tableau. Enfin, je vide le dict Python en JSON.

def format(self, record):
    record.message = self.getMessage(record)
    record.asctime = self.formatTime(record, self.datefmt)
    formatted_record = self._format(record)
    if record.exc_info:
        if not record.exc_text:
            record.exc_text = self.formatException(record.exc_info)
    if record.exc_text:
        formatted_record['traceback'] = [
            msg.strip() for msg in record.exc_text.strip().split('\n')
        ]
    if record.stack_info:
        formatted_record['stack'] = record.stack_info.strip()

    return self._format_json(formatted_record)

Matériel de référence

Recommended Posts

J'ai essayé de développer un formateur qui génère des journaux Python en JSON
J'ai essayé d'implémenter un pseudo pachislot en Python
J'ai essayé d'implémenter un automate cellulaire unidimensionnel en Python
J'ai essayé "un programme qui supprime les déclarations en double en Python"
J'ai essayé "Comment obtenir une méthode décorée en Python"
J'ai fait un chronomètre en utilisant tkinter avec python
J'ai essayé d'implémenter PLSA en Python
J'ai essayé d'implémenter la permutation en Python
J'ai essayé d'implémenter ADALINE en Python
J'ai essayé d'implémenter PPO en Python
[Python] Un mémo que j'ai essayé de démarrer avec asyncio
J'ai essayé de mettre en œuvre un jeu de dilemme de prisonnier mal compris en Python
Un mémo que j'ai écrit un tri rapide en Python
Je veux créer une fenêtre avec Python
J'ai essayé de jouer à un jeu de frappe avec Python
Comment créer un fichier JSON en Python
J'ai essayé d'implémenter TOPIC MODEL en Python
J'ai essayé d'ajouter un module Python 3 en C
Une histoire qui n'a pas fonctionné lorsque j'ai essayé de me connecter avec le module de requêtes Python
J'ai essayé d'implémenter le tri sélectif en python
J'ai essayé d'implémenter le jeu de cartes de Trump en Python
Je souhaite créer une file d'attente prioritaire pouvant être mise à jour avec Python (2.7)
[Python] J'ai essayé de créer une IA Shiritori qui améliore le vocabulaire grâce aux batailles
J'ai essayé d'implémenter ce qui semble être un outil de snipper Windows avec Python
J'ai essayé de représenter graphiquement les packages installés en Python
Analyser une chaîne JSON écrite dans un fichier en Python
Je veux facilement implémenter le délai d'expiration en python
Je veux écrire en Python! (2) Écrivons un test
Je veux échantillonner au hasard un fichier avec Python
J'ai essayé d'implémenter le poker de Drakue en Python
Je veux travailler avec un robot en python.
J'ai essayé d'implémenter GA (algorithme génétique) en Python
J'ai essayé de résumer comment utiliser les pandas de python
[Python] J'ai essayé d'obtenir Json de squid ring 2
Comment développer en Python
[Python + Bottle] J'ai publié un service Web qui visualise les tweets positionnés de Twitter.
J'ai essayé de créer une API list.csv avec Python à partir de swagger.yaml
Un moyen standard de développer et de distribuer des packages en Python
Comment développer dans un environnement virtuel Python [Memo]
Créez le code qui renvoie "A et prétendant B" en python
J'ai créé une classe en Python et essayé de taper du canard
J'ai essayé d'implémenter la fonction d'envoi de courrier en Python
Je veux ajouter un joli complément à input () en python
J'ai essayé d'implémenter le blackjack du jeu Trump en Python
J'ai essayé de toucher Python (installation)
J'ai créé un script en Python pour convertir un fichier texte pour JSON (pour l'extrait d'utilisateur vscode)
[1 hour challenge] J'ai essayé de créer un site de bonne aventure qui soit trop adapté à Python
J'ai essayé d'automatiser "un cœur même séparé" à l'aide d'un algorithme génétique en Python
J'ai essayé de créer un générateur qui génère une classe conteneur C # à partir de CSV avec Python
J'ai aussi essayé d'imiter la fonction monade et la monade d'état avec le générateur en Python
J'ai écrit un doctest dans "J'ai essayé de simuler la probabilité d'un jeu de bingo avec Python"
J'ai essayé la notification de ligne en Python
[5e] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé de créer un environnement serveur qui fonctionne sous Windows 10
Je souhaite utiliser un caractère générique que je souhaite décortiquer avec Python remove