中文文档
BqLog est un système de journalisation léger et hautes performances utilisé dans des projets tels que « Honor of Kings ». Il a été déployé avec succès et fonctionne correctement.
Windows 64 bits
Mac OS
Linux
IOS
Android (X86_64, arm64-v8a, armeabi-v7a)
Unix (Réussir le test sur FreeBSD)
C++
Java
Kotlin
C#
Par rapport aux bibliothèques de journalisation open source existantes, BqLog offre des avantages significatifs en termes de performances (voir Benchmark). Il convient non seulement aux serveurs et aux clients, mais est également hautement compatible avec les appareils mobiles.
Avec une faible consommation de mémoire, dans le cas Benchmark de 10 threads et 20 000 000 d'entrées de journal, BqLog lui-même consomme moins de 1 Mo de mémoire.
Fournit un format de journal en temps réel haute performance et haute compression
Peut être utilisé normalement dans les moteurs de jeu ( Unity
, Unreal
), avec la prise en charge des types courants fournis pour Unreal.
Prend en charge les caractères et chaînes UTF-8
, UTF-16
, UTF-32
, ainsi que les types de paramètres courants tels que bool, float, double et diverses longueurs et types d'entiers.
Prend en charge format specifications
C++20
La journalisation asynchrone prend en charge la révision des crashs pour éviter la perte de données (inspirée de XLog)
Taille extrêmement petite, la bibliothèque dynamique ne faisant qu'environ 200 Ko après la compilation Android
Ne génère pas d'allocations de tas supplémentaires en Java et C#, évitant ainsi la création constante de nouveaux objets pendant l'exécution
Dépend uniquement de la bibliothèque de langage C standard et des API de la plate-forme, et peut être compilé en mode ANDROID_STL = none
d'Android
Prend en charge les normes de compilation C++11
et ultérieures et peut être compilé selon les exigences strictes de -Wall -Wextra -pedantic -Werror
Le module de compilation est basé sur CMake
et fournit des scripts de compilation pour différentes plateformes, ce qui le rend facile à utiliser
Prend en charge les types de paramètres personnalisés
Très convivial pour les suggestions de code
Pourquoi BqLog est-il si rapide - Format de journal compressé en temps réel haute performance
Pourquoi BqLog est-il si rapide - Buffer en anneau à haute concurrence
Intégrer BqLog dans votre projet
Démo simple
Présentation de l'architecture
Instructions d'utilisation de l'API du processus principal
1-Création d'un objet de journal
2-Récupération d'un objet de journal
3-Enregistrement des messages
4-Autres API
Journalisation synchrone et asynchrone
1. Sécurité des threads de la journalisation asynchrone
Introduction aux annexes
1. ConsoleAppender
2. TextFileAppender
3. CompressedFileAppender (fortement recommandé)
4. RawFileAppender
Instructions de configuration
1. Exemple complet
2. Explication détaillée
Décodage hors ligne des appenders au format binaire
Instructions de construction
1. Création de bibliothèque
2. Création et exécution de la démo
3. Instructions d'exécution des tests automatisés
4. Instructions d'exécution de référence
Sujets d'utilisation avancée
1. Aucune allocation de tas
2. Consigner les objets avec prise en charge des catégories
3. Protection des données en cas de sortie anormale du programme
4. À propos de NDK et ANDROID_STL = aucun
5. Types de paramètres personnalisés
6. Utilisation de BqLog dans Unreal Engine
Référence
1. Description de l'indice de référence
2. Code de référence BqLog C++
3. Code de référence Java BqLog
4. Code de référence Log4j
5. Résultats de référence
BqLog peut être intégré à votre projet sous diverses formes. Pour C++, il prend en charge les bibliothèques dynamiques, les bibliothèques statiques et les fichiers sources. Pour Java et C#, il prend en charge les bibliothèques dynamiques avec le code source wrapper. Vous trouverez ci-dessous les méthodes pour inclure BqLog :
Le référentiel de code comprend des fichiers de bibliothèque dynamique précompilés situés dans /dist/dynamic_lib/. Pour intégrer BqLog dans votre projet à l'aide des fichiers de bibliothèque, vous devez procéder comme suit :
Sélectionnez le fichier de bibliothèque dynamique correspondant à votre plateforme et ajoutez-le au système de build de votre projet.
Copiez le répertoire /dist/dynamic_lib/include dans votre projet et ajoutez-le à la liste des répertoires d'inclusion. (Si vous utilisez la bibliothèque .framework de XCode, vous pouvez ignorer cette étape car le fichier .framework inclut déjà les fichiers d'en-tête).
Le référentiel de code comprend des fichiers de bibliothèque statique précompilés situés dans /dist/static_lib/. Pour intégrer BqLog dans votre projet à l'aide des fichiers de bibliothèque, vous devez procéder comme suit :
Sélectionnez le fichier de bibliothèque statique correspondant à votre plateforme et ajoutez-le au système de build de votre projet.
Copiez le répertoire /dist/static_lib/include dans votre projet et ajoutez-le à la liste des répertoires d'inclusion. (Si vous utilisez la bibliothèque .framework de XCode, vous pouvez ignorer cette étape car le fichier .framework inclut déjà les fichiers d'en-tête).
BqLog prend également en charge l'inclusion directe du code source dans votre projet pour compilation. Pour intégrer BqLog à l'aide du code source, suivez ces étapes :
Copiez le répertoire /src dans votre projet comme référence de code source.
Copiez le répertoire /include dans votre projet et ajoutez-le à la liste des répertoires d'inclusion.
Si vous compilez la version Windows dans Visual Studio, ajoutez /Zc:__cplusplus aux options de compilation pour garantir que la prise en charge standard actuelle du compilateur C++ est correctement déterminée.
Si vous utilisez le code source dans le NDK d'Android, veuillez vous référer à 4. À propos de NDK et ANDROID_STL = none pour des considérations importantes.
En C#, BqLog peut être utilisé via une bibliothèque dynamique native et un Wrapper C#, prenant en charge les moteurs Mono, Microsoft CLR et Unity. Unity est compatible avec les modes Mono et IL2CPP. Pour utiliser BqLog en C#, procédez comme suit :
Sélectionnez le fichier de bibliothèque dynamique correspondant à votre plateforme dans /dist/dynamic_lib/ et ajoutez-le à votre projet (pour Unity, reportez-vous à Unity Import et configurez les plug-ins).
Copiez les fichiers de code source de /wrapper/csharp/src dans votre projet.
En Java, BqLog peut être utilisé via une bibliothèque dynamique native et un Java Wrapper, prenant en charge les environnements JVM courants et Android. Pour intégrer BqLog dans une JVM, suivez ces étapes :
Sélectionnez le fichier de bibliothèque dynamique correspondant à votre plateforme dans /dist/dynamic_lib/ et ajoutez-le à votre projet.
Copiez les fichiers de code source de /wrapper/java/src dans votre projet.
(Facultatif) Copiez le répertoire /dist/dynamic_lib/include dans votre projet et ajoutez-le à la liste des répertoires d'inclusion si vous avez l'intention d'appeler BqLog à partir du NDK.
Le code suivant affichera plus de 1000 journaux sur votre console (ou ADB Logcat si sur Android)
#si défini (WIN32) #include#endif#include #include int main() { #if défini (WIN32) // Basculez la ligne de commande Windows vers UTF-8 car BqLog génère tout le texte final en codage UTF-8 pour éviter les problèmes d'affichage SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); #endif // Cette chaîne est la configuration du journal. Ici, il configure un enregistreur avec un appender (cible de sortie) nommé appender_0, qui génère une sortie vers la console. std::string config = R"( # La cible de sortie de cet appender est la console appenders_config.appender_0.type=console # Cet appender utilise l'heure locale pour les horodatages appenders_config.appender_0.time_zone=heure locale par défaut # Cet appender génère les journaux de ces 6 niveaux (pas d'espace entre les deux) appenders_config.appender_0.levels=[verbose,debug,info,warning,error,fatal] )"; bq::log log = bq::log::create_log("my_first_log", config); // Créez un objet journal en utilisant la configuration for(int i = 0; i < 1024; ++i) { log.info("Ceci est un journal de test d'informations, la chaîne de format est UTF-8, param int :{}, param bool :{}, param string8 :{}, param string16 :{}, param string32 :{} , param float:{}", i, true, "utf8-string", u"utf16-string", U"utf32-string", 4.3464f); } log.error(U"Ceci est un journal de test d'erreurs, la chaîne de format est UTF-32"); bq::log::force_flush_all_logs(); // BqLog par défaut est une sortie asynchrone. Pour garantir que les journaux sont visibles avant la sortie du programme, forcez le vidage à synchroniser la sortie une fois. renvoie 0 ; }
utilisant System.Text; utilisant System; public class demo_main { public static void Main (string [] args) { Console.OutputEncoding = Encodage.UTF8 ; Console.InputEncoding = Encodage.UTF8 ; string config = @" # La cible de sortie de cet appender est la console appenders_config.appender_0.type=console # Cet appender utilise l'heure locale pour les horodatages ppenders_config.appender_0.time_zone=heure locale par défaut # Cet appender génère les journaux de ces 6 niveaux (sans espaces dans entre) appenders_config.appender_0.levels=[verbose,debug,info,warning,error,fatal] "; bq.log log = bq.log.create_log("my_first_log", config); // Créez un objet journal en utilisant la configuration for (int i = 0; i < 1024; ++i) { log.info("Ceci est un journal de test d'informations, la chaîne de format est UTF-16, param int:{}, param bool :{}, param string:{}, param float:{}", i, true, " Texte de chaîne", 4.3464f); } bq.log.force_flush_all_logs(); Console.ReadKey(); }}
public class demo_main { public static void main(String[] args) { // TODO Stub de méthode généré automatiquement String config = """ # La cible de sortie de cet appender est la console appenders_config.appender_0.type=console # Cet appender utilise l'heure locale pour les horodatages appenders_config.appender_0.time_zone=heure locale par défaut # Cet appender génère des journaux de ces 6 niveaux (sans espaces entre les deux) appenders_config.appender_0.levels=[verbose,debug,info,warning,error,fatal] """; bq.log log = bq.log.create_log("my_first_log", config); // Créez un objet journal en utilisant la configuration pour (int i = 0; i < 1024; ++i) { log.info("Ceci est un journal de test d'informations, la chaîne de format est UTF-16, param int:{}, param bool :{}, param string:{}, param float:{}", i, true, "Chaîne de texte", 4.3464f); } bq.log.force_flush_all_logs(); } }
Le diagramme ci-dessus illustre clairement la structure de base de BqLog. Sur le côté droit du diagramme se trouve l'implémentation interne de la bibliothèque BqLog, tandis que sur le côté gauche se trouvent votre programme et votre code. Votre programme peut appeler BqLog à l'aide des wrappers fournis (API orientées objet pour différents langages). Dans le diagramme, deux journaux sont créés : l'un nommé "Log A" et l'autre nommé "Log B". Chaque journal est attaché à un ou plusieurs appenders. Un Appender peut être compris comme la cible de sortie du contenu du journal. Il peut s'agir de la console (journaux ADB Logcat pour Android), de fichiers texte ou même de formats spécialisés tels que des fichiers journaux compressés ou des fichiers au format journal binaire standard.
Au sein du même processus, les wrappers de différentes langues peuvent accéder au même objet Log. Par exemple, si un objet Log nommé Log A est créé en Java, il est également accessible et utilisé du côté C++ sous le nom Log A.
Dans des cas extrêmes, comme un jeu développé par Unity fonctionnant sur le système Android, vous pouvez impliquer les langages Java, Kotlin, C# et C++ dans la même application. Ils peuvent tous partager le même objet Log. Vous pouvez créer le journal côté Java à l'aide de create_log, puis y accéder dans d'autres langues à l'aide de get_log_by_name.
Remarque : Les API suivantes sont déclarées dans la classe bq::log (ou bq.log). Pour gagner de la place, seules les API C++ sont répertoriées. Les API en Java et C# sont identiques et ne seront pas répétées ici.
En C++, bq::string
est le type de chaîne UTF-8 dans la bibliothèque BqLog. Vous pouvez également transmettre des chaînes de style C comme char ou std::string
ou std::string_view
, qui seront automatiquement et implicitement converties.
Un objet journal peut être créé à l'aide de la fonction statique create_log. Sa déclaration est la suivante :
//API C++ ////// Créer un objet de journal /// /// Si le nom du journal est une chaîne vide, bqLog vous attribuera automatiquement un nom de journal unique. Si le nom du journal existe déjà, il renverra l'objet de journal existant précédemment et remplacera la configuration précédente par la nouvelle configuration. /// Chaîne de configuration du journal ///Un objet journal, si la création échoue, sa méthode is_valid() renverra false static log create_log(const bq::string& log_name, const bq::string& config_content);
Le code crée un objet journal en transmettant le nom de l'objet journal et une chaîne de configuration. La configuration du journal peut être référencée dans les instructions de configuration. Voici quelques points clés à noter :
Qu'il s'agisse de C# ou de Java, l'objet journal renvoyé ne sera jamais nul. Cependant, en raison d'erreurs de configuration ou pour d'autres raisons, un objet de journal non valide peut être créé. Par conséquent, vous devez utiliser la fonction is_valid() pour vérifier l'objet renvoyé. Effectuer des opérations sur un objet non valide peut provoquer le blocage du programme.
Si une chaîne vide est transmise comme nom de journal, bqLog générera automatiquement un nom de journal unique, tel que « AutoBqLog_1 ».
L’appel de create_log sur un objet journal déjà existant portant le même nom ne créera pas de nouvel objet journal mais écrasera la configuration précédente par la nouvelle. Cependant, certains paramètres ne peuvent pas être modifiés au cours de ce processus ; voir les instructions de configuration pour plus de détails.
Sauf lors de l'utilisation dans le NDK (voir 4. À propos du NDK et ANDROID_STL = none), vous pouvez initialiser l'objet journal directement dans des variables globales ou statiques en utilisant cette API dans d'autres situations.
Si un objet journal a déjà été créé ailleurs, vous pouvez obtenir l'objet journal créé directement à l'aide de la fonction get_log_by_name.
//API C++ ////// Obtenez un objet de journal par son nom /// /// Nom de l'objet de journal que vous souhaitez rechercher ///Un objet de journal, si l'objet de journal avec un nom spécifique n'a pas été trouvé, sa méthode is_valid() renverra false journal statique get_log_by_name(const bq::string& log_name);
Vous pouvez également utiliser cette fonction pour initialiser un objet journal dans des variables globales ou des fonctions statiques. Notez toutefois que vous devez vous assurer que l’objet journal portant le nom spécifié existe déjà. Sinon, l'objet journal renvoyé sera inutilisable et sa méthode is_valid() renverra false.
///Fonctions de journalisation principales, il existe 6 niveaux de journalisation : ///verbose, debug, info, warn, error, fatal templatebq::enable_if_t ::value, bool> verbose(const STR& log_content) const; modèle bq::enable_if_t ::value, bool> verbose(const STR& log_format_content, const Args&... args) const; modèle bq::enable_if_t ::value, bool> debug(const STR& log_content) const; modèle bq::enable_if_t ::value, bool> debug(const STR& log_format_content, const Args&... args) const; modèle bq::enable_if_t ::value, bool> info(const STR& log_content) const; modèle bq::enable_if_t ::value, bool> info(const STR& log_format_content, const Args&... args) const; modèle bq::enable_if_t ::value, bool> avertissement(const STR& log_content) const; modèle bq::enable_if_t ::value, bool> avertissement(const STR& log_format_content, const Args&... args) const; modèle bq::enable_if_t ::value, bool> error(const STR& log_content) const; modèle bq::enable_if_t ::value, bool> error(const STR& log_format_content, const Args&... args) const; modèle bq::enable_if_t ::value, bool> fatal(const STR& log_content) const; modèle bq::enable_if_t ::value, bool> fatal(const STR& log_format_content, const Args&... args) const;
Lorsque vous enregistrez des messages, faites attention à trois points clés :
Comme vous pouvez le constater, nos journaux sont divisés en six niveaux : détaillé, débogage, information, avertissement, erreur et fatal, conformément à Android. Leur importance augmente séquentiellement. Lors de leur sortie sur la console, ils apparaîtront dans des couleurs différentes.
Le paramètre STR est similaire au premier paramètre de printf et peut être de différents types de chaînes courants, notamment :
Java.lang.String de Java
La chaîne de C#
Divers codages de chaînes de style C de C++ et std::string
( char*
, char16_t*
, char32_t*
, wchar_t*
, std::string
, std::u8string
, std::u16string
, std::u32string
, std::wstring
, std::string_view
, std::u16string_view
, std::u32string_view
, std::wstring_view
et même des types de chaînes personnalisés, auxquels vous pouvez vous référer dans Custom Parameter Types )
Vous pouvez ajouter divers paramètres après le paramètre STR. Ces paramètres seront formatés aux endroits spécifiés dans le STR, en suivant des règles similaires au std::format de C++20 (sauf pour le manque de prise en charge des arguments de position et du format date/heure). Par exemple, l'utilisation d'un seul {} représente un formatage par défaut d'un paramètre et {:.2f} spécifie la précision de formatage d'un nombre à virgule flottante. Essayez d'utiliser des paramètres formatés pour générer des journaux plutôt que de concaténer des chaînes manuellement. Cette approche est optimale pour les performances et le stockage au format compressé.
Les types de paramètres actuellement pris en charge incluent :
Pointeurs nuls (sortie comme null)
Pointeurs (sortie sous forme d'adresse hexadécimale commençant par 0x)
bouffon
Caractères à un octet (char)
Caractères à deux octets (char16_t, wchar_t, char de C#, char de Java)
Caractères de quatre octets (char32_t ou wchar_t)
Entiers de 8 bits
Entiers non signés de 8 bits
Entiers de 16 bits
Entiers non signés de 16 bits
Entiers de 32 bits
Entiers non signés de 32 bits
Entiers de 64 bits
Entiers non signés de 64 bits
Nombres à virgule flottante 32 bits
Nombres à virgule flottante 64 bits
Autres types de POD inconnus en C++ (limités aux tailles 1, 2, 4 ou 8 octets, traités respectivement comme int8, int16, int32 et int64)
Chaînes, y compris tous les types de chaînes mentionnés dans le paramètre STR
Toute classe ou objet en C# et Java (générant leur chaîne ToString())
Types de paramètres personnalisés, comme détaillé dans Types de paramètres personnalisés
Il existe d'autres API couramment utilisées qui peuvent accomplir des tâches spécifiques. Pour des descriptions détaillées des API, reportez-vous à bq_log/bq_log.h, ainsi qu'à la classe bq.log en Java et C#. Voici quelques API clés qui doivent être mises en avant :
////// Désinitialisez BqLog, veuillez appeler cette fonction avant que votre programme n'existe. /// static void uninit();
Il est recommandé d'exécuter uninit()
avant de quitter le programme ou de désinstaller la bibliothèque dynamique auto-implémentée qui utilise BqLog, sinon le programme pourrait rester bloqué lors de la sortie dans certaines circonstances spécifiques.
////// Si bqLog est asynchrone, un crash du programme peut empêcher la conservation des journaux dans le tampon sur le disque. /// Si cette fonctionnalité est activée, bqLog tentera d'effectuer un vidage forcé des journaux dans le tampon en cas de crash. Cependant, /// cette fonctionnalité ne garantit pas le succès et ne prend en charge que les systèmes POSIX. /// static void activate_auto_crash_handle();
Pour une introduction détaillée, voir Protection des données en cas de sortie anormale du programme
////// Videz de manière synchrone le tampon de tous les objets de journal /// pour garantir que toutes les données du tampon sont traitées après l'appel. /// static void force_flush_all_logs(); ////// Videz de manière synchrone le tampon de cet objet de journal /// pour garantir que toutes les données du tampon sont traitées après l'appel. /// void force_flush();
Étant donné que bqLog utilise la journalisation asynchrone par défaut, vous souhaiterez peut-être parfois synchroniser et afficher immédiatement tous les journaux. Dans de tels cas, vous devez appeler avec force force_flush().
////// Enregistrez un rappel qui sera invoqué chaque fois qu'un message de journal de la console est généré. /// Ceci peut être utilisé par un système externe pour surveiller la sortie du journal de la console. /// /// static void register_console_callback(bq::type_func_ptr_console_callback callback); ////// Annuler l'enregistrement d'un rappel de console. /// /// static void unregister_console_callback(bq::type_func_ptr_console_callback callback);
La sortie de ConsoleAppender est envoyée à la console ou aux journaux ADB Logcat sur Android, mais cela peut ne pas couvrir toutes les situations. Par exemple, dans les moteurs de jeu personnalisés ou les IDE personnalisés, un mécanisme est fourni pour appeler une fonction de rappel pour chaque sortie du journal de la console. Cela vous permet de retraiter et de sortir le journal de la console n'importe où dans votre programme.
Attention supplémentaire : ne publiez aucun journal BQ synchronisé dans le rappel de la console, car cela pourrait facilement conduire à des blocages.
////// Activer ou désactiver le tampon d'ajout de console. /// Étant donné que notre wrapper peut s'exécuter à la fois sur des machines virtuelles C# et Java et que nous ne souhaitons pas appeler directement des rappels à partir d'un thread natif, /// nous pouvons activer cette option. De cette façon, toutes les sorties de la console seront enregistrées dans le tampon jusqu'à ce que nous les récupérions. /// /// ///static void set_console_buffer_enable(bool activate); /// /// Récupère et supprime une entrée de journal du tampon d'appender de la console de manière thread-safe. /// Si le tampon de l'appender de la console n'est pas vide, la fonction on_console_callback sera invoquée pour cette entrée de journal. /// Veuillez vous assurer de ne pas générer de journaux BQ synchronisés dans la fonction de rappel. /// /// Une fonction de rappel à appeler pour l'entrée de journal récupérée si le tampon d'appender de la console n'est pas vide ///True si le Le tampon d'ajout de console n'est pas vide et une entrée de journal est récupérée ; sinon False est renvoyé. static bool fetch_and_remove_console_buffer(bq::type_func_ptr_console_callback on_console_callback);
En plus d'intercepter la sortie de la console via un rappel de console, vous pouvez récupérer activement les sorties du journal de la console. Parfois, nous ne souhaitons pas que la sortie du journal de la console passe par un rappel, car vous ne savez pas de quel thread le rappel proviendra (par exemple, dans certaines machines virtuelles C# ou JVM, la VM peut effectuer un garbage collection lorsque la console un rappel est appelé, ce qui pourrait potentiellement entraîner des blocages ou des plantages).
La méthode utilisée ici consiste à activer le tampon de la console via set_console_buffer_enable
. Cela entraîne le stockage de chaque sortie du journal de la console en mémoire jusqu'à ce que nous appelions activement fetch_and_remove_console_buffer
pour la récupérer. Par conséquent, si vous choisissez d'utiliser cette méthode, n'oubliez pas de récupérer et d'effacer rapidement les journaux pour éviter toute mémoire non libérée.
Attention supplémentaire : ne publiez aucun journal BQ synchronisé dans le rappel de la console, car cela pourrait facilement conduire à des blocages.
Attention supplémentaire : si vous utilisez ce code dans un environnement IL2CPP, assurez-vous que on_console_callback est marqué comme statique dangereux et est décoré avec l'attribut [MonoPInvokeCallback(typeof(type_console_callback))] .
////// Modifiez la configuration du journal, mais certains champs, tels que buffer_size, ne peuvent pas être modifiés. /// /// ///bool reset_config(const bq::string& config_content);
Parfois, vous souhaiterez peut-être modifier la configuration d'un journal dans votre programme. En plus de recréer l'objet journal pour écraser la configuration (voir Création d'un objet journal), vous pouvez également utiliser l'interface de réinitialisation. Notez cependant que tous les éléments de configuration ne peuvent pas être modifiés de cette façon. Pour plus de détails, reportez-vous aux instructions de configuration
////// Désactiver ou activer temporairement un Appender spécifique. /// /// /// void set_appenders_enable(const bq::string& appender_name, bool activate) ;
Par défaut, les Appenders de la configuration sont actifs, mais un mécanisme est fourni ici pour les désactiver et les réactiver temporairement.
////// Fonctionne uniquement lorsque l'instantané est configuré. /// Il décodera le tampon d'instantané en texte. /// /// si l'horodatage de chaque journal est l'heure GMT ou l'heure locale ///le tampon d'instantané décodé bq::string take_snapshot(bool use_gmt_time) const;
Parfois, certaines fonctionnalités spéciales nécessitent la sortie de la dernière partie des journaux, ce qui peut être effectué à l'aide de la fonction d'instantané. Pour activer cette fonctionnalité, vous devez d'abord activer l'instantané dans la configuration du journal et définir la taille maximale du tampon, en octets. De plus, vous devez spécifier les niveaux de journalisation et les catégories à filtrer pour l'instantané (facultatif). Pour une configuration détaillée, veuillez vous référer à Configuration d'instantané. Lorsqu'un instantané est nécessaire, l'appel de take_snapshot() renverra la chaîne formatée contenant les entrées de journal les plus récentes stockées dans le tampon d'instantané. En C++, le type est bq::string
, qui peut être implicitement converti en std::string
.
namespace bq{ namespace tools { //Il s'agit d'une classe utilitaire pour décoder les formats de journaux binaires. //Pour l'utiliser, créez d'abord un objet log_decoder, //puis appelez sa fonction decode pour décoder. //Après chaque appel réussi, //vous pouvez utiliser get_last_decoded_log_entry() pour récupérer le résultat décodé. //Chaque appel décode une entrée de journal. structure log_decoder { privé: bq::string decode_text_; bq::appender_decode_result result_ = bq::appender_decode_result::success; uint32_t handle_ = 0; public : ////// Créez un objet log_decoder, chaque objet log_decoder correspondant à un fichier journal binaire. /// /// le chemin d'un fichier journal binaire peut être un chemin relatif ou un chemin absolu log_decoder(const bq::string& log_file_path); ~log_decoder(); ////// Décode une entrée de journal. chaque appel de cette fonction ne décodera qu'une seule entrée de journal /// ///résultat du décodage, appender_decode_result::eof signifie que l'intégralité du fichier journal a été décodé bq::appender_decode_result decode(); ////// obtenir le dernier résultat du décodage /// ///bq::appender_decode_result get_last_decode_result() const; /// /// obtenir le contenu de la dernière entrée du journal de décodage /// ///const bq::string& get_last_decoded_log_entry() const; } ; } }
Il s'agit d'une classe utilitaire qui peut décoder les fichiers journaux générés par les Appenders de type binaire au moment de l'exécution, tels que CompressedFileAppender et RawFileAppender.
Pour l'utiliser, créez d'abord un objet log_decoder. Ensuite, chaque fois que vous appelez la fonction decode(), elle décode une entrée de journal en séquence. Si le résultat renvoyé est bq::appender_decode_result::success, vous pouvez appeler get_last_decoded_log_entry() pour obtenir le contenu textuel formaté de la dernière entrée de journal décodée. Si le résultat est bq::appender_decode_result::eof, cela signifie que tous les journaux ont été entièrement lus.
BqLog vous permet de configurer si un objet de journal est synchrone ou asynchrone via le paramètre thread_mode. Les principales différences entre ces deux modes sont les suivantes :
Journalisation synchrone | Journalisation asynchrone | |
---|---|---|
Comportement | Après avoir appelé la fonction de journalisation, le journal est immédiatement écrit dans l'appender correspondant. | Après avoir appelé la fonction de journalisation, le journal n'est pas immédiatement écrit ; au lieu de cela, il est transmis à un thread de travail pour un traitement périodique. |
Performance | Faible, car le thread qui écrit le journal doit bloquer et attendre que le journal soit écrit dans l'appender correspondant avant de revenir de la fonction de journalisation. | Élevé, car le thread qui écrit le journal n'a pas besoin d'attendre la sortie réelle et peut revenir immédiatement après la journalisation. |
Sécurité des fils | Élevé, mais nécessite que les paramètres de journalisation ne soient pas modifiés lors de l'exécution de la fonction de journalisation. | Élevé, mais nécessite que les paramètres de journalisation ne soient pas modifiés lors de l'exécution de la fonction de journalisation. |
Une idée fausse courante à propos de la journalisation asynchrone est qu'elle est moins sécurisée pour les threads, les utilisateurs craignant que les paramètres puissent être récupérés au moment où le thread de travail traite le journal. Par exemple:
{ const char str_array[5] = {'T', 'E', 'S', 'T', '�'}; const char* str_ptr = str_array; log_obj.info("Ceci est le paramètre de test : {}, {}", str_array, str_ptr); }
Dans l'exemple ci-dessus, str_array
est stocké sur la pile, et une fois la portée quittée, sa mémoire n'est plus valide. Les utilisateurs peuvent craindre que si la journalisation asynchrone est utilisée, au moment où le thread de travail traite le journal, str_array
et str_ptr
seront des variables non valides.
Cependant, une telle situation ne se produira pas car BqLog copie tout le contenu des paramètres dans son ring_buffer
interne lors de l'exécution de la fonction info
. Une fois la fonction info
renvoyée, les variables externes comme str_array
ou str_ptr
ne sont plus nécessaires. De plus, le ring_buffer
ne stockera pas d'adresse de pointeur const char*
mais stockera toujours la chaîne entière.
Le véritable problème potentiel se pose dans le scénario suivant :
static std::string global_str = "hello world"; // Il s'agit d'une variable globale modifiée par plusieurs threads.void thread_a() { log_obj.info("Ceci est le paramètre de test : {}", global_str); }
Si le contenu de global_str
change pendant l'exécution de la fonction info
, cela peut conduire à un comportement indéfini. BqLog fera de son mieux pour éviter un crash, mais l'exactitude du résultat final ne peut être garantie.
Un Appender représente la cible de sortie du journal. Le concept des Appenders dans bqLog est fondamentalement le même que dans Log4j. Actuellement, bqLog fournit les types d'appenders suivants :
La cible de sortie de cet Appender est la console, y compris l'ADB d'Android et la console correspondante sur iOS. Le codage du texte est UTF-8.
Cet Appender génère des fichiers journaux directement au format texte UTF-8.
Cet Appender génère des fichiers journaux dans un format compressé, qui est le highly recommended format by bqLog
. Il offre les performances les plus élevées parmi tous les Appenders et produit le plus petit fichier de sortie. Cependant, le fichier final doit être décodé. Le décodage peut être effectué lors du décodage à l'exécution ou du décodage hors ligne.
Cet Appender génère le contenu du journal binaire de la mémoire directement dans un fichier. Ses performances sont supérieures à celles de TextFileAppender, mais il consomme plus d'espace de stockage. Le fichier final doit être décodé. Le décodage peut être effectué lors du décodage à l'exécution ou du décodage hors ligne. Cet Appender n'est pas recommandé pour une utilisation.
Vous trouverez ci-dessous une comparaison complète des différents Appenders :
Nom | Cible de sortie | Directement lisible | Performances de sortie | Taille de sortie |
---|---|---|---|---|
ConsoleAppender | Console | ✔ | Faible | - |
TextFileAppender | Déposer | ✔ | Faible | Grand |
CompressedFileAppender | Déposer | ✘ | Haut | Petit |
RawFileAppender | Déposer | ✘ | Moyen | Grand |
La configuration fait référence à la chaîne de configuration dans les fonctions create_log et reset_config. Cette chaîne utilise le format de fichier de propriétés et prend en charge # commentaires (mais n'oubliez pas de commencer une nouvelle ligne par # pour les commentaires).
Ci-dessous un exemple complet :
# Cette configuration configure un objet de journal avec un total de 5 Appenders, dont deux TextFileAppenders qui génèrent deux fichiers différents.# Le premier Appender est nommé appender_0 et son type est ConsoleAppenderappenders_config.appender_0.type=console# Le fuseau horaire de appender_0 est l'heure locale du systèmeappenders_config.appender_0.time_zone=heure locale par défaut# appender_0 affichera les 6 niveaux de journaux (remarque : il ne doit y avoir aucun espace entre les niveaux de journalisation, sinon l'analyse échouera)appenders_config.appender_0.levels=[verbose,debug ,info,warning,error,fatal]# Le deuxième Appender est nommé appender_1 et son type est TextFileAppenderappenders_config.appender_1.type=text_file# Le fuseau horaire de appender_1 est GMT, soit UTC+0appenders_config.appender_1.time_zone=gmt# appender_1 uniquement génère des journaux de niveau info et supérieur, les autres seront ignorés.appenders_config.appender_1.levels=[info,warning,error,fatal]# Le chemin de appender_1 sera dans le répertoire relatif bqLog du programme, avec les noms de fichiers commençant par normal, suivis de la date et l'extension .log# Sur iOS, il sera enregistré dans /var/mobile/Containers/Data/Application/[APP]/Library/Caches/bqLog# Sur Android, il sera enregistré dans [android.content.Context .getExternalFilesDir()]/bqLogappenders_config.appender_1.file_name=bqLog/normal# La taille maximale du fichier est de 10 000 000 octets ; si dépassé, un nouveau fichier sera crééappenders_config.appender_1.max_file_size=10000000# Les fichiers datant de plus de dix jours seront nettoyésappenders_config.appender_1.expire_time_days=10# Si la taille totale de la sortie dépasse 100 000 000 octets, les fichiers seront nettoyés à partir du oldappenders_config.appender_1.capacity_limit=100000000# Le troisième Appender est nommé appender_2 et son type est TextFileAppenderappenders_config.appender_2.type=text_file# appender_2 affichera tous les niveaux de journauxappenders_config.appender_2.levels=[all]# Le chemin d'accès à appender_2 sera dans le répertoire bqLog relatif du programme, avec les noms de fichiers commençant par new_normal, suivis de la date et de l'extension .logappenders_config.appender_2.file_name=bqLog/new_normal# Cette option n'est efficace que sur Android, enregistrant les journaux dans le répertoire de stockage interne, qui est [android .content.Context.getFilesDir()]/bqLogappenders_config.appender_2.is_in_sandbox=true# Le quatrième Appender est nommé appender_3 et son type est CompressedFileAppenderappenders_config.appender_3.type=compressed_file# appender_3 affichera tous les niveaux de logsappenders_config.appender_3.levels=[all ]# Le chemin de appender_3 sera dans le répertoire de chemin absolu ~/bqLog du programme, avec les noms de fichiers commençant par compress_log, suivi de la date et de l'extension .logcomprappenders_config.appender_3.file_name=~/bqLog/compress_log# Le cinquième Appender est nommé appender_4 et son type est RawFileAppenderappenders_config.appender_4.type=raw_file# appender_4 est désactivé par défaut et peut être activé ultérieurement en utilisant set_appenders_enableappenders_config.appender_4.enable=false# appender_4 affichera tous les niveaux de journauxappenders_config.appender_4.levels=[all]# Le chemin pour appender_4 sera dans le répertoire relatif bqLog du programme, avec les noms de fichiers commençant par raw_log, suivis de la date et de l'extension .lograwappenders_config.appender_4.file_name=bqLog/raw_log# Les journaux ne seront traités que si leur catégorie commence par ModuleA, ModuleB. SystemC, sinon tout sera ignoré (le concept de catégorie est expliqué en détail dans les rubriques d'utilisation avancées plus tard)appenders_config.appender_4.categories_mask=[ModuleA,ModuleB.SystemC]# La taille totale du tampon asynchrone est de 65 535 octets ; la signification spécifique est expliquée plus tardlog.buffer_size=65535# Le niveau de fiabilité du journal est normal ; la signification spécifique est expliquée plus tardlog.reliable_level=normal# Les journaux ne seront traités que si leur catégorie correspond aux trois caractères génériques suivants, sinon tous seront ignorés (le concept de catégorie est expliqué en détail dans les rubriques d'utilisation avancées plus tard)log.categories_mask= [*default,ModuleA,ModuleB.SystemC]# Il s'agit d'un journal asynchrone ; les journaux asynchrones sont les journaux les plus performants et recommandés. typelog.thread_mode=async# Si le niveau de journalisation est une erreur ou fatal, incluez les informations de pile d'appels avec chaque entrée de journal log.print_stack_levels=[error,fatal]# Activez la fonctionnalité d'instantané, la taille du cache d'instantané est de 64 Ko. .buffer_size=65536# Seuls les journaux avec des niveaux d'informations et d'erreur seront enregistrés dans l'instantané.levels=[info,error]# Seuls les journaux dont la catégorie commence par ModuleA, ModuleB.SystemC seront enregistrés dans l'instantané, sinon ils seront ignorés. .categories_mask=[ModuleA.SystemA.ClassA,ModuleB]
appenders_config
est un ensemble de configurations pour Appenders. Le premier paramètre après appenders_config
est le nom de l'Appender, et tous les Appenders portant le même nom partagent la même configuration.
Nom | Requis | Valeurs configurables | Défaut | Applicable à ConsoleAppender | Applicable à TextFileAppender | Applicable à CompressedFileAppender | Applicable à RawFileAppender |
---|---|---|---|---|---|---|---|
taper | ✔ | console, fichier_texte, fichier_comprimé, fichier_raw | ✔ | ✔ | ✔ | ✔ | |
activer | ✘ | Si l'Appender est activé par défaut | vrai | ✔ | ✔ | ✔ | ✔ |
niveaux | ✘ | Tableau de niveaux de journalisation | [tous] | ✔ | ✔ | ✔ | ✔ |
fuseau_heure | ✘ | gmt ou toute autre chaîne | Heure locale | ✔ | ✔ | ✔ | ✔ |
nom de fichier | ✔ | Chemin relatif ou absolu | ✘ | ✔ | ✔ | ✔ | |
est_in_sandbox | ✘ | vrai, faux | FAUX | ✘ | ✔ | ✔ | ✔ |
taille_fichier_max | ✘ | Entier positif ou 0 | 0 | ✘ | ✔ | ✔ | ✔ |
expire_time_days | ✘ | Entier positif ou 0 | 0 | ✘ | ✔ | ✔ | ✔ |
capacité_limite | ✘ | Entier positif ou 0 | 0 | ✘ | ✔ | ✔ | ✔ |
catégories_masque | ✘ | Tableau de chaînes entourées de [] | Vide | ✔ | ✔ | ✔ | ✔ |
Spécifie le type de l'Appender.
console
: représente ConsoleAppender
text_file
: représente TextFileAppender
compressed_file
: représente CompressedFileAppender
raw_file
: représente RawFileAppender
La valeur par défaut est true
. S'il est défini sur false
, l'Appender sera désactivé par défaut et pourra être activé ultérieurement à l'aide de set_appenders_enable
.
Un tableau entouré de []
, contenant toute combinaison de verbose
, debug
, info
, warning
, error
, fatal
ou [all]
pour accepter tous les niveaux. Remarque : N'incluez pas d'espaces entre les niveaux, sinon l'analyse échouera.
Spécifie le fuseau horaire des journaux. gmt
représente l'heure moyenne de Greenwich (UTC+0), et toute autre chaîne ou la laissant vide utilisera le fuseau horaire local. Le fuseau horaire affecte deux choses :
L'horodatage des journaux de texte formaté (applicable à ConsoleAppender et TextFileAppender)
Un nouveau fichier journal sera créé au passage de minuit dans le fuseau horaire spécifié (applicable à TextFileAppender, CompressedFileAppender et RawFileAppender).
Le chemin et le préfixe du nom de fichier pour enregistrer les fichiers. Le chemin peut être absolu (non recommandé pour Android et iOS) ou relatif. Le nom du fichier de sortie final sera ce chemin et ce nom, suivis de la date, du numéro de fichier et de l'extension de l'Appender.
Uniquement significatif sur Android :
true
: les fichiers sont stockés dans le répertoire de stockage interne (android.content.Context.getFilesDir()). S'ils ne sont pas disponibles, ils sont stockés dans le répertoire de stockage externe (android.content.Context.getExternalFilesDir()). Si cela n'est pas non plus disponible, ils sont stockés dans le répertoire Cache (android.content.Context.getCacheDir()).
false
: les fichiers sont stockés par défaut dans le répertoire de stockage externe. S'ils ne sont pas disponibles, ils sont stockés dans le répertoire de stockage interne. Si cela n'est pas non plus disponible, ils sont stockés dans le répertoire Cache.
La taille maximale du fichier en octets. Lorsque le fichier enregistré dépasse cette taille, un nouveau fichier journal est créé, avec des numéros de fichier incrémentés séquentiellement. 0
désactive cette fonctionnalité.
Le nombre maximum de jours pour conserver les fichiers. Les fichiers plus anciens seront automatiquement supprimés. 0
désactive cette fonctionnalité.
La taille totale maximale des fichiers générés par cet Appender dans le répertoire de sortie. Si cette limite est dépassée, les fichiers sont supprimés en commençant par le plus ancien jusqu'à ce que la taille totale soit dans la limite. 0
désactive cette fonctionnalité.
Si l'objet journal est un objet Log qui prend en charge les catégories, cela peut être utilisé pour filtrer une liste arborescente de catégories. Lorsque le tableau n'est pas vide, cette fonctionnalité est active. Par exemple, [*default,ModuleA,ModuleB.SystemC]
signifie que les journaux avec la catégorie par défaut