LCOV - code coverage report
Current view: top level - snaplogger - logger.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 97 268 36.2 %
Date: 2019-12-13 00:59:36 Functions: 15 41 36.6 %
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/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/private_logger.h"
      37             : #include    "snaplogger/syslog_appender.h"
      38             : 
      39             : 
      40             : // last include
      41             : //
      42             : #include    <snapdev/poison.h>
      43             : 
      44             : 
      45             : 
      46             : namespace snaplogger
      47             : {
      48             : 
      49             : 
      50             : namespace
      51             : {
      52             : 
      53             : 
      54             : 
      55             : bool                        g_first_instance = true;
      56             : logger::pointer_t *         g_instance = nullptr;
      57             : 
      58             : 
      59             : struct auto_delete_logger
      60             : {
      61           2 :     ~auto_delete_logger()
      62             :     {
      63           2 :         logger::pointer_t * ptr(nullptr);
      64             :         {
      65           4 :             guard g;
      66           2 :             swap(ptr, g_instance);
      67             :         }
      68           2 :         if(ptr != nullptr)
      69             :         {
      70           2 :             (*ptr)->shutdown();
      71           2 :             delete ptr;
      72             :         }
      73           2 :     }
      74             : };
      75             : 
      76           2 : auto_delete_logger          g_logger_deleter = auto_delete_logger();
      77             : 
      78             : 
      79             : 
      80             : }
      81             : // no name namespace
      82             : 
      83             : 
      84             : 
      85           2 : logger::logger()
      86             : {
      87           2 : }
      88             : 
      89             : 
      90           2 : logger::~logger()
      91             : {
      92           2 : }
      93             : 
      94             : 
      95      284557 : logger::pointer_t logger::get_instance()
      96             : {
      97      569114 :     guard g;
      98             : 
      99      284557 :     if(g_instance == nullptr)
     100             :     {
     101           2 :         if(!g_first_instance)
     102             :         {
     103           0 :             throw duplicate_error("preventing an attempt of re-creating the snap logger.");
     104             :         }
     105             : 
     106           2 :         g_first_instance = false;
     107             : 
     108             :         // note that we create a `private_logger` object
     109             :         //
     110           2 :         g_instance = new logger::pointer_t();
     111           2 :         g_instance->reset(new private_logger());
     112             :     }
     113             : 
     114      569114 :     return *g_instance;
     115             : }
     116             : 
     117             : 
     118             : /** \brief Reset the logger to its startup state.
     119             :  *
     120             :  * This function resets the logger to non-asynchronous and no appenders.
     121             :  *
     122             :  * This is mainly used in our unit tests so we do not have to run the
     123             :  * tests one at a time. It should nearly never be useful in your environment
     124             :  * except if you do a fork() and wanted the child to have its own special
     125             :  * log environment.
     126             :  */
     127          13 : void logger::reset()
     128             : {
     129          26 :     guard g;
     130             : 
     131          13 :     set_asynchronous(false);
     132          13 :     f_appenders.clear();
     133          13 :     f_lowest_severity = severity_t::SEVERITY_OFF;
     134          13 : }
     135             : 
     136             : 
     137           0 : void logger::shutdown()
     138             : {
     139           0 : }
     140             : 
     141             : 
     142           0 : bool logger::is_configured() const
     143             : {
     144           0 :     guard g;
     145             : 
     146           0 :     return !f_appenders.empty();
     147             : }
     148             : 
     149             : 
     150           0 : bool logger::has_appender(std::string const & type) const
     151             : {
     152           0 :     return std::find_if(
     153             :           f_appenders.begin()
     154             :         , f_appenders.end()
     155           0 :         , [&type](auto a)
     156           0 :         {
     157           0 :             return type == a->get_type();
     158           0 :         }) != f_appenders.end();
     159             : }
     160             : 
     161             : 
     162           2 : appender::pointer_t logger::get_appender(std::string const & name) const
     163             : {
     164             :     auto it(std::find_if(
     165             :           f_appenders.begin()
     166             :         , f_appenders.end()
     167           1 :         , [&name](auto a)
     168           1 :         {
     169           1 :             return name == a->get_name();
     170           3 :         }));
     171           2 :     if(it == f_appenders.end())
     172             :     {
     173           1 :         return appender::pointer_t();
     174             :     }
     175             : 
     176           1 :     return *it;
     177             : }
     178             : 
     179             : 
     180           0 : void logger::set_config(advgetopt::getopt const & params)
     181             : {
     182           0 :     if(params.is_defined("asynchronous"))
     183             :     {
     184           0 :         set_asynchronous(params.get_string("asynchronous") == "true");
     185             :     }
     186             : 
     187           0 :     std::string const name(advgetopt::CONFIGURATION_SECTIONS);
     188           0 :     auto const & sections(params.get_option(name));
     189           0 :     if(sections != nullptr)
     190             :     {
     191           0 :         size_t const max(sections->size());
     192           0 :         for(size_t idx(0); idx < max; ++idx)
     193             :         {
     194           0 :             std::string const section_name(sections->get_value(idx));
     195           0 :             std::string const section_type(section_name + "::type");
     196           0 :             std::string type;
     197           0 :             if(params.is_defined(section_type))
     198             :             {
     199           0 :                 type = params.get_string(section_type);
     200             :             }
     201             :             else
     202             :             {
     203             :                 // try with the name of the section if no type is defined
     204             :                 //
     205           0 :                 type = section_name;
     206             :             }
     207           0 :             if(!type.empty())
     208             :             {
     209           0 :                 appender::pointer_t a(create_appender(type, section_name));
     210           0 :                 if(a != nullptr)
     211             :                 {
     212           0 :                     add_appender(a);
     213             :                 }
     214             :                 // else -- this may be a section which does not represent an appender
     215             :             }
     216             :         }
     217             :     }
     218             : 
     219           0 :     guard g;
     220             : 
     221           0 :     for(auto a : f_appenders)
     222             :     {
     223           0 :         a->set_config(params);
     224             :     }
     225           0 : }
     226             : 
     227             : 
     228           0 : void logger::reopen()
     229             : {
     230           0 :     guard g;
     231             : 
     232           0 :     for(auto a : f_appenders)
     233             :     {
     234           0 :         a->reopen();
     235             :     }
     236           0 : }
     237             : 
     238             : 
     239          18 : void logger::add_appender(appender::pointer_t a)
     240             : {
     241          36 :     guard g;
     242             : 
     243          18 :     if(a->unique())
     244             :     {
     245           0 :         std::string const type(a->get_type());
     246             :         auto it(std::find_if(
     247             :                   f_appenders.begin()
     248             :                 , f_appenders.end()
     249           0 :                 , [&type](auto app)
     250           0 :                 {
     251           0 :                     return type == app->get_type();
     252           0 :                 }));
     253           0 :         if(it != f_appenders.end())
     254             :         {
     255             :             throw duplicate_error(
     256             :                           "an appender of type \""
     257           0 :                         + type
     258           0 :                         + "\" can only be added once.");
     259             :         }
     260             :     }
     261             : 
     262          18 :     f_appenders.push_back(a);
     263             : 
     264          18 :     severity_changed(a->get_severity());
     265          18 : }
     266             : 
     267             : 
     268           0 : void logger::add_config(std::string const & config_filename)
     269             : {
     270           0 :     advgetopt::options_environment opt_env;
     271             : 
     272           0 :     char const * configuration_files[] =
     273             :     {
     274           0 :           config_filename.c_str()
     275             :         , nullptr
     276           0 :     };
     277             : 
     278           0 :     opt_env.f_project_name = "logger";
     279           0 :     opt_env.f_environment_variable_name = "SNAPLOGGER";
     280           0 :     opt_env.f_configuration_files = configuration_files;
     281           0 :     opt_env.f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_DYNAMIC_PARAMETERS;
     282             : 
     283           0 :     advgetopt::getopt opts(opt_env);
     284             : 
     285           0 :     opts.parse_configuration_files();
     286           0 :     opts.parse_environment_variable();
     287             : 
     288           0 :     set_config(opts);
     289           0 : }
     290             : 
     291             : 
     292           0 : appender::pointer_t logger::add_console_appender()
     293             : {
     294           0 :     appender::pointer_t a(std::make_shared<console_appender>("console"));
     295             : 
     296           0 :     advgetopt::options_environment opt_env;
     297           0 :     opt_env.f_project_name = "logger";
     298           0 :     advgetopt::getopt opts(opt_env);
     299           0 :     a->set_config(opts);
     300             : 
     301           0 :     guard g;
     302             : 
     303           0 :     add_appender(a);
     304             : 
     305           0 :     return a;
     306             : }
     307             : 
     308             : 
     309           0 : appender::pointer_t logger::add_syslog_appender(std::string const & identity)
     310             : {
     311           0 :     appender::pointer_t a(std::make_shared<syslog_appender>("syslog"));
     312             : 
     313           0 :     advgetopt::option options[] =
     314             :     {
     315             :         advgetopt::define_option(
     316             :               advgetopt::Name("syslog::identity")
     317             :             , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_REQUIRED>())
     318             :         ),
     319             :         advgetopt::end_options()
     320             :     };
     321             : 
     322           0 :     advgetopt::options_environment opt_env;
     323           0 :     opt_env.f_project_name = "logger";
     324           0 :     opt_env.f_options = options;
     325           0 :     advgetopt::getopt opts(opt_env);
     326           0 :     if(!identity.empty())
     327             :     {
     328           0 :         opts.get_option("syslog::identity")->set_value(0, identity);
     329             :     }
     330           0 :     a->set_config(opts);
     331             : 
     332           0 :     guard g;
     333             : 
     334           0 :     add_appender(a);
     335             : 
     336           0 :     return a;
     337             : }
     338             : 
     339             : 
     340           0 : appender::pointer_t logger::add_file_appender(std::string const & filename)
     341             : {
     342           0 :     file_appender::pointer_t a(std::make_shared<file_appender>("file"));
     343             : 
     344           0 :     advgetopt::option options[] =
     345             :     {
     346             :         advgetopt::define_option(
     347             :               advgetopt::Name("file::filename")
     348             :             , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_REQUIRED>())
     349             :         ),
     350             :         advgetopt::end_options()
     351             :     };
     352             : 
     353           0 :     advgetopt::options_environment opt_env;
     354           0 :     opt_env.f_project_name = "logger";
     355           0 :     opt_env.f_options = options;
     356           0 :     advgetopt::getopt opts(opt_env);
     357           0 :     if(!filename.empty())
     358             :     {
     359           0 :         opts.get_option("file::filename")->set_value(0, filename);
     360             :     }
     361           0 :     a->set_config(opts);
     362             : 
     363           0 :     guard g;
     364             : 
     365           0 :     add_appender(a);
     366             : 
     367           0 :     return a;
     368             : }
     369             : 
     370             : 
     371       94615 : severity_t logger::get_lowest_severity() const
     372             : {
     373      189230 :     guard g;
     374             : 
     375       94615 :     if(f_appenders.empty())
     376             :     {
     377             :         // we do not know the level yet, we do not have the appenders
     378             :         // yet... so accept anything at this point
     379             :         //
     380           1 :         return severity_t::SEVERITY_ALL;
     381             :     }
     382             : 
     383       94614 :     return f_lowest_severity;
     384             : }
     385             : 
     386             : 
     387           0 : void logger::set_severity(severity_t severity_level)
     388             : {
     389           0 :     guard g;
     390             : 
     391           0 :     f_lowest_severity = severity_level;
     392           0 :     for(auto a : f_appenders)
     393             :     {
     394           0 :         a->set_severity(severity_level);
     395             :     }
     396           0 : }
     397             : 
     398             : 
     399           0 : void logger::reduce_severity(severity_t severity_level)
     400             : {
     401           0 :     for(auto a : f_appenders)
     402             :     {
     403           0 :         a->reduce_severity(severity_level);
     404             :     }
     405           0 : }
     406             : 
     407             : 
     408         306 : void logger::severity_changed(severity_t severity_level)
     409             : {
     410         612 :     guard g;
     411             : 
     412         306 :     if(severity_level < f_lowest_severity)
     413             :     {
     414          16 :         f_lowest_severity = severity_level;
     415             :     }
     416         290 :     else if(severity_level > f_lowest_severity)
     417             :     {
     418             :         // if the severity level grew we have to search for the new lowest;
     419             :         // this happens very rarely while running, it's likely to happen
     420             :         // up to once per appender on initialization.
     421             :         //
     422         286 :         auto minmax(std::minmax_element(f_appenders.begin(), f_appenders.end()));
     423         286 :         f_lowest_severity = (*minmax.first)->get_severity();
     424             :     }
     425         306 : }
     426             : 
     427             : 
     428           0 : void logger::add_component_to_include(component::pointer_t comp)
     429             : {
     430           0 :     guard g;
     431             : 
     432           0 :     f_components_to_include.insert(comp);
     433           0 : }
     434             : 
     435             : 
     436           0 : void logger::add_component_to_ignore(component::pointer_t comp)
     437             : {
     438           0 :     guard g;
     439             : 
     440           0 :     f_components_to_ignore.insert(comp);
     441           0 : }
     442             : 
     443             : 
     444           0 : bool logger::is_asynchronous() const
     445             : {
     446           0 :     guard g;
     447             : 
     448           0 :     return f_asynchronous;
     449             : }
     450             : 
     451             : 
     452          15 : void logger::set_asynchronous(bool status)
     453             : {
     454          15 :     status = status != false;
     455             : 
     456          15 :     bool do_delete(false);
     457             :     {
     458          30 :         guard g;
     459             : 
     460          15 :         if(f_asynchronous != status)
     461             :         {
     462           2 :             f_asynchronous = status;
     463           2 :             if(!f_asynchronous)
     464             :             {
     465           1 :                 do_delete = true;
     466             :             }
     467             :         }
     468             :     }
     469             : 
     470          15 :     if(do_delete)
     471             :     {
     472           1 :         private_logger * l(dynamic_cast<private_logger *>(this));
     473           1 :         l->delete_thread();
     474             :     }
     475          15 : }
     476             : 
     477             : 
     478       94614 : void logger::log_message(message const & msg)
     479             : {
     480       94614 :     if(const_cast<message &>(msg).tellp() != 0)
     481             :     {
     482       94614 :         bool asynchronous(false);
     483             :         {
     484      189228 :             guard g;
     485             : 
     486       94614 :             if(f_asynchronous)
     487             :             {
     488           2 :                 message::pointer_t m(std::make_shared<message>(msg, msg));
     489           1 :                 private_logger * l(dynamic_cast<private_logger *>(this));
     490           1 :                 l->send_message_to_thread(m);
     491           1 :                 asynchronous = true;
     492             :             }
     493             :         }
     494             : 
     495       94614 :         if(!asynchronous)
     496             :         {
     497       94613 :             process_message(msg);
     498             :         }
     499             :     }
     500             : 
     501      189218 :     if(f_fatal_severity != severity_t::SEVERITY_OFF
     502       94609 :     && msg.get_severity() >= f_fatal_severity)
     503             :     {
     504           0 :         call_fatal_error_callback();
     505           0 :         throw fatal_error("A fatal error occurred.");
     506             :     }
     507       94609 : }
     508             : 
     509             : 
     510       94614 : void logger::process_message(message const & msg)
     511             : {
     512      189228 :     appender::vector_t appenders;
     513             : 
     514             :     {
     515      189228 :         guard g;
     516             : 
     517       94614 :         bool include(f_components_to_include.empty());
     518       94614 :         component::set_t const & components(msg.get_components());
     519       94614 :         if(components.empty())
     520             :         {
     521       94610 :             if(f_components_to_ignore.find(f_normal_component) != f_components_to_ignore.end())
     522             :             {
     523           0 :                 return;
     524             :             }
     525       94610 :             if(!include)
     526             :             {
     527           0 :                 if(f_components_to_include.find(f_normal_component) != f_components_to_include.end())
     528             :                 {
     529           0 :                     include = true;
     530             :                 }
     531             :             }
     532             :         }
     533             :         else
     534             :         {
     535           8 :             for(auto c : components)
     536             :             {
     537           4 :                 if(f_components_to_ignore.find(c) != f_components_to_ignore.end())
     538             :                 {
     539           0 :                     return;
     540             :                 }
     541           4 :                 if(!include)
     542             :                 {
     543           0 :                     if(f_components_to_include.find(c) != f_components_to_include.end())
     544             :                     {
     545           0 :                         include = true;
     546             :                     }
     547             :                 }
     548             :             }
     549             :         }
     550       94614 :         if(!include)
     551             :         {
     552           0 :             return;
     553             :         }
     554             : 
     555       94614 :         if(f_appenders.empty())
     556             :         {
     557           0 :             if(isatty(fileno(stdout)))
     558             :             {
     559           0 :                 add_console_appender();
     560             :             }
     561             :             else
     562             :             {
     563           0 :                 add_syslog_appender(std::string());
     564             :             }
     565             :         }
     566             : 
     567       94614 :         appenders = f_appenders;
     568             :     }
     569             : 
     570      189241 :     for(auto a : appenders)
     571             :     {
     572       94627 :         a->send_message(msg);
     573             :     }
     574             : }
     575             : 
     576             : 
     577           0 : void logger::set_fatal_error_callback(std::function<void(void)> & f)
     578             : {
     579           0 :     f_fatal_error_callback = f;
     580           0 : }
     581             : 
     582             : 
     583           0 : void logger::call_fatal_error_callback()
     584             : {
     585           0 :     if(f_fatal_error_callback != nullptr)
     586             :     {
     587           0 :         f_fatal_error_callback();
     588             :     }
     589           0 : }
     590             : 
     591             : 
     592           0 : bool is_configured()
     593             : {
     594           0 :     guard g;
     595             : 
     596           0 :     if(g_instance == nullptr)
     597             :     {
     598           0 :         return false;
     599             :     }
     600             : 
     601           0 :     return (*g_instance)->is_configured();
     602             : }
     603             : 
     604             : 
     605           0 : bool has_appender(std::string const & type)
     606             : {
     607           0 :     guard g;
     608             : 
     609           0 :     if(g_instance == nullptr)
     610             :     {
     611           0 :         return false;
     612             :     }
     613             : 
     614           0 :     return (*g_instance)->has_appender(type);
     615             : }
     616             : 
     617             : 
     618           0 : void reopen()
     619             : {
     620           0 :     guard g;
     621             : 
     622           0 :     if(g_instance == nullptr)
     623             :     {
     624           0 :         return;
     625             :     }
     626             : 
     627           0 :     (*g_instance)->reopen();
     628             : }
     629             : 
     630             : 
     631           0 : bool configure_console(bool force)
     632             : {
     633           0 :     bool result(!is_configured() || (force && !has_appender("console")));
     634           0 :     if(result)
     635             :     {
     636           0 :         logger::get_instance()->add_console_appender();
     637             :     }
     638             : 
     639           0 :     return result;
     640             : }
     641             : 
     642             : 
     643           0 : bool configure_syslog(std::string const & identity)
     644             : {
     645           0 :     bool result(!is_configured());
     646           0 :     if(result)
     647             :     {
     648           0 :         logger::get_instance()->add_syslog_appender(identity);
     649             :     }
     650             : 
     651           0 :     return result;
     652             : }
     653             : 
     654             : 
     655           0 : bool configure_file(std::string const & filename)
     656             : {
     657           0 :     bool result(!is_configured());
     658           0 :     if(result)
     659             :     {
     660           0 :         logger::get_instance()->add_file_appender(filename);
     661             :     }
     662             : 
     663           0 :     return result;
     664             : }
     665             : 
     666             : 
     667           0 : bool configure_config(std::string const & config_filename)
     668             : {
     669           0 :     bool result(!is_configured());
     670           0 :     if(result)
     671             :     {
     672           0 :         logger::get_instance()->add_config(config_filename);
     673             :     }
     674             : 
     675           0 :     return result;
     676             : }
     677             : 
     678             : 
     679             : 
     680             : 
     681           6 : } // snaplogger namespace
     682             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13