En tant que SGBDR moderne, PostgreSQL est livré avec de nombreux paramètres pour un réglage fin. L'un des domaines à prendre en compte est la manière dont PostgreSQL doit enregistrer ses activités. La journalisation est souvent négligée dans la gestion de la base de données Postgres, et si elle n'est pas ignorée, elle est généralement mal définie. Cela se produit parce que la plupart du temps, le but de la journalisation n'est pas clair. Bien sûr, la raison fondamentale de la journalisation est bien connue, mais ce qui manque parfois, c'est une compréhension de la manière dont les journaux seront utilisés.
Les exigences de journalisation de chaque organisation sont uniques et, par conséquent, la façon dont la journalisation PostgreSQL doit être configurée sera également différente. Ce qu'une entreprise de services financiers doit capturer dans ses journaux de base de données sera différent de ce qu'une entreprise traitant d'informations critiques sur la santé doit enregistrer. Et dans certains cas, ils peuvent aussi être similaires.
Dans cet article, je couvrirai certaines pratiques fondamentales pour tirer le meilleur parti des journaux PostgreSQL. Ce blog n'est pas un livre de règles absolues; les lecteurs sont plus que bienvenus pour partager leurs réflexions dans la section des commentaires. Cependant, pour en tirer le meilleur parti, je demande au lecteur de réfléchir à la manière dont il souhaite utiliser les journaux de son serveur de base de données PostgreSQL :
- Raison de conformité légale pour laquelle des informations spécifiques doivent être saisies
- Audit de sécurité lorsque des détails d'événements spécifiques doivent être présents
- Dépannage des performances où les requêtes et leurs paramètres doivent être enregistrés
- Assistance opérationnelle quotidienne où un nombre défini de métriques doit être surveillé
Cela dit, commençons.
N'apportez pas de modifications manuelles à postgresql.conf
Toute modification du fichier postgresql.conf doit être effectuée à l'aide d'un système de gestion de configuration tel que Puppet, Ansible ou Chef. Cela garantit que les modifications sont traçables et peuvent être restaurées en toute sécurité à une version précédente si nécessaire. Cela est vrai lorsque vous apportez des modifications aux paramètres de journalisation.
Les administrateurs de base de données créent souvent plusieurs copies du fichier postgresql.conf, chacune avec des paramètres légèrement différents, chacune dans un but différent. La gestion manuelle de différents fichiers de configuration est une tâche fastidieuse si elle n'est pas sujette aux erreurs. D'autre part, un système de gestion de configuration peut être amené à renommer et à utiliser différentes versions du fichier postgresql.conf en fonction d'un paramètre qui lui est transmis. Ce paramètre dictera l'objectif de la version actuelle. Lorsque le besoin est terminé, l'ancien fichier de configuration peut être remis en place en modifiant le même paramètre d'entrée.
Par exemple, si vous souhaitez enregistrer toutes les instructions en cours d'exécution sur votre instance PostgreSQL, un fichier de configuration avec la valeur de paramètre "log_statement=all" peut être utilisé. Lorsqu'il n'est pas nécessaire d'enregistrer toutes les déclarations - peut-être après un exercice de dépannage - le fichier de configuration précédent peut être rétabli.
Utiliser des fichiers journaux séparés pour PostgreSQL
Je recommande d'activer le collecteur de journalisation natif de PostgreSQL pendant les opérations normales. Pour activer la journalisation native PostgreSQL, définissez le paramètre suivant sur on :
logging_collector = on
Il y a deux raisons à cela :
Tout d'abord, dans les systèmes occupés, le système d'exploitation peut ne pas enregistrer de manière cohérente les messages PostgreSQL dans syslog (en supposant une installation basée sur nix) et supprime souvent les messages. Avec la journalisation PostgreSQL native, un démon séparé se charge d'enregistrer les événements. Lorsque PostgreSQL est occupé, ce processus différera l'écriture dans les fichiers journaux pour laisser les threads de requête se terminer. Cela peut bloquer tout le système jusqu'à ce que l'événement du journal soit écrit. Il est donc utile d'enregistrer des messages moins verbeux dans le journal (comme nous le verrons plus loin) et d'utiliser des préfixes de ligne de journal raccourcis.
Deuxièmement - et comme nous le verrons plus tard - les journaux doivent être collectés, analysés, indexés et analysés avec un utilitaire de gestion des journaux. Faire en sorte que PostgreSQL enregistre ses événements dans syslog signifiera créer une couche supplémentaire de filtrage et de correspondance de modèles dans la partie de gestion des journaux pour filtrer tous les « messages parasites ». Les fichiers journaux dédiés peuvent être facilement analysés et indexés pour les événements par la plupart des outils.
Définir la destination du journal sur stderr
Considérons le paramètre "log_destination". Il peut avoir quatre valeurs :
log_destination = stderr | syslog | csv | eventlog
Sauf s'il existe une bonne raison d'enregistrer les événements du journal dans un format séparé par des virgules ou dans le journal des événements sous Windows, je recommande de définir ce paramètre sur stderr. En effet, avec une destination de fichier CSV, une valeur de paramètre personnalisée "log_line_prefix" n'aura aucun effet, et pourtant, le préfixe peut contenir des informations précieuses.
D'un autre côté, un journal CSV peut être facilement importé dans une table de base de données et interrogé ultérieurement à l'aide de SQL standard. Certains utilisateurs de PostgreSQL trouvent cela plus pratique que de gérer des fichiers journaux bruts. Comme nous le verrons plus tard, les solutions modernes de gestion des journaux peuvent analyser nativement les journaux PostgreSQL et créer automatiquement des informations significatives à partir de ceux-ci. Avec CSV, le reporting et la visualisation doivent être effectués manuellement par l'utilisateur.
En fin de compte, cela dépend de votre choix. Si vous êtes à l'aise pour créer votre propre pipeline d'ingestion de données pour charger les journaux CSV dans des tables de base de données, nettoyer et transformer les données et créer des rapports personnalisés adaptés aux besoins de votre entreprise, assurez-vous que "log_destination" est défini sur CSV.
Utilisez des noms de fichiers journaux significatifs
Lorsque les fichiers journaux PostgreSQL sont enregistrés localement, suivre un style de nommage peut ne pas sembler nécessaire. Le style de nom de fichier par défaut est "postgresql-%Y-%m-%d_%H%M%S.log" pour les journaux au format non CSV, ce qui est suffisant dans la plupart des cas.
La dénomination devient importante lorsque vous enregistrez des fichiers journaux de plusieurs serveurs dans un emplacement central tel qu'un serveur de journalisation dédié, un volume NFS monté ou un compartiment S3. Je recommande d'utiliser deux paramètres dans ce cas :
log_directory log_filename
Pour stocker les fichiers journaux de plusieurs instances en un seul endroit, commencez par créer une hiérarchie de répertoires distincte pour chaque instance. Cela peut ressembler à ceci :
/<Application_Name>/<Environment_Name>/<Instance_Name>
Le "log_directory" de chaque instance PostgreSQL peut alors pointer vers son dossier désigné.
Chaque instance peut alors utiliser la même valeur de paramètre « log_filename ». La valeur par défaut créera un fichier comme
postgresql_2020-08-25_2359.log
Pour utiliser un nom plus significatif, définissez "log_filename" sur quelque chose comme ceci :
log_filename = "postgresql_%A-%d-%B_%H%M"
Les fichiers journaux seront alors nommés comme :
postgresql_samedi-23-août_2230
Utiliser un préfixe de ligne de journal significatif
Les préfixes de ligne de journal PostgreSQL peuvent contenir les informations les plus précieuses en plus du message lui-même. La documentation Postgres montre plusieurs caractères d'échappement pour la configuration du préfixe d'événement de journal. Ces séquences d'échappement sont remplacées par diverses valeurs d'état au moment de l'exécution. Certaines applications comme pgBadger attendent un préfixe de ligne de journal spécifique.
Je recommande d'inclure les informations suivantes dans le préfixe :
- L'heure de l'événement (sans millisecondes) :%t
- Nom ou adresse IP du client distant :%h
- Nom d'utilisateur :%u
- Base de données consultée :%d
- Nom de l'application :%a
- ID de processus :%p
- Arrêt de la sortie du processus hors session :%q
- Le numéro de ligne de journal pour chaque session ou processus, commençant à 1 : %l
Pour comprendre de quoi parle chaque champ du préfixe, nous pouvons ajouter une petite chaîne littérale avant le champ. Ainsi, la valeur de l'ID de processus peut être précédée du littéral "PID=", le nom de la base de données peut être préfixé par "db=", etc. Voici un exemple :
log_line_prefix = 'time=%t, pid=%p %q db=%d, usr=%u, client=%h , app=%a, line=%l '
Selon l'origine de l'événement, le préfixe de la ligne de journal affichera différentes valeurs. Les processus d'arrière-plan et les processus utilisateur enregistreront leurs messages dans le fichier journal. Pour les processus système, j'ai spécifié %q, qui supprimera tout texte après l'ID de processus (%p). Toute autre session affichera le nom de la base de données, le nom d'utilisateur, l'adresse du client, le nom de l'application et une ligne numérotée pour chaque événement.
De plus, j'ai inclus un seul espace après le préfixe de la ligne de journal. Cet espace sépare le préfixe d'événement du journal du message d'événement réel. Il n'est pas nécessaire qu'il s'agisse d'un caractère d'espace ; vous pouvez utiliser n'importe quoi comme un double-virgule (: :), un trait d'union (-) ou un autre séparateur significatif.
Définissez également le paramètre "log_hostname" sur 1 :
log_hostname = 1
Sans cela, seule l'adresse IP du client sera affichée. Dans les systèmes de production, il s'agira généralement de l'adresse du proxy, de l'équilibreur de charge ou du pool de connexions. À moins que vous ne connaissiez par cœur les adresses IP de ces systèmes, il peut être utile de consigner leurs noms d'hôte. Cependant, la recherche DNS ajoutera également du temps supplémentaire au démon de journalisation pour écrire dans le fichier.
Un autre paramètre qui doit être défini avec le "log_line_prefix" est "log_timezone". Définir ceci sur le fuseau horaire local du serveur garantira que les événements enregistrés sont faciles à suivre à partir de leur horodatage au lieu de les convertir d'abord à l'heure locale. Dans l'extrait de code ci-dessous, nous définissons le log_timzeone sur le fuseau horaire standard de l'est de l'Australie :
log_timezone = 'Australia/Sydney'
Connecter uniquement les connexions
Deux paramètres contrôlent la manière dont PostgreSQL enregistre les connexions et les déconnexions des clients. Les deux paramètres sont désactivés par défaut. En fonction des exigences de sécurité de votre organisation, vous pouvez définir l'un d'eux sur 1 et l'autre sur 0 (sauf si vous utilisez un outil comme pgBadger - nous en reparlerons plus tard).
log_connections = 1 log_disconnections = 0
Définir log_connections sur 1 enregistrera toutes les connexions autorisées ainsi que les tentatives Connexions. C'est évidemment bon pour l'audit de sécurité :une attaque par force brute peut être facilement identifiée à partir des journaux. Cependant, avec ce paramètre activé, un environnement PostgreSQL occupé avec des milliers, voire des centaines de connexions valides de courte durée pourrait voir le fichier journal être inondé.
Néanmoins, il peut également identifier des problèmes d'application qui pourraient ne pas être évidents autrement. Un grand nombre de tentatives de connexion à partir de nombreuses adresses client valides différentes peut indiquer que l'instance a besoin d'un équilibreur de charge ou d'un service de regroupement de connexions devant elle. Un grand nombre de tentatives de connexion à partir d'une seule adresse client peut découvrir une application avec trop de threads nécessitant un certain type de limitation.
Consigner uniquement les opérations DDL et DML
Il y a beaucoup de débats autour de ce qui devrait être enregistré dans le journal Postgres - c'est-à-dire, quelle devrait être la valeur du paramètre "log_statement". Il peut avoir trois valeurs :
log_statement = 'off' | 'ddl' | 'mod' | 'all'
Il peut être tentant de le définir sur "tout" pour capturer chaque instruction SQL exécutée sur le serveur, mais ce n'est pas toujours une bonne idée en réalité.
Les systèmes de production occupés exécutent principalement des instructions SELECT, parfois des milliers par heure. L'instance peut fonctionner parfaitement bien, sans aucun problème de performances. Définir ce paramètre sur "all" dans de tels cas surchargerait inutilement le démon de journalisation car il doit écrire toutes ces instructions dans le fichier.
Ce que vous voulez capturer, cependant, c'est toute corruption de données ou tout changement dans la structure de la base de données qui a causé un certain type de problème. Les modifications de base de données indésirables ou non autorisées causent plus de problèmes d'application que la sélection de données ; c'est pourquoi je recommande de régler ce paramètre sur "mod". Avec ce paramètre, PostgreSQL enregistrera toutes les modifications DDL et DML dans le fichier journal.
Si votre instance PostgreSQL est modérément occupée (des dizaines de requêtes par heure), n'hésitez pas à définir ce paramètre sur "all". Lorsque vous dépannez des requêtes SELECT à exécution lente ou que vous recherchez un accès non autorisé aux données, vous pouvez également le définir temporairement sur "tout". Certaines applications comme pgBadger s'attendent également à ce que vous définissiez ce paramètre sur "tous".
Journaliser les messages "d'avertissement" et plus
Si le paramètre "log_statement" décide du type d'instructions qui seront enregistrées, les deux paramètres suivants dictent le niveau de détail du message :
log_min_messages log_min_error_statement
Chaque événement PostgreSQL a un niveau de message associé. Le niveau de message peut être n'importe quoi, de DEBUG détaillé à PANIC laconique. Plus le niveau est bas, plus le message est détaillé. La valeur par défaut du paramètre "log_min_messages" est "WARNING". Je vous recommande de le conserver à ce niveau, sauf si vous souhaitez que les messages d'information soient également enregistrés.
Le paramètre "log_min_error_statement" contrôle les instructions SQL générant une erreur qui seront enregistrées. Comme "log_min_message", toute instruction SQL ayant un niveau de gravité d'erreur égal ou supérieur à la valeur spécifiée dans "log_min_error_statement" sera enregistrée. La valeur par défaut est "ERREUR", et je recommande de conserver la valeur par défaut.
Conserver les paramètres de durée du journal par défaut
Nous avons alors les deux paramètres suivants :
log_duration log_min_duration_statement
Le paramètre "log_duration" prend une valeur booléenne. Lorsqu'il est défini sur 1, la durée de chaque instruction terminée sera enregistrée. Si défini sur 0, la durée de l'instruction ne sera pas enregistrée. Il s'agit de la valeur par défaut et je vous recommande de la conserver à 0, sauf si vous résolvez des problèmes de performances. Le calcul et l'enregistrement des durées des instructions obligent le moteur de base de données à effectuer un travail supplémentaire (aussi petit soit-il), et lorsqu'il est extrapolé à des centaines ou des milliers de requêtes, les économies peuvent être importantes.
Enfin, nous avons le paramètre "log_min_duration_statement". Lorsque ce paramètre est défini (sans aucune unité, il est pris en millisecondes), la durée de toute instruction égale ou supérieure à la valeur du paramètre sera enregistrée. La définition de cette valeur de paramètre sur 0 enregistrera la durée de toutes les instructions terminées. Le définir sur -1 désactivera la journalisation de la durée des instructions. Il s'agit de la valeur par défaut, et je recommande de la conserver.
La seule fois où vous souhaitez définir ce paramètre sur 0, c'est lorsque vous souhaitez créer une ligne de base de performances pour les requêtes fréquemment exécutées. Gardez cependant à l'esprit que si le paramètre "log_statement" est défini, les instructions enregistrées ne seront pas répétées dans les messages de journal indiquant les durées. Vous devrez donc charger les fichiers journaux dans une table de base de données, puis joindre les valeurs d'ID de processus et d'ID de session à partir des préfixes de ligne de journal pour identifier les instructions associées et leurs durées.
Quel que soit le moyen, une fois que vous disposez d'une ligne de base pour chaque requête fréquemment exécutée, vous pouvez définir le paramètre "log_min_duration_statement" sur la plus élevée des valeurs de ligne de base. Désormais, toute requête s'exécutant plus longtemps que la valeur de référence la plus élevée sera susceptible d'être ajustée.
Conserver la verbosité des messages d'erreur par défaut
Le paramètre "log_error_verbosity" peut prendre trois valeurs possibles :
log_error_verbosity = terse | standard | verbose
Ce paramètre contrôle la quantité d'informations que PostgreSQL enregistrera pour chaque événement enregistré dans le fichier journal. À moins de déboguer une application de base de données, il est préférable de conserver ce paramètre sur "par défaut". Le mode verbeux sera utile lorsque vous devrez capturer le nom du fichier ou de la fonction et le numéro de ligne qui a généré l'erreur. Définir ce paramètre sur "laconique" supprimera la journalisation de la requête, ce qui peut ne pas être utile non plus.
Trouvez une politique de rotation des journaux qui fonctionne pour votre Cas d'utilisation
Je recommande de créer une stratégie de rotation des journaux basée sur la taille ou l'âge du fichier journal, mais pas sur les deux. Deux paramètres de configuration PostgreSQL dictent la manière dont les anciens journaux sont archivés et les nouveaux journaux sont créés :
log_rotation_age = <number of minutes> log_rotation_size = <number of kilobytes>
La valeur par défaut de « log_rotration_age » est de 24 heures et la valeur par défaut de « log_rotation_size » est de 10 mégaoctets.
Dans la plupart des cas, avoir une politique de rotation de taille ne garantit pas toujours que le dernier message de journal dans le fichier journal archivé est entièrement contenu dans ce fichier uniquement.
Si le "log_rotation_age" est maintenu à sa valeur par défaut de 24 heures, chaque fichier peut être facilement identifié et examiné individuellement, car chaque fichier contiendra l'équivalent d'une journée d'événements. Cependant, cela ne garantit pas non plus que chaque fichier sera une unité autonome de journaux des dernières 24 heures. Parfois, une requête lente peut prendre plus de 24 heures pour se terminer ; des événements peuvent se produire lorsque l'ancien fichier est fermé et que le nouveau est généré. Cela peut être le cas lors d'un travail par lots de nuit, entraînant l'enregistrement de certaines parties des requêtes dans un fichier et le reste dans un autre.
Notre recommandation est de trouver une période de rotation des journaux qui fonctionne pour votre cas d'utilisation. Vérifier le décalage horaire entre deux périodes consécutives de plus faible activité (par exemple, d'un samedi à l'autre). Vous pouvez ensuite définir la valeur « log_rotation_age » sur cette différence de temps, et pendant l'une de ces périodes, redémarrez le service PostgreSQL. De cette façon, PostgreSQL fera tourner le fichier journal actuel lors de la prochaine période d'accalmie. Cependant, si vous devez redémarrer le service entre ces périodes, la rotation des journaux sera à nouveau faussée. Vous devrez alors répéter ce processus. Mais comme pour beaucoup d'autres choses dans PostgreSQL, les essais et les erreurs dicteront la meilleure valeur. De plus, si vous utilisez un utilitaire de gestion des journaux, l'âge ou la taille de la rotation des journaux n'aura pas d'importance, car l'agent du gestionnaire de journaux ingérera chaque événement du fichier au fur et à mesure qu'il sera ajouté.
Utilisez des outils comme pgBadger
pgBadger est un puissant analyseur de journaux PostgreSQL qui peut créer des informations très utiles à partir des fichiers journaux PostgreSQL. Il s'agit d'un outil open-source écrit en Perl avec un très faible encombrement dans la machine sur laquelle il s'exécute. L'outil est exécuté à partir de la ligne de commande et est livré avec un grand nombre d'options. Il prendra un ou plusieurs journaux en entrée et peut produire un rapport HTML avec des statistiques détaillées sur :
- Requêtes en attente les plus fréquentes
- Requêtes générant la plupart des fichiers temporaires ou les fichiers temporaires les plus volumineux
- Requêtes les plus lentes
- Durée moyenne des requêtes
- Requêtes les plus fréquemment exécutées
- Erreurs les plus fréquentes dans les requêtes
- Utilisateurs et application qui exécutent des requêtes
- Statistiques des points de contrôle.
- Statistiques de vide automatique et d'analyse automatique.
- Statistiques de verrouillage
- Événements d'erreur (panique, fatal, erreur et avertissement).
- Profils de connexion et de session (par base de données, utilisateur, application)
- Profils de session
- Profils de requêtes (types de requêtes, requêtes par base de données/application)
- Statistiques d'E/S
- etc.
Comme je l'ai mentionné précédemment, certains des paramètres de configuration liés au journal doivent être activés pour capturer tous les événements du journal afin que pgBadger puisse analyser efficacement ces journaux. Comme cela peut produire des fichiers journaux volumineux avec de nombreux événements, pgBadger ne doit être utilisé que pour créer des benchmarks ou résoudre des problèmes de performances. Une fois les journaux détaillés générés, les paramètres de configuration peuvent être modifiés pour revenir à leurs valeurs d'origine. Pour une analyse continue des journaux, il est préférable d'utiliser une application de gestion des journaux dédiée.
Si vous êtes plus à l'aise de faire des choses dans l'invite de commande et d'utiliser les vues système, vous voudrez utiliser pg_stat_statements. En fait, cela devrait être activé dans toute installation PostgreSQL de production.
pg_stat_statements est une extension PostgreSQL et avec l'installation par défaut maintenant. Pour l'activer, le paramètre de configuration « shared_preload_libraries » doit avoir pg_stat_statements comme l'une de ses valeurs. Il peut ensuite être installé comme n'importe quelle autre extension en utilisant la commande "CREATE EXTENSION". L'extension crée la vue pg_stat_statement qui fournit des informations précieuses sur les requêtes.
Utilisez une application de gestion des journaux pour obtenir des informations
Il existe de nombreux utilitaires de gestion des journaux sur le marché, et la plupart des organisations en utilisent un ou plusieurs de nos jours. Quel que soit l'outil en place, je recommande de l'utiliser pour collecter et gérer les journaux PostgreSQL.
Il y a plusieurs raisons à cela :
Il est beaucoup plus facile d'analyser, d'analyser et de filtrer le bruit des fichiers journaux avec un outil automatisé. Parfois, un événement peut s'étendre sur plusieurs fichiers journaux en fonction de la durée de l'événement et de l'âge ou de la taille de rotation du journal. Avoir un gestionnaire de journaux simplifie beaucoup la présentation de ces informations dans leur ensemble.
Les solutions de gestion des journaux d'aujourd'hui sont généralement dotées d'une capacité intégrée d'analyse des journaux PostgreSQL. Certains sont également livrés avec des tableaux de bord qui peuvent afficher les métriques les plus courantes extraites de ces journaux.
La plupart des applications modernes de gestion des journaux offrent également de puissantes fonctionnalités de recherche, de filtrage, de correspondance de modèles, de corrélation d'événements et d'analyse de tendances activées par l'IA. Ce qui n'est pas visible aux yeux ordinaires peut être facilement mis en évidence par ces outils.
Enfin, l'utilisation d'un gestionnaire de journaux pour stocker les journaux PostgreSQL signifie également que les événements sont enregistrés pour la postérité, même si les fichiers d'origine sont supprimés accidentellement ou de manière malveillante.
Bien qu'il y ait des avantages évidents à utiliser une application de gestion des journaux, de nombreuses organisations ont des restrictions sur où leurs journaux peuvent vivre. Il s'agit d'un cas typique des solutions SaaS où les journaux sont souvent enregistrés en dehors des limites géographiques d'une organisation, ce qui peut ne pas être conforme aux exigences réglementaires.
Dans de tels cas, je recommande de choisir un fournisseur avec une présence dans le centre de données local - si possible - ou d'utiliser un gestionnaire de journaux autogéré hébergé sur le réseau de l'organisation, comme une pile ELK.
Derniers mots
Les journaux du serveur PostgreSQL peuvent être une mine d'or d'informations lorsqu'ils sont correctement configurés. L'astuce consiste à déterminer ce qu'il faut enregistrer et la quantité à enregistrer, et plus important encore, à tester si les journaux peuvent fournir les bonnes informations en cas de besoin. Ce sera une question d'essais et d'erreurs, mais ce dont j'ai discuté ici aujourd'hui devrait donner un bon départ. Comme je l'ai dit au début, je serais plus qu'heureux d'entendre parler de votre expérience de configuration de la journalisation PostgreSQL pour des résultats optimaux.