LCOV - code coverage report
Current view: top level - snaplogger - private_logger.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 173 190 91.1 %
Date: 2019-08-24 13:53:02 Functions: 33 35 94.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013-2019  Made to Order Software Corp.  All Rights Reserved
       3             :  *
       4             :  * https://snapwebsites.org/project/snaplogger
       5             :  * contact@m2osw.com
       6             :  *
       7             :  * This program is free software; you can redistribute it and/or modify
       8             :  * it under the terms of the GNU General Public License as published by
       9             :  * the Free Software Foundation; either version 2 of the License, or
      10             :  * (at your option) any later version.
      11             :  *
      12             :  * This program is distributed in the hope that it will be useful,
      13             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :  * GNU General Public License for more details.
      16             :  *
      17             :  * You should have received a copy of the GNU General Public License along
      18             :  * with this program; if not, write to the Free Software Foundation, Inc.,
      19             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      20             :  */
      21             : 
      22             : /** \file
      23             :  * \brief Appenders are used to append data to somewhere.
      24             :  *
      25             :  * This file declares the base appender class.
      26             :  */
      27             : 
      28             : // self
      29             : //
      30             : #include    "snaplogger/private_logger.h"
      31             : 
      32             : #include    "snaplogger/console_appender.h"
      33             : #include    "snaplogger/exception.h"
      34             : #include    "snaplogger/file_appender.h"
      35             : #include    "snaplogger/guard.h"
      36             : #include    "snaplogger/logger.h"
      37             : #include    "snaplogger/syslog_appender.h"
      38             : 
      39             : 
      40             : // advgetopt lib
      41             : //
      42             : #include    <advgetopt/log.h>
      43             : 
      44             : 
      45             : // cppthread lib
      46             : //
      47             : #include    <cppthread/runner.h>
      48             : 
      49             : 
      50             : // last include
      51             : //
      52             : #include    <snapdev/poison.h>
      53             : 
      54             : 
      55             : 
      56             : namespace snaplogger
      57             : {
      58             : 
      59             : 
      60             : namespace
      61             : {
      62             : 
      63             : 
      64             : 
      65           0 : void getopt_logs(advgetopt::log_level_t l, std::string const & m)
      66             : {
      67           0 :     severity_t sev(severity_t::SEVERITY_ERROR);
      68           0 :     switch(l)
      69             :     {
      70             :     case advgetopt::log_level_t::debug:
      71           0 :         sev = severity_t::SEVERITY_DEBUG;
      72           0 :         break;
      73             : 
      74             :     case advgetopt::log_level_t::info:
      75           0 :         sev = severity_t::SEVERITY_INFORMATION;
      76           0 :         break;
      77             : 
      78             :     case advgetopt::log_level_t::warning:
      79           0 :         sev = severity_t::SEVERITY_WARNING;
      80           0 :         break;
      81             : 
      82             :     //case advgetopt::log_level_t::error:
      83             :     default:
      84             :         // anything else, keep SEVERITY_ERROR
      85           0 :         break;
      86             : 
      87             :     case advgetopt::log_level_t::fatal:
      88           0 :         sev = severity_t::SEVERITY_FATAL;
      89           0 :         break;
      90             : 
      91             :     }
      92             : 
      93           0 :     message msg(sev, __FILE__, __func__, __LINE__);
      94           0 :     msg << m;
      95             : 
      96             :     // this call cannot create a loop, if the creation of the logger
      97             :     // generates an advgetopt log, then the second call will generate
      98             :     // an exception (see get_instance() in snaplogger/logger.cpp)
      99             :     //
     100           0 :     logger::pointer_t lg(logger::get_instance());
     101             : 
     102           0 :     lg->log_message(msg);
     103           0 : }
     104             : 
     105             : 
     106             : 
     107             : }
     108             : // no name namespace
     109             : 
     110             : 
     111             : namespace detail
     112             : {
     113             : 
     114             : 
     115             : 
     116             : 
     117           1 : class asynchronous_logger
     118             :     : public cppthread::runner
     119             : {
     120             : public:
     121           1 :     asynchronous_logger(message_fifo_t::pointer_t fifo)
     122             :         : runner("logger asynchronous thread")
     123           2 :         , f_logger(logger::get_instance())
     124           3 :         , f_fifo(fifo)
     125             :     {
     126           1 :     }
     127             : 
     128           2 :     virtual void run()
     129             :     {
     130             :         // loop until the FIFO is marked as being done
     131             :         //
     132           1 :         for(;;)
     133             :         {
     134           3 :             message::pointer_t msg;
     135           2 :             if(!f_fifo->pop_front(msg, -1))
     136             :             {
     137           1 :                 break;
     138             :             }
     139           2 :             logger::pointer_t l(f_logger.lock());
     140           1 :             if(l != nullptr)
     141             :             {
     142           1 :                 l->process_message(*msg);
     143             :             }
     144             :         }
     145           1 :     }
     146             : 
     147             : private:
     148             :     logger::weak_pointer_t      f_logger = logger::pointer_t();
     149             :     message_fifo_t::pointer_t   f_fifo = message_fifo_t::pointer_t();
     150             : };
     151             : 
     152             : 
     153             : 
     154             : }
     155             : // detail namespace
     156             : 
     157             : 
     158             : 
     159           2 : private_logger::private_logger()
     160             : {
     161           2 :     f_normal_component = get_component(COMPONENT_NORMAL);
     162             : 
     163           2 :     advgetopt::set_log_callback(getopt_logs);
     164           2 : }
     165             : 
     166             : 
     167           6 : private_logger::~private_logger()
     168             : {
     169           2 :     delete_thread();
     170           4 : }
     171             : 
     172             : 
     173           2 : void private_logger::shutdown()
     174             : {
     175           2 :     delete_thread();
     176           2 : }
     177             : 
     178             : 
     179           8 : void private_logger::register_appender_factory(appender_factory::pointer_t factory)
     180             : {
     181           8 :     if(factory == nullptr)
     182             :     {
     183             :         throw logger_logic_error(                                       // LCOV_EXCL_LINE
     184             :                 "register_appender_factory() called with a nullptr.");  // LCOV_EXCL_LINE
     185             :     }
     186             : 
     187          16 :     guard g;
     188             : 
     189           8 :     if(f_appender_factories.find(factory->get_type()) != f_appender_factories.end())
     190             :     {
     191             :         throw duplicate_error(                                  // LCOV_EXCL_LINE
     192             :                   "trying to register appender type \""         // LCOV_EXCL_LINE
     193             :                 + factory->get_type()                           // LCOV_EXCL_LINE
     194             :                 + "\" twice won't work.");                      // LCOV_EXCL_LINE
     195             :     }
     196             : 
     197           8 :     f_appender_factories[factory->get_type()] = factory;
     198           8 : }
     199             : 
     200             : 
     201           2 : appender::pointer_t private_logger::create_appender(std::string const & type, std::string const & name)
     202             : {
     203           4 :     guard g;
     204             : 
     205           2 :     auto it(f_appender_factories.find(type));
     206           2 :     if(it != f_appender_factories.end())
     207             :     {
     208           1 :         return it->second->create(name);
     209             :     }
     210             : 
     211           1 :     return appender::pointer_t();
     212             : }
     213             : 
     214             : 
     215          23 : component::pointer_t private_logger::get_component(std::string const & name)
     216             : {
     217          46 :     guard g;
     218             : 
     219          23 :     auto it(f_components.find(name));
     220          23 :     if(it != f_components.end())
     221             :     {
     222          17 :         return it->second;
     223             :     }
     224             : 
     225          12 :     auto comp(std::make_shared<component>(name));
     226           6 :     f_components[name] = comp;
     227             : 
     228           6 :     return comp;
     229             : }
     230             : 
     231             : 
     232          15 : format::pointer_t private_logger::get_default_format()
     233             : {
     234          30 :     guard g;
     235             : 
     236          15 :     if(f_default_format == nullptr)
     237             :     {
     238           2 :         f_default_format = std::make_shared<format>(
     239             :             //"${env:name=HOME:padding='-':align=center:exact_width=6} "
     240             :             "${date} ${time} ${hostname}"
     241             :             " ${progname}[${pid}]: ${severity}:"
     242             :             " ${message:escape:max_width=1000}"
     243             :             " (in function \"${function}()\")"
     244             :             " (${basename}:${line})"
     245           1 :         );
     246             :     }
     247             : 
     248          30 :     return f_default_format;
     249             : }
     250             : 
     251             : 
     252       96407 : environment::pointer_t private_logger::create_environment()
     253             : {
     254       96407 :     pid_t const tid(cppthread::gettid());
     255             : 
     256      192814 :     guard g;
     257             : 
     258       96407 :     auto it(f_environment.find(tid));
     259       96407 :     if(it == f_environment.end())
     260             :     {
     261           2 :         auto result(std::make_shared<environment>(tid));
     262           1 :         f_environment[tid] = result;
     263           1 :         return result;
     264             :     }
     265             : 
     266       96406 :     return it->second;
     267             : }
     268             : 
     269             : 
     270             : /** \brief Add a severity.
     271             :  *
     272             :  * This function adds a severity to the private logger object.
     273             :  *
     274             :  * Remember that a severity can be given aliases so this function may
     275             :  * add quite a few entries, not just one.
     276             :  *
     277             :  * \warning
     278             :  * You should not be calling this function directly. Please see the
     279             :  * direct snaplogger::add_severity() function instead.
     280             :  *
     281             :  * \exception duplicate_error
     282             :  * The function verifies that the new severity is not a duplicate of
     283             :  * an existing system severity.
     284             :  *
     285             :  * \param[in] sev  The severity object to be added.
     286             :  */
     287          25 : void private_logger::add_severity(severity::pointer_t sev)
     288             : {
     289          50 :     guard g;
     290             : 
     291          25 :     auto it(f_severity_by_severity.find(sev->get_severity()));
     292          25 :     if(it != f_severity_by_severity.end())
     293             :     {
     294           3 :         if(it->second->is_system())
     295             :         {
     296           2 :             throw duplicate_error("a system severity cannot be replaced.");
     297             :         }
     298             :     }
     299             : 
     300          59 :     for(auto n : sev->get_all_names())
     301             :     {
     302          36 :         auto s(f_severity_by_name.find(n));
     303          36 :         if(s != f_severity_by_name.end())
     304             :         {
     305           3 :             if(s->second->is_system())
     306             :             {
     307           2 :                 throw duplicate_error("a system severity cannot be replaced.");
     308             :             }
     309             :         }
     310             :     }
     311             : 
     312          21 :     f_severity_by_severity[sev->get_severity()] = sev;
     313             : 
     314          55 :     for(auto n : sev->get_all_names())
     315             :     {
     316          34 :         f_severity_by_name[n] = sev;
     317             :     }
     318          21 : }
     319             : 
     320             : 
     321          24 : severity::pointer_t private_logger::get_severity(std::string const & name) const
     322             : {
     323          48 :     guard g;
     324             : 
     325          24 :     auto it(f_severity_by_name.find(name));
     326          24 :     if(it == f_severity_by_name.end())
     327             :     {
     328          17 :         return severity::pointer_t();
     329             :     }
     330             : 
     331           7 :     return it->second;
     332             : }
     333             : 
     334             : 
     335          16 : severity::pointer_t private_logger::get_severity(severity_t sev) const
     336             : {
     337          32 :     guard g;
     338             : 
     339          16 :     auto it(f_severity_by_severity.find(sev));
     340          16 :     if(it == f_severity_by_severity.end())
     341             :     {
     342           2 :         return severity::pointer_t();
     343             :     }
     344             : 
     345          14 :     return it->second;
     346             : }
     347             : 
     348             : 
     349          23 : void private_logger::set_diagnostic(std::string const & key, std::string const & diagnostic)
     350             : {
     351          46 :     guard g;
     352             : 
     353          23 :     f_map_diagnostics[key] = diagnostic;
     354          23 : }
     355             : 
     356             : 
     357           1 : void private_logger::unset_diagnostic(std::string const & key)
     358             : {
     359           2 :     guard g;
     360             : 
     361           1 :     auto it(f_map_diagnostics.find(key));
     362           1 :     if(it != f_map_diagnostics.end())
     363             :     {
     364           1 :         f_map_diagnostics.erase(it);
     365             :     }
     366           1 : }
     367             : 
     368             : 
     369          45 : map_diagnostics_t private_logger::get_map_diagnostics()
     370             : {
     371          90 :     guard g;
     372             : 
     373          90 :     return f_map_diagnostics;
     374             : }
     375             : 
     376             : 
     377           3 : void private_logger::push_nested_diagnostic(std::string const & diagnostic)
     378             : {
     379           6 :     guard g;
     380             : 
     381           3 :     f_nested_diagnostics.push_back(diagnostic);
     382           3 : }
     383             : 
     384             : 
     385           3 : void private_logger::pop_nested_diagnostic()
     386             : {
     387           6 :     guard g;
     388             : 
     389           3 :     f_nested_diagnostics.pop_back();
     390           3 : }
     391             : 
     392             : 
     393           8 : string_vector_t private_logger::get_nested_diagnostics() const
     394             : {
     395          16 :     guard g;
     396             : 
     397          16 :     return f_nested_diagnostics;
     398             : }
     399             : 
     400             : 
     401          57 : void private_logger::register_variable_factory(variable_factory::pointer_t factory)
     402             : {
     403         114 :     guard g;
     404             : 
     405          57 :     auto it(f_variable_factories.find(factory->get_type()));
     406          57 :     if(it != f_variable_factories.end())
     407             :     {
     408             :         throw duplicate_error(
     409             :                   "trying to add two variable factories of type \""
     410           2 :                 + factory->get_type()
     411           3 :                 + "\".");
     412             :     }
     413             : 
     414          56 :     f_variable_factories[factory->get_type()] = factory;
     415          56 : }
     416             : 
     417             : 
     418         152 : variable::pointer_t private_logger::get_variable(std::string const & type)
     419             : {
     420         304 :     guard g;
     421             : 
     422         152 :     if(f_variable_factories.empty())
     423             :     {
     424             :         throw invalid_variable("No variable factories were registered yet; you can't create a variable with type \""    // LCOV_EXCL_LINE
     425             :                              + type                                     // LCOV_EXCL_LINE
     426             :                              + "\" at this point.");                    // LCOV_EXCL_LINE
     427             :     }
     428             : 
     429         152 :     auto it(f_variable_factories.find(type));
     430         152 :     if(it == f_variable_factories.end())
     431             :     {
     432             :         // TBD: should we instead return a null var.?
     433             :         throw invalid_variable("You can't create variable with type \""
     434           2 :                              + type
     435           3 :                              + "\", no such variable type was registered.");
     436             :     }
     437             : 
     438         302 :     return it->second->create_variable();
     439             : }
     440             : 
     441             : 
     442       48800 : bool private_logger::has_functions() const
     443             : {
     444       97600 :     guard g;
     445             : 
     446       97600 :     return !f_functions.empty();
     447             : }
     448             : 
     449             : 
     450          23 : void private_logger::register_function(function::pointer_t func)
     451             : {
     452          46 :     guard g;
     453             : 
     454          23 :     auto it(f_functions.find(func->get_name()));
     455          23 :     if(it != f_functions.end())
     456             :     {
     457             :         throw duplicate_error(
     458             :                   "trying to add two functions named \""
     459           2 :                 + func->get_name()
     460           3 :                 + "\".");
     461             :     }
     462          22 :     f_functions[func->get_name()] = func;
     463          22 : }
     464             : 
     465             : 
     466          36 : function::pointer_t private_logger::get_function(std::string const & name) const
     467             : {
     468          72 :     guard g;
     469             : 
     470          36 :     auto it(f_functions.find(name));
     471          36 :     if(it != f_functions.end())
     472             :     {
     473          14 :         return it->second;
     474             :     }
     475             : 
     476          22 :     return function::pointer_t();
     477             : }
     478             : 
     479             : 
     480           1 : void private_logger::create_thread()
     481             : {
     482           2 :     guard g;
     483             : 
     484             :     try
     485             :     {
     486           1 :         f_fifo = std::make_shared<message_fifo_t>();
     487           1 :         f_asynchronous_logger = std::make_shared<detail::asynchronous_logger>(f_fifo);
     488           1 :         f_thread = std::make_shared<cppthread::thread>("asynchronous logger thread", f_asynchronous_logger.get());
     489           1 :         f_thread->start();
     490             :     }
     491             :     catch(...)                              // LCOV_EXCL_LINE
     492             :     {
     493             :         if(f_fifo != nullptr)               // LCOV_EXCL_LINE
     494             :         {
     495             :             f_fifo->done(false);            // LCOV_EXCL_LINE
     496             :         }
     497             : 
     498             :         f_thread.reset();                   // LCOV_EXCL_LINE
     499             :         f_asynchronous_logger.reset();      // LCOV_EXCL_LINE
     500             :         f_fifo.reset();                     // LCOV_EXCL_LINE
     501             :         throw;                              // LCOV_EXCL_LINE
     502             :     }
     503           1 : }
     504             : 
     505             : 
     506           5 : void private_logger::delete_thread()
     507             : {
     508             :     // WARNING: we can't call fifo::done() while our guard is locked
     509             :     //          we also have to make sure it's not a null pointer
     510             :     //
     511          10 :     message_fifo_t::pointer_t       fifo                = message_fifo_t::pointer_t();
     512          10 :     asynchronous_logger_pointer_t   asynchronous_logger = asynchronous_logger_pointer_t();
     513          10 :     cppthread::thread::pointer_t    thread              = cppthread::thread::pointer_t();
     514             : 
     515             :     {
     516          10 :         guard g;
     517             : 
     518           5 :         swap(thread,              f_thread);
     519           5 :         swap(asynchronous_logger, f_asynchronous_logger);
     520           5 :         swap(fifo,                f_fifo);
     521             :     }
     522             : 
     523           5 :     if(fifo != nullptr)
     524             :     {
     525           1 :         fifo->done(false);
     526             :     }
     527             : 
     528             :     try
     529             :     {
     530           5 :         thread.reset();
     531             :     }
     532             :     catch(std::exception const & e)
     533             :     {
     534             :         // in most cases this one happens when quitting when one of your
     535             :         // functions attempts to get an instance of the logger, which is
     536             :         // forbidden once you return from your main() function
     537             :         //
     538             :         std::cerr << "got exception \""
     539             :                   << e.what()
     540             :                   << "\" while deleting the asynchronous thread."
     541             :                   << std::endl;
     542             :     }
     543           5 : }
     544             : 
     545             : 
     546           1 : void private_logger::send_message_to_thread(message::pointer_t msg)
     547             : {
     548             :     {
     549           2 :         guard g;
     550             : 
     551           1 :         if(f_fifo == nullptr)
     552             :         {
     553           1 :             create_thread();
     554             :         }
     555             :     }
     556             : 
     557           1 :     f_fifo->push_back(msg);
     558           1 : }
     559             : 
     560             : 
     561             : 
     562             : 
     563       96749 : private_logger::pointer_t get_private_logger()
     564             : {
     565       96749 :     return std::dynamic_pointer_cast<private_logger>(logger::get_instance());
     566             : }
     567             : 
     568             : 
     569       48866 : private_logger::pointer_t get_private_logger(message const & msg)
     570             : {
     571       48866 :     return std::dynamic_pointer_cast<private_logger>(msg.get_logger());
     572             : }
     573             : 
     574             : 
     575             : 
     576           6 : } // snaplogger namespace
     577             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12