dlib C++ Library - logger_kernel_1.h

    // Copyright (C) 2006  Davis E. King (davisking@users.sourceforge.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_LOGGER_KERNEl_1_
    #define DLIB_LOGGER_KERNEl_1_

    #include "../threads.h"
    #include "../misc_api.h"
    #include "../set.h"
    #include "logger_kernel_abstract.h"
    #include <limits>
    #include <cstring>
    #include "../algs.h"
    #include "../assert.h"
    #include "../uintn.h"
    #include "../map.h"
    #include "../smart_pointers.h"

    namespace dlib
    {

    // ----------------------------------------------------------------------------------------

        class log_level
        {
        public:
            log_level(
                int priority_,
                const char* name_
            ) :
                priority(priority_)
            {
                strncpy(name,name_,19);
                name[19] = '\0';
            }

            int priority;
            char name[20];
        };

        const log_level LALL  (std::numeric_limits<int>::min(),"ALL");
        const log_level LNONE (std::numeric_limits<int>::max(),"NONE");
        const log_level LTRACE(-100,"TRACE");
        const log_level LDEBUG(0  ,"DEBUG");
        const log_level LINFO (100,"INFO ");
        const log_level LWARN (200,"WARN ");
        const log_level LERROR(300,"ERROR");
        const log_level LFATAL(400,"FATAL");

    // ----------------------------------------------------------------------------------------

        void set_all_logging_output_streams (
            std::ostream& out
        );

        void set_all_logging_levels (
            const log_level& new_level
        );

    // ----------------------------------------------------------------------------------------

        void print_default_logger_header (
            std::ostream& out,
            const std::string& logger_name,
            const log_level& l,
            const uint64 thread_id
        );

    // ----------------------------------------------------------------------------------------

        class logger
        {
            /*!
                INITIAL VALUE
                    - print_header == print_default_logger_header
                    - out.rdbuf() == std::cout.rdbuf()
                    - cur_level == LERROR
                    - auto_flush_enabled == true

                CONVENTION
                    - print_header == logger_header()
                    - out.rdbuf() == output_streambuf()
                    - cur_level == level()
                    - logger_name == name()
                    - auto_flush_enabled == auto_flush()

                    - logger::gd::loggers == a set containing all currently existing loggers.
                    - logger::gd::m == the mutex used to lock everything in the logger
                    - logger::gd::thread_names == a map of thread ids to thread names.
                    - logger::gd::next_thread_name == the next thread name that will be given out
                      to a thread when we find that it isn't already in thread_names.
            !*/

            class logger_stream
            {
                /*!
                    INITIAL VALUE
                        - been_used == false

                    CONVENTION
                        - enabled == is_enabled()
                        - if (been_used) then
                            - logger::gd::m is locked
                            - someone has used the << operator to write something to the
                              output stream.
                !*/
            public:
                logger_stream (
                    const log_level& l_,
                    logger& log_
                ) :
                    l(l_),
                    log(log_),
                    been_used(false),
                    enabled (l.priority >= log.cur_level.priority)
                {}

                inline ~logger_stream(
                )
                {
                    if (!been_used)
                    {
                        return;
                    }
                    else
                    {
                        print_end_of_line();
                    }
                }

                bool is_enabled (
                ) const { return enabled; }

                template <typename T>
                inline logger_stream& operator << (
                    const T& item
                )
                {
                    if (!enabled)
                    {
                        return *this;
                    }
                    else
                    {
                        print_header_and_stuff();
                        log.out << item;
                        return *this;
                    }
                }

            private:

                void print_header_and_stuff (
                );
                /*!
                    ensures
                        - if (!been_used) then
                            - prints the logger header
                            - locks log.gd.m
                            - #been_used == true
                !*/

                void print_end_of_line (
                );
                /*!
                    ensures
                        - prints a newline to log.out
                        - unlocks log.gd.m
                !*/

                const log_level& l;
                logger& log;
                bool been_used;
                const bool enabled;
            };

            friend class logger_stream;
        public:

            logger (
                const char* name_
            );

            virtual ~logger (
            );

            const std::string& name (
            ) const { return logger_name; }

            logger_stream operator << (
                const log_level& l
            ) const { return logger_stream(l,const_cast<logger&>(*this)); }

            bool is_child_of (
                const logger& log
            ) const
            {
                return (name().find(log.name() + ".") == 0) || (log.name() == name());
            }

            const log_level level (
            ) const
            {
                auto_mutex M(gd.m);
                return log_level(cur_level);
            };

            void set_level (
                const log_level& new_level
            )
            {
                auto_mutex M(gd.m);
                gd.loggers.reset();
                while (gd.loggers.move_next())
                {
                    if (gd.loggers.element()->is_child_of(*this))
                        gd.loggers.element()->cur_level = new_level;
                }

                gd.set_level(logger_name, new_level);
            }

            bool auto_flush (
            ) const
            {
                auto_mutex M(gd.m);
                return auto_flush_enabled;
            };

            void set_auto_flush (
                bool enabled
            )
            {
                auto_mutex M(gd.m);
                gd.loggers.reset();
                while (gd.loggers.move_next())
                {
                    if (gd.loggers.element()->is_child_of(*this))
                        gd.loggers.element()->auto_flush_enabled = enabled;
                }

                gd.set_auto_flush(logger_name, enabled);
            }

            std::streambuf* output_streambuf (
            )
            {
                auto_mutex M(gd.m);
                return out.rdbuf();
            }

            void set_output_stream (
                std::ostream& out_
            )
            {
                auto_mutex M(gd.m);
                gd.loggers.reset();
                while (gd.loggers.move_next())
                {
                    if (gd.loggers.element()->is_child_of(*this))
                        gd.loggers.element()->out.rdbuf(out_.rdbuf());
                }

                gd.set_output_stream(logger_name, out_);
            }

            typedef void (*print_header_type)(
                std::ostream& out,
                const std::string& logger_name,
                const log_level& l,
                const uint64 thread_id
            );

            print_header_type logger_header (
            ) const { return print_header; }

            void set_logger_header (
                print_header_type ph
            )
            {
                auto_mutex M(gd.m);
                gd.loggers.reset();
                while (gd.loggers.move_next())
                {
                    if (gd.loggers.element()->is_child_of(*this))
                        gd.loggers.element()->print_header = ph;
                }

                gd.set_logger_header(logger_name, ph);
            }

        private:

            struct global_data
            {
                rmutex m;
                set<logger*>::kernel_1b loggers;
                map<thread_id_type,uint64>::kernel_1b thread_names;
                uint64 next_thread_name;

                global_data (
                );

                ~global_data(
                );

                uint64 get_thread_name (
                );
                /*!
                    requires
                        - m is locked
                    ensures
                        - returns a unique id for the calling thread.  also makes the number
                          small and nice unlike what you get from get_thread_id()
                !*/

                void thread_end_handler (
                );
                /*!
                    ensures
                        - removes the terminated thread from thread_names
                !*/

                struct level_container
                {
                    level_container ();

                    log_level val;
                    map<std::string,scoped_ptr<level_container> >::kernel_1b_c table;
                } level_table;

                const log_level level (
                    const std::string& name
                ) const;
                /*!
                    ensures
                        - returns the level loggers with the given name are supposed
                          to have
                !*/

                void set_level (
                    const std::string& name,
                    const log_level& new_level
                );
                /*!
                    ensures
                        - for all children C of name:
                            - #level(C) == new_level
                        - if name == "" then
                            - for all loggers L:
                                - #level(L) == new_level
                !*/

                struct auto_flush_container
                {
                    bool val;
                    map<std::string,scoped_ptr<auto_flush_container> >::kernel_1b_c table;
                } auto_flush_table;

                bool auto_flush (
                    const std::string& name
                ) const;
                /*!
                    ensures
                        - returns the auto_flush value loggers with the given name are supposed
                          to have
                !*/

                void set_auto_flush (
                    const std::string& name,
                    bool enabled
                );
                /*!
                    ensures
                        - for all children C of name:
                            - #auto_flush_enabled(C) == enabled
                        - if name == "" then
                            - for all loggers L:
                                - #auto_flush_enabled(L) == enabled
                !*/

                struct output_streambuf_container
                {
                    std::streambuf* val;
                    map<std::string,scoped_ptr<output_streambuf_container> >::kernel_1b_c table;
                } streambuf_table;

                std::streambuf* output_streambuf (
                    const std::string& name
                );
                /*!
                    ensures
                        - returns the streambuf loggers with the given name are supposed
                          to have
                !*/

                void set_output_stream (
                    const std::string& name,
                    std::ostream& out_
                );
                /*!
                    ensures
                        - for all children C of name:
                            - #output_streambuf(C) == out_.rdbuf()
                        - if name == "" then
                            - for all loggers L:
                                - #output_streambuf(L) == out_.rdbuf()
                !*/

                struct logger_header_container
                {
                    print_header_type val;
                    map<std::string,scoped_ptr<logger_header_container> >::kernel_1b_c table;
                } header_table;

                print_header_type logger_header (
                    const std::string& name
                );
                /*!
                    ensures
                        - returns the header function loggers with the given name are supposed
                          to have
                !*/

                void set_logger_header (
                    const std::string& name,
                    print_header_type ph
                );
                /*!
                    ensures
                        - for all children C of name:
                            - #logger_header(C) == ph
                        - if name == "" then
                            - for all loggers L:
                                - #logger_header(L) == ph
                !*/

            }; // end of struct global_data

            static global_data& get_global_data();

            friend void set_all_logging_levels (
                const log_level& new_level
            );

            friend void set_all_logging_output_streams (
                std::ostream& out
            );

            global_data& gd;

            const std::string logger_name;

            print_header_type print_header;
            bool auto_flush_enabled;
            std::ostream out;
            log_level cur_level;

            // restricted functions
            logger(const logger&);        // copy constructor
            logger& operator=(const logger&);    // assignment operator

        };

    // ----------------------------------------------------------------------------------------

    }

    #ifdef NO_MAKEFILE
    #include "logger_kernel_1.cpp"
    #endif

    #endif // DLIB_LOGGER_KERNEl_1_