LCOV - code coverage report
Current view: top level - snaplogger - logger.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 75 249 30.1 %
Date: 2019-08-13 00:35:33 Functions: 13 37 35.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.12