123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- <chapter xml:id="logger.h">
- <title><tt>__vic/logger.h</tt></title>
- <chapter xml:id="logger">
- <title><tt>logger</tt></title>
- <code-block lang="C++"><![CDATA[
- class logger : private non_copyable
- {
- public:
- enum class severity : unsigned char
- {
- trace,
- debug,
- info,
- notice,
- warning,
- error,
- fatal
- };
- using severity_t = severity; // use this alias as a type name
- struct output
- {
- virtual void publish_record(severity_t , const char * , size_t ) = 0;
- protected:
- ~output() = default;
- };
- class settings_t
- {
- struct output &output() const;
- severity_t level() const;
- };
- class record;
- explicit logger(output &out, severity_t = severity::info);
- explicit logger(settings_t s);
- ~logger();
- severity_t level() const;
- void level(severity_t new_level);
- settings_t settings() const;
- output &reset_output(output &out);
- output &get_output();
- const output &get_output() const;
- static constexpr size_t min_buffer_size = ...;
- void shrink_buffer(size_t limit);
- void message(severity_t severity, const char *msg, size_t msg_len);
- #if __cpp_lib_string_view // C++17
- void message(severity_t severity, std::string_view msg);
- void trace(std::string_view msg);
- void debug(std::string_view msg);
- void info(std::string_view msg);
- void notice(std::string_view msg);
- void warning(std::string_view msg);
- void error(std::string_view msg);
- void fatal(std::string_view msg);
- #else // until C++17
- void message(severity_t severity, const char *msg);
- void message(severity_t severity, const std::string &msg);
- void trace(const char *msg);
- void debug(const char *msg);
- void info(const char *msg);
- void notice(const char *msg);
- void warning(const char *msg);
- void error(const char *msg);
- void fatal(const char *msg);
- void trace(const std::string &msg);
- void debug(const std::string &msg);
- void info(const std::string &msg);
- void notice(const std::string &msg);
- void warning(const std::string &msg);
- void error(const std::string &msg);
- void fatal(const std::string &msg);
- #endif
- #if __cpp_lib_format >= 202106L // C++20 + P2508
- template<class... Args>
- void format(severity_t s,
- std::format_string<Args...> fmt, Args&&... args);
- template<class Arg1, class... Args>
- void trace(std::format_string<Arg1,Args...> fmt,
- Arg1 &&arg1, Args&&... args);
- template<class Arg1, class... Args>
- void debug(std::format_string<Arg1,Args...> fmt,
- Arg1 &&arg1, Args&&... args);
- template<class Arg1, class... Args>
- void info(std::format_string<Arg1,Args...> fmt,
- Arg1 &&arg1, Args&&... args);
- template<class Arg1, class... Args>
- void notice(std::format_string<Arg1,Args...> fmt,
- Arg1 &&arg1, Args&&... args);
- template<class Arg1, class... Args>
- void warning(std::format_string<Arg1,Args...> fmt,
- Arg1 &&arg1, Args&&... args);
- template<class Arg1, class... Args>
- void error(std::format_string<Arg1,Args...> fmt,
- Arg1 &&arg1, Args&&... args);
- template<class Arg1, class... Args>
- void fatal(std::format_string<Arg1,Args...> fmt,
- Arg1 &&arg1, Args&&... args);
- #endif
- record trace();
- record debug();
- record info();
- record notice();
- record warning();
- record error();
- record fatal();
- bool trace_visible() const;
- bool debug_visible() const;
- bool info_visible() const;
- bool notice_visible() const;
- bool warning_visible() const;
- bool error_visible() const;
- bool fatal_visible() const;
- };
- class logger::record
- {
- public:
- record(logger &log, severity_t sev);
- ~record();
- record append(const char *str, size_t str_len);
- template<class T> record operator<<(const T &v);
- };
- template<class T> struct log_value
- {
- static void to_text(const T &v, std::string &s);
- };
- const char *to_string(logger::severity_t s);
- #if __cpp_lib_string_view // C++17
- constexpr std::string_view to_string_view(logger::severity s);
- #endif
- ]]></code-block>
- <p>Logging front-end. Provides building of the
- log records using operator <tt><<</tt>, like <tt>iostream</tt>. Each log
- record has the associated severity. The logger itself can filter out the
- records by severity. There are 7 predefined levels of the severity (in
- ascending order):</p>
- <list style="numbered">
- <item>TRACE - detailed debug,</item>
- <item>DEBUG - debug,</item>
- <item>INFO - informational,</item>
- <item>NOTICE - normal but significant event,</item>
- <item>WARNING - insignificant error or suspicious situation,</item>
- <item>ERROR - severe error but the application can continue,</item>
- <item>FATAL - critical unrecoverable error, the application can't
- continue.</item>
- </list>
- <p>INFO is the default logging level but any other can be chosen. If severity
- of the log message (record) is below the logging level, it will be ignored and
- will not be published in the log's output.</p>
- <p>For creation of messages with the required severity, the set of functions
- with the same name as the severity is available. For instance <tt>info()</tt>
- for INFO messages. Or alternatively the universal function <tt>message()</tt>
- can be used, in which the severity is the argument. Usually the specific
- functions should be used.</p>
- <p>There are two ways of logging. The first is plain and common:</p>
- <code-block lang="C++">
- log.trace("Trace message");
- log.debug("Debug message");
- log.info("Info message");
- log.notice("Notice");
- log.warning("Warning");
- log.error("Recoverable error");
- log.fatal("Fatal error");
- </code-block>
- <p>The second is slightly more complex but provides more capabilities:</p>
- <code-block lang="C++"><![CDATA[
- log.error() << "Cannot open file " << filename << '!';
- log.warning() << "Loop iteration no " << i;
- ]]></code-block>
- <p>The call without parameters creates the object of type
- <tt>logger::record</tt> with the corresponding severity. Futher, the message
- is formed using operators <tt><<</tt>. The message will be output to the
- log at the end of the full-expression (term from the Standard).</p>
- <p>If the message can't or shouldn't be formed with a single expression,
- the named object of type <tt>logger::record</tt> has to be created, and parts
- of the message have to be written to it. The resulting message will be output
- to the log by it's destructor:</p>
- <code-block lang="C++"><![CDATA[
- {
- logger::record rec = log.info(); // Begin new record
- rec << "List elements: ";
- for(auto el : list) rec << el << ", ";
- // Constructed record will be printed to the log when the block exits
- }
- ]]></code-block>
- <note>In order to optimize the performace, use the plain functional notation
- when the complete message is available:</note>
- <code-block lang="C++"><![CDATA[
- log.info("Message");
- // but not
- log.info() << "Message";
- ]]></code-block>
- <p>Output of the records with the severities DEBUG and TRACE is usually disabled.
- Such records will not be published in the log but the program will waste
- time to format it. Therefore before creating any debug message using operator
- <tt><<</tt> one should check if debug is enabled using
- <tt>debug_visible()</tt> or <tt>trace_visible()</tt> call:</p>
- <code-block lang="C++"><![CDATA[
- if(log.debug_visible())
- log.debug() << ...; // build the message
- ]]></code-block>
- <p>This advice doesn't cover plain calls <tt>debug(msg)</tt> and
- <tt>trace(msg)</tt>, which have a prepared message already and don't perform any
- formatting.</p>
- <p>To use <tt>logger</tt> one has to implement abstract base class
- <tt>logger::output</tt> (override <tt>publish_record()</tt>). The implementation
- has to output the passed record somewhere, e.g. to file, terminal or DB. The
- output specified during <tt>logger</tt> construction can be replaced later
- using <tt>reset_output()</tt>.</p>
- <section><title>Class members</title>
- <synopsis>
- <prototype>severity::trace</prototype>
- <prototype>severity::debug</prototype>
- <prototype>severity::info</prototype>
- <prototype>severity::notice</prototype>
- <prototype>severity::warning</prototype>
- <prototype>severity::error</prototype>
- <prototype>severity::fatal</prototype>
- <p>Severity constants. Use this form for both C++11 and C++98 mode.</p>
- </synopsis>
- <synopsis>
- <prototype>typename severity_t</prototype>
- <p>Use this identifier as a type name if your code has to be C++98-compatible.
- Since C++11 it is just a synonym for <tt>severity</tt>.</p>
- </synopsis>
- <synopsis>
- <prototype>class output</prototype>
- <p>Logging back-end interface.</p>
- </synopsis>
- <synopsis>
- <prototype>void output::publish_record(severity_t sev, const char *buf, size_t buf_len)</prototype>
- <p>The implementaion of this pure virtual function has to output the content of
- <tt>buf</tt> to the log as one record. <tt>buf_len</tt> is the length of
- <tt>buf</tt>. The function is always called with <tt>sev >= level()</tt>.
- The implementation can rely on it.</p>
- </synopsis>
- <synopsis>
- <prototype>class settings_t</prototype>
- <p>Keeps the logger settings: logging level and reference to output
- (<tt>level()</tt> + <tt>get_output()</tt>).</p>
- </synopsis>
- <synopsis>
- <prototype>explicit logger(output &out, severity_t level = severity::info)</prototype>
- <p>Creates logger with the specified output and logging level. The output object
- must outlive the logger object!</p>
- <postcondition><tt>this->level() == level && &this->get_output() == &out</tt></postcondition>
- </synopsis>
- <synopsis>
- <prototype>explicit logger(settings_t s)</prototype>
- <p>Creates logger using the specified settings.</p>
- </synopsis>
- <synopsis>
- <prototype>severity_t level() const</prototype>
- <p>Returns current logging level.</p>
- </synopsis>
- <synopsis>
- <prototype>void level(severity_t new_level)</prototype>
- <p>Sets the logging level.</p>
- <postcondition><tt>level() == new_level</tt></postcondition>
- </synopsis>
- <synopsis>
- <prototype>settings_t settings() const</prototype>
- <p>Returns current logging settings.</p>
- </synopsis>
- <synopsis>
- <prototype>output &reset_output(output &out)</prototype>
- <p>Sets new output and returns the previous one.</p>
- <postcondition><tt>&get_output() == &out</tt></postcondition>
- </synopsis>
- <synopsis>
- <prototype>output &get_output()</prototype>
- <prototype>const output &get_output() const</prototype>
- <p>Returns reference to the current logging output.</p>
- </synopsis>
- <synopsis>
- <prototype>static constexpr size_t min_buffer_size</prototype>
- <p>Minimal size of the internal buffer in bytes.</p>
- </synopsis>
- <synopsis>
- <prototype>void shrink_buffer(size_t limit)</prototype>
- <p>Sets the internal buffer size to <tt>min_buffer_size</tt> if it is more than
- <tt>limit</tt> bytes. Allows to restrict the buffer growth when long records
- are formed.</p>
- </synopsis>
- <synopsis>
- <prototype>void message(severity_t severity, const char *msg, size_t msg_len)</prototype>
- <prototype>void message(severity_t severity, std::string_view msg) <badge>C++17</badge></prototype>
- <prototype>void message(severity_t severity, const char *msg) <badge>until C++17</badge></prototype>
- <prototype>void message(severity_t severity, const std::string &msg) <badge>until C++17</badge></prototype>
- <p>Writes the message with the specified severity.</p>
- </synopsis>
- <synopsis>
- <prototype>void trace(std::string_view msg) <badge>C++17</badge></prototype>
- <prototype>void trace(const char *msg) <badge>until C++17</badge></prototype>
- <prototype>void trace(const std::string &msg) <badge>until C++17</badge></prototype>
- <prototype>void debug(std::string_view msg) <badge>C++17</badge></prototype>
- <prototype>void debug(const char *msg) <badge>until C++17</badge></prototype>
- <prototype>void debug(const std::string &msg) <badge>until C++17</badge></prototype>
- <prototype>void info(std::string_view msg) <badge>C++17</badge></prototype>
- <prototype>void info(const char *msg) <badge>until C++17</badge></prototype>
- <prototype>void info(const std::string &msg) <badge>until C++17</badge></prototype>
- <prototype>void notice(std::string_view msg) <badge>C++17</badge></prototype>
- <prototype>void notice(const char *msg) <badge>until C++17</badge></prototype>
- <prototype>void notice(const std::string &msg) <badge>until C++17</badge></prototype>
- <prototype>void warning(std::string_view msg) <badge>C++17</badge></prototype>
- <prototype>void warning(const char *msg) <badge>until C++17</badge></prototype>
- <prototype>void warning(const std::string &msg) <badge>until C++17</badge></prototype>
- <prototype>void error(std::string_view msg) <badge>C++17</badge></prototype>
- <prototype>void error(const char *msg) <badge>until C++17</badge></prototype>
- <prototype>void error(const std::string &msg) <badge>until C++17</badge></prototype>
- <prototype>void fatal(std::string_view msg) <badge>C++17</badge></prototype>
- <prototype>void fatal(const char *msg) <badge>until C++17</badge></prototype>
- <prototype>void fatal(const std::string &msg) <badge>until C++17</badge></prototype>
- <p>Writes the message with the corresponding severity.</p>
- </synopsis>
- <synopsis>
- <prototype><![CDATA[template<class... Args>
- void format(severity_t s,
- std::format_string<Args...> fmt, Args&&... args)]]> <badge>C++20</badge></prototype>
- <p>Formats the message using the specified format string and arguments (like
- <tt>std::format</tt> does) then writes it with the specified severity.</p>
- </synopsis>
- <synopsis>
- <prototype><![CDATA[template<class Arg1, class... Args>
- void trace(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
- <prototype><![CDATA[template<class Arg1, class... Args>
- void debug(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
- <prototype><![CDATA[template<class Arg1, class... Args>
- void info(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
- <prototype><![CDATA[template<class Arg1, class... Args>
- void notice(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
- <prototype><![CDATA[template<class Arg1, class... Args>
- void warning(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
- <prototype><![CDATA[template<class Arg1, class... Args>
- void error(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
- <prototype><![CDATA[template<class Arg1, class... Args>
- void fatal(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
- <p>Formats the message using the specified format string and arguments (like
- <tt>std::format</tt> does) then writes it with the corresponding severity.</p>
- </synopsis>
- <synopsis>
- <prototype>logger::record trace()</prototype>
- <prototype>logger::record debug()</prototype>
- <prototype>logger::record info()</prototype>
- <prototype>logger::record notice()</prototype>
- <prototype>logger::record warning()</prototype>
- <prototype>logger::record error()</prototype>
- <prototype>logger::record fatal()</prototype>
- <p>Creates new record with the corresponding severity. Message parts can be
- added using operators <tt><<</tt>.</p>
- </synopsis>
- <synopsis>
- <prototype>bool trace_visible() const</prototype>
- <prototype>bool debug_visible() const</prototype>
- <prototype>bool info_visible() const</prototype>
- <prototype>bool notice_visible() const</prototype>
- <prototype>bool warning_visible() const</prototype>
- <prototype>bool error_visible() const</prototype>
- <prototype>bool fatal_visible() const</prototype>
- <p>Returns <tt>true</tt> if a record with the corresponding severity will be
- published. Usage of this functions enables to avoid formatting of the
- messages which eventually will not be published.</p>
- </synopsis>
- <synopsis>
- <prototype>record::record(logger &log, severity_t sev)</prototype>
- <p>Creates the log record with the specified severity. Usually the <tt>logger</tt>'s
- functions like <tt>info()</tt> without parameters should be used instead.</p>
- </synopsis>
- <synopsis>
- <prototype>record::~record()</prototype>
- <p>Outputs the constructed record to the log.</p>
- </synopsis>
- <synopsis>
- <prototype>record record::append(const char *str, size_t str_len)</prototype>
- <p>Appends the string to the message.</p>
- </synopsis>
- <synopsis>
- <prototype>template<class T> record record::operator<<(const T &v)</prototype>
- <p>The set of inserters for various data types. The specified value is
- converted to text using <tt>log_value<T>::to_text()</tt> call.</p>
- </synopsis>
- </section>
- <section><title>Value-to-text converter</title>
- <synopsis>
- <prototype>template<class T> void log_value<T>::to_text(const T &v, std::string &s)</prototype>
- <p>Converts a value of type <tt>T</tt> to text using unqualified
- <xref to="to_text_append"/> call.</p>
- <note>This function (class with static function, to be precise) can be
- specialized for your <tt>T</tt>. But usually you just define
- <tt>to_text_append(const T &, std::string &)</tt>.</note>
- </synopsis>
- </section>
- <section><title>Free functions</title>
- <synopsis>
- <prototype>const char *to_string(logger::severity_t s)</prototype>
- <prototype>constexpr std::string_view to_string_view(logger::severity s) <badge>C++17</badge></prototype>
- <p>Returns text label for the specified severity that can be printed to the
- log. For example, <tt>"DEBUG"</tt> will be returned for
- <tt>severity::debug</tt>.</p>
- </synopsis>
- </section>
- <section><title>Example</title>
- <code-block lang="C++"><![CDATA[
- /////////////////////////////////////////////////////////////////////////////
- // Output messages to std::clog with the severity label
- class coutput : public __vic::logger::output
- {
- public:
- void publish_record(__vic::logger::severity_t s,
- const char *rec, size_t rec_n)
- {
- std::clog << to_string(s) << ": ";
- std::clog.write(rec, rec_n) << std::endl;
- }
- };
- /////////////////////////////////////////////////////////////////////////////
- int main()
- {
- coutput log_output:
- __vic::logger log(log_output, __vic::logger::severity::debug);
- log.info("Application is started");
- for(int i = 0; i < 5; i++)
- log.debug() << "Loop i = " << i;
- log.warning("Application end");
- }
- ]]></code-block>
- <p>Output:</p>
- <tty>
- INFO: Application is started
- DEBUG: Loop i = 0
- DEBUG: Loop i = 1
- DEBUG: Loop i = 2
- DEBUG: Loop i = 3
- DEBUG: Loop i = 4
- WARNING: Application end
- </tty>
- </section>
- </chapter>
- </chapter>
|