4 votes

Journalisation C++ vers la console et le fichier journal simultanément

J'ai une classe de base, par exemple "ProcessingThread", qui a plusieurs dérivations. Chaque dérivation porte un nom spécifique, par exemple "DerivationOne", "DerivationTwo", ... Il me semble maintenant utile d'avoir une sortie formatée vers la console qui imprime quelque chose comme :

[DerivationOne]: Action X took place!
[DerivationTwo]: Action Y took place!
[DerivationTwo]: Action Z took place!

Simultanément, il devrait écrire ces informations dans un fichier journal spécifique à la dérivation. J'ai pensé à une classe qui peut être appelée de manière standard, par exemple "custom_out << "Write stuff" << std::endl ;" et qui utilise le flux unique pour générer deux flux dont l'un s'exécute dans la console avec formatage et l'autre dans un fichier journal sans formater le nom [nom] à l'avant.

Existe-t-il une méthode standard pour ce faire ? Peut-être que les enregistreurs normaux supportent déjà ce type de comportement ? Peut-être puis-je dériver d'une manière ou d'une autre d'un std::stream pour accomplir cela ? Quelle est la meilleure (ou au moins la bonne) méthode ?

1voto

Demosthenes Points 1404

Je pense que c'est une excellente question.

Vous considérez en fait, à mon avis, deux choses différentes :

  1. Reformatage de la sortie (préfixage) par le flux personnalisé
  2. Redirection vers plusieurs flux

Je ne ferais pas les deux dans une même classe. J'écrirais des cours pour chaque caractéristique.

Pour la première, j'utiliserais une classe simple, je passerais un std::ostream et une chaîne de caractères (pour le préfixe, bien qu'il soit probablement possible de généraliser plus que le préfixe ici) au constructeur, puis surchargez operator<< ;

Pour la seconde, j'écrirais une classe prenant probablement deux itérateurs (comme par exemple std::sort takes) pour la liste des flux, et encore une fois, surcharger `opérateur<<'.

Pour votre classe qui fait les deux, je passerais alors un objet de la seconde classe au constructeur de la première.

1voto

Jive Dadson Points 3563

Voici un kit de démarrage pour l'idée dont j'ai discuté dans les commentaires. Vous devrez décider de ce qu'il faut faire en cas d'erreur d'écriture sur le fichier disque - renvoyer false, lancer une exception, ou autre. Je l'ai modifié pour qu'il renvoie true/false. True signifie qu'il n'y a pas d'erreur. Travail en cours.

#include <iostream>
#include <mutex>
#include <string>
#include <fstream>
#include <string_view>
#include <iomanip>

namespace dj {

    inline bool print(std::ostream& out) {
        return !!(out << std::endl);
    }

    template<typename T>
    bool print(std::ostream& out, T&& value)
    {
        return !!(out << std::forward<T>(value) << std::endl);
    }

    template<typename First, typename ... Rest>
    bool print(std::ostream& out, First&& first, Rest&& ... rest)
    {
        return !!(out << std::forward<First>(first)) && print(out, std::forward<Rest>(rest)...);
    }

    inline std::mutex logger_mtx;

    class log_stream {
    public:
        log_stream(std::string_view str, std::ostream& ifile)
            : name(str)
            , file(ifile)
        {
            std::string s{ "[" };
            name = s + name + "] ";
        }

        template <typename... Args>
        bool operator() (Args&&... args) {
            bool OK = print(file, std::forward<Args>(args)...);
            {
                std::lock_guard<std::mutex> lck(logger_mtx);
                print(std::cout, name, std::forward<Args>(args)...);
                if (!OK) {
                    print(std::cout, name, "-- Error writing to log file. --");
                }
            }
            return OK;
        }

    private:
        std::string name;
        std::ostream& file;
    };

}
int main()
{
    std::ofstream outfile("DerivationOne.log.txt");
    dj::log_stream log("DerivationOne", outfile);

    std::ofstream outfile2; // ERROR. File not opened
    dj::log_stream log2("DerivationTwo", outfile2);

    log("Life. See ", 42, ", meaning of.");
    bool OK = 
      log2("log", std::setw(4), 2.01, " That's all, folks. -", 30, '-');
    std::cout << (OK ? "OK" : "So not OK") << std::endl;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X