2016-08-31 1 views
4

J'utilise la fonction add_file_log() pour initialiser un récepteur de consignation qui stocke les enregistrements de journal dans un fichier texte. Quand je définis plusieurs puits, j'ai observé:Boost.log: Comment empêcher la sortie sera dupliquée à tous les flux ajoutés quand il utilise la fonction add_file_log()?

  • un fichier est créé pour chaque puits.
  • La sortie est copiée dans tous les fichiers.

Ceci est mon enregistreur:

class logger 
{ 
public: 
    logger(const logger&) =delete; 
    logger(logger&&) =delete; 
    logger& operator=(const logger&) =delete; 
    logger& operator=(logger&&) =delete; 

    static logger& get_instance(
    const std::string& file, 
    bool console 
) 
    { 
     boost::log::register_simple_formatter_factory< 
                boost::log::trivial::severity_level, 
                char 
                >("Severity"); 

     std::string the_format = "[%TimeStamp%] (%LineID%) [%Severity%]: %Message%"; 

     if(!file.empty()) { 
     boost::log::add_file_log(
      boost::log::keywords::file_name = file + "_%N.log", 
      boost::log::keywords::rotation_size = 10 * 1024 * 1024, 
      boost::log::keywords::time_based_rotation = 
      boost::log::sinks::file::rotation_at_time_point(0, 0, 0), 
      boost::log::keywords::auto_flush = true, 
      //boost::log::keywords::open_mode = (std::ios::out | std::ios::app), 
      boost::log::keywords::format = the_format 
     ); 
     } 

     boost::log::add_common_attributes(); 
     static logger instance{ the_format, console }; 
     return instance; 
    } 

    void log(
    const std::string& msg 
) 
    { 
     BOOST_LOG_SEV (m_log_, boost::log::trivial::info) << msg; 
    } 

private: 
    boost::log::sources::severity_logger< 
             boost::log::trivial::severity_level 
             > m_log_; 

    logger(
    const std::string& format, 
    bool console 
) 
    { 
     if(console) { 
     boost::log::add_console_log(
      std::clog, 
      boost::log::keywords::format = format 
     ); 
     } 
    } 
}; // logger 

C'est ma fonction main():

void test(
    const std::string& file 
) 
{ 
    logger& lg1 = logger::get_instance(file, false); 
    lg1.log("Hello"); 
    lg1.log("World"); 
    lg1.log("Bye"); 
} // test 

int main() 
{ 
    unsigned char result = EXIT_SUCCESS; 

    try 
    { 
    std::string file1 = "a.txt", 
       file2 = "b.txt"; 
    logger& lg = logger::get_instance(file1, false); 

    for(int i = 1; i<=10; i++) { 
     lg.log(std::to_string(i)); 
     if(i == 5) { 
     test(file2); 
     } 
    } 
    } 
    catch (std::exception& e) 
    { 
    std::cerr << "Error: " << e.what() << std::endl; 
    result = EXIT_FAILURE; 
    } 
    return result; 
} 

Après avoir exécuté l'exemple, les fichiers contiennent:

a.txt_0.log

[2016-Aug-31 11:49:48.584353] (1) [info]: 1 
[2016-Aug-31 11:49:48.585376] (2) [info]: 2 
[2016-Aug-31 11:49:48.585418] (3) [info]: 3 
[2016-Aug-31 11:49:48.585442] (4) [info]: 4 
[2016-Aug-31 11:49:48.585462] (5) [info]: 5 
[2016-Aug-31 11:49:48.585505] (6) [info]: Hello <-- 
[2016-Aug-31 11:49:48.585610] (7) [info]: World <-- Generated by second logger 
[2016-Aug-31 11:49:48.585672] (8) [info]: Bye <-- 
[2016-Aug-31 11:49:48.585709] (9) [info]: 6 
[2016-Aug-31 11:49:48.585744] (10) [info]: 7 
[2016-Aug-31 11:49:48.585777] (11) [info]: 8 
[2016-Aug-31 11:49:48.585813] (12) [info]: 9 
[2016-Aug-31 11:49:48.585842] (13) [info]: 10 

b.txt_0.log

[2016-Aug-31 11:49:48.585505] (6) [info]: Hello 
[2016-Aug-31 11:49:48.585610] (7) [info]: World 
[2016-Aug-31 11:49:48.585672] (8) [info]: Bye 
[2016-Aug-31 11:49:48.585709] (9) [info]: 6 <-- 
[2016-Aug-31 11:49:48.585744] (10) [info]: 7 <-- 
[2016-Aug-31 11:49:48.585777] (11) [info]: 8 <-- Generated by the first logger 
[2016-Aug-31 11:49:48.585813] (12) [info]: 9 <-- 
[2016-Aug-31 11:49:48.585842] (13) [info]: 10 <-- 

Comment puis-je éviter ce comportement? Je veux que chaque fichier ne stocke que les informations générées par son enregistreur associé.

+0

peut-être configurer les canaux corrects? (Voir http://stackoverflow.com/a/34372526/819272) – TemplateRex

Répondre

9

Vous semblez avoir un malentendu sur le fonctionnement de Boost.Log.

Il existe sources et puits. Une source prend des données, telles qu'une chaîne, et crée une entrée avec elle. L'entrée est ensuite donnée au cœur , qui le distribue à tous les puits. Les puits peuvent ensuite filtrer, formater et afficher les entrées où elles le souhaitent, par exemple stdout ou un fichier. Un exemple de source serait le severity_logger que vous utilisez. Vous pourriez être habitué au terme "logger" au lieu de "source", mais "logger" n'est pas très précis car la journalisation est un processus en plusieurs étapes.

Vous n'avez généralement pas besoin de créer plusieurs sources ("loggers"). Au lieu de cela, vous pouvez ajouter plusieurs récepteurs globaux. Dans votre cas, vous aurez besoin d'un récepteur filtré par fichier.

        +--------------+ 
          +---> | console sink | ----> stdout 
          |  +--------------+ 
          | 
+--------+  +------+ |  +--------------+ 
| source | ---> | core | ---+---> | file sink | ----> log1.txt 
+--------+  +------+ |  +--------------+ 
          | 
          |  +--------------+ 
          +---> | file sink | ----> log2.txt 
            +--------------+ 

Maintenant, vous pouvez avoir plusieurs enregistreurs, chacun avec leur propre modèle de thread, les attributs, le type de caractères, etc., mais ils seraient encore tous générer des entrées et les donner à cœur. Dans votre cas, cela ne serait pas très utile.

Obtenons les en-têtes de la route:

#include <string> 
#include <fstream> 
#include <boost/log/sinks.hpp> 
#include <boost/log/utility/setup/formatter_parser.hpp> 
#include <boost/log/sources/severity_channel_logger.hpp> 
#include <boost/log/trivial.hpp> 
#include <boost/log/utility/setup/file.hpp> 
#include <boost/log/utility/setup/common_attributes.hpp> 
#include <boost/log/utility/setup/console.hpp> 
#include <boost/log/expressions.hpp> 
#include <boost/log/attributes/scoped_attribute.hpp> 

namespace bl = boost::log; 

Commençons:

BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string); 

Une entrée de journal a attributs qui peut être réglé à chaque fois que quelque chose est connecté. Ces attributs sont généralement utilisés dans la mise en forme (par exemple "[%TimeStamp%] [%Message%]"), mais nous allons ajouter un nouvel attribut pour permettre la différenciation entre les différents fichiers. J'ai appelé l'attribut "Tag".

using logger_type = 
    bl::sources::severity_logger<bl::trivial::severity_level>; 
static logger_type g_logger; 

const std::string g_format = 
    "[%TimeStamp%] (%LineID%) [%Severity%] [%Tag%]: %Message%"; 

Maintenant, dans cet exemple, l'enregistreur d'amplification proprement dit est un objet global. Vous souhaiterez peut-être restreindre sa portée et la transmettre à vos propres objets logger. J'ai également fait du format une constante globale. YMMV.

C'est la logger classe:

class logger 
{ 
public: 
    logger(std::string file); 
     : tag_(file) 
    { 
     using backend_type = bl::sinks::text_file_backend; 
     using sink_type = bl::sinks::synchronous_sink<backend_type>; 

     auto backend = boost::make_shared<backend_type>(
      bl::keywords::file_name = file + "_%N.log", 
      bl::keywords::rotation_size = 10 * 1024 * 1024, 
      bl::keywords::time_based_rotation = 
       bl::sinks::file::rotation_at_time_point(0, 0, 0), 
      bl::keywords::auto_flush = true); 

     auto sink = boost::make_shared<sink_type>(backend); 
     sink->set_formatter(bl::parse_formatter(g_format)); 
     sink->set_filter(tag_attr == tag_); 

     bl::core::get()->add_sink(sink); 
    } 

    void log(const std::string& s) 
    { 
     BOOST_LOG_SCOPED_THREAD_TAG("Tag", tag_); 
     BOOST_LOG_SEV(g_logger, bl::trivial::info) << s; 
    } 

private: 
    const std::string tag_; 
}; 

Je l'ai utilisé le nom de fichier comme une étiquette, mais il pourrait être autre chose aussi longtemps qu'il est unique. Chaque entrée de journal aura cette balise comme attribut, qui sera utilisé dans le filtre de puits.

D'abord, un text_file_backend est créé et est donné à un nouveau récepteur, qui est ensuite ajouté au noyau. C'est en fait ce qui se passe quand vous appelez add_file_log(), c'est juste une fonction d'aide. J'ai réutilisé les mêmes paramètres que vous aviez dans votre exemple (modèle de nom de fichier, rotation, etc.)

La ligne intéressante est celle-ci:

sink->set_filter(tag_attr == tag_); 

Ici, a été défini ci-dessus tag_attr comme un mot-clé d'attribut . Les mots-clés sont un peu inhabituels dans Boost.Log: ils peuvent être utilisés pour créer des expressions qui seront évaluées lors de l'exécution. Dans ce cas, le récepteur acceptera uniquement les entrées où tag_attr == tag_. Donc, quand un enregistreur enregistre quelque chose, il définit son propre tag comme un attribut, et le récepteur ignorera tout ce qui n'a pas cette étiquette. Dans log(), vous pouvez voir l'attribut "Tag" en cours de définition.

est ici main():

int main() 
{ 
    bl::register_simple_formatter_factory< 
     bl::trivial::severity_level, char>("Severity"); 

    boost::log::add_common_attributes(); 

    bl::add_console_log(
     std::clog, bl::keywords::format=g_format); 

    logger lg1("1"); 
    logger lg2("2"); 

    lg1.log("a"); 
    lg1.log("b"); 
    lg1.log("c"); 

    lg2.log("d"); 
    lg2.log("e"); 
    lg2.log("f"); 
} 

Vous verrez que j'ai déplacé les choses communes en dehors de logger car il ne fait pas vraiment là. Les entrées "a", "b" et "c" seront écrites à "1_0.txt" et "d", "e" et "f" à "2_0.txt". Les six entrées seront écrites sur la console.

 +--------------+ 
    | lg1.log("a") | 
    +--------------+ 
      | 
      v 
+-------------------------+ 
| Entry:     | 
| Timestamp: 1472660811 | 
| Message: "a"   | 
| LineID: 1   | 
| Severity: info  | 
| Tag:  "1"   | 
+-------------------------+ 
      |    
      v 
     +------+    +---------------------+ 
     | core | -----+----> | console sink  | 
     +------+  |  | file: stdout  | --> written 
         |  | filter: none  | 
         |  +---------------------+ 
         | 
         |  +---------------------+ 
         |  | file sink   | 
         +----> | file: "1_0.txt" | --> written 
         |  | filter: tag == "1" | 
         |  +---------------------+ 
         | 
         |  +---------------------+ 
         |  | file sink   | 
         +----> | file: "2_0.txt" | --> discarded 
           | filter: tag == "2" | 
           +---------------------+