LCOV - code coverage report
Current view: top level - snaplogger - logger.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 86 265 32.5 %
Date: 2019-08-18 12:59:55 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      278269 : logger::pointer_t logger::get_instance()
      96             : {
      97      556538 :     guard g;
      98             : 
      99      278269 :     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      556538 :     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          11 : void logger::reset()
     128             : {
     129          22 :     guard g;
     130             : 
     131          11 :     set_asynchronous(false);
     132          11 :     f_appenders.clear();
     133          11 :     f_lowest_severity = severity_t::SEVERITY_OFF;
     134          11 : }
     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          12 : void logger::add_appender(appender::pointer_t a)
     240             : {
     241          24 :     guard g;
     242             : 
     243          12 :     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          12 :     f_appenders.push_back(a);
     263             : 
     264          12 :     severity_changed(a->get_severity());
     265          12 : }
     266             : 
     267             : 
     268           0 : void logger::add_config(std::string const & config_filename)
     269             : {
     270           0 :     advgetopt::options_environment opt_env;
     271             : 
     272             :     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             :     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           0 :     };
     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             :     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           0 :     };
     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       92588 : severity_t logger::get_lowest_severity() const
     372             : {
     373      185176 :     guard g;
     374             : 
     375      185176 :     return f_lowest_severity;
     376             : }
     377             : 
     378             : 
     379           0 : void logger::set_severity(severity_t severity_level)
     380             : {
     381           0 :     guard g;
     382             : 
     383           0 :     f_lowest_severity = severity_level;
     384           0 :     for(auto a : f_appenders)
     385             :     {
     386           0 :         a->set_severity(severity_level);
     387             :     }
     388           0 : }
     389             : 
     390             : 
     391           0 : void logger::reduce_severity(severity_t severity_level)
     392             : {
     393           0 :     for(auto a : f_appenders)
     394             :     {
     395           0 :         a->reduce_severity(severity_level);
     396             :     }
     397           0 : }
     398             : 
     399             : 
     400         297 : void logger::severity_changed(severity_t severity_level)
     401             : {
     402         594 :     guard g;
     403             : 
     404         297 :     if(severity_level < f_lowest_severity)
     405             :     {
     406          14 :         f_lowest_severity = severity_level;
     407             :     }
     408         283 :     else if(severity_level > f_lowest_severity)
     409             :     {
     410             :         // if the severity level grew we have to search for the new lowest;
     411             :         // this happens very rarely while running, it's likely to happen
     412             :         // up to once per appender on initialization.
     413             :         //
     414         283 :         auto minmax(std::minmax_element(f_appenders.begin(), f_appenders.end()));
     415         283 :         f_lowest_severity = (*minmax.first)->get_severity();
     416             :     }
     417         297 : }
     418             : 
     419             : 
     420           0 : void logger::add_component_to_include(component::pointer_t comp)
     421             : {
     422           0 :     guard g;
     423             : 
     424           0 :     f_components_to_include.insert(comp);
     425           0 : }
     426             : 
     427             : 
     428           0 : void logger::add_component_to_ignore(component::pointer_t comp)
     429             : {
     430           0 :     guard g;
     431             : 
     432           0 :     f_components_to_ignore.insert(comp);
     433           0 : }
     434             : 
     435             : 
     436           0 : bool logger::is_asynchronous() const
     437             : {
     438           0 :     guard g;
     439             : 
     440           0 :     return f_asynchronous;
     441             : }
     442             : 
     443             : 
     444          11 : void logger::set_asynchronous(bool status)
     445             : {
     446          11 :     status = status != false;
     447             : 
     448          11 :     bool do_delete(false);
     449             :     {
     450          22 :         guard g;
     451             : 
     452          11 :         if(f_asynchronous != status)
     453             :         {
     454           0 :             f_asynchronous = status;
     455           0 :             if(!f_asynchronous)
     456             :             {
     457           0 :                 do_delete = true;
     458             :             }
     459             :         }
     460             :     }
     461             : 
     462          11 :     if(do_delete)
     463             :     {
     464           0 :         private_logger * l(dynamic_cast<private_logger *>(this));
     465           0 :         l->delete_thread();
     466             :     }
     467          11 : }
     468             : 
     469             : 
     470       92588 : void logger::log_message(message const & msg)
     471             : {
     472       92588 :     if(const_cast<message &>(msg).tellp() != 0)
     473             :     {
     474       92588 :         bool asynchronous(false);
     475             :         {
     476      185176 :             guard g;
     477             : 
     478       92588 :             if(f_asynchronous)
     479             :             {
     480           0 :                 message::pointer_t m(std::make_shared<message>(msg, msg));
     481           0 :                 private_logger * l(dynamic_cast<private_logger *>(this));
     482           0 :                 l->send_message_to_thread(m);
     483           0 :                 asynchronous = true;
     484             :             }
     485             :         }
     486             : 
     487       92588 :         if(!asynchronous)
     488             :         {
     489       92588 :             process_message(msg);
     490             :         }
     491             :     }
     492             : 
     493      185172 :     if(f_fatal_severity != severity_t::SEVERITY_OFF
     494       92586 :     && msg.get_severity() >= f_fatal_severity)
     495             :     {
     496           0 :         call_fatal_error_callback();
     497           0 :         throw fatal_error("A fatal error occurred.");
     498             :     }
     499       92586 : }
     500             : 
     501             : 
     502       92588 : void logger::process_message(message const & msg)
     503             : {
     504      185176 :     appender::vector_t appenders;
     505             : 
     506             :     {
     507      185176 :         guard g;
     508             : 
     509       92588 :         bool include(f_components_to_include.empty());
     510       92588 :         component::set_t const & components(msg.get_components());
     511       92588 :         if(components.empty())
     512             :         {
     513       92584 :             if(f_components_to_ignore.find(f_normal_component) != f_components_to_ignore.end())
     514             :             {
     515           0 :                 return;
     516             :             }
     517       92584 :             if(!include)
     518             :             {
     519           0 :                 if(f_components_to_include.find(f_normal_component) != f_components_to_include.end())
     520             :                 {
     521           0 :                     include = true;
     522             :                 }
     523             :             }
     524             :         }
     525             :         else
     526             :         {
     527           8 :             for(auto c : components)
     528             :             {
     529           4 :                 if(f_components_to_ignore.find(c) != f_components_to_ignore.end())
     530             :                 {
     531           0 :                     return;
     532             :                 }
     533           4 :                 if(!include)
     534             :                 {
     535           0 :                     if(f_components_to_include.find(c) != f_components_to_include.end())
     536             :                     {
     537           0 :                         include = true;
     538             :                     }
     539             :                 }
     540             :             }
     541             :         }
     542       92588 :         if(!include)
     543             :         {
     544           0 :             return;
     545             :         }
     546             : 
     547       92588 :         if(f_appenders.empty())
     548             :         {
     549           0 :             if(isatty(fileno(stdout)))
     550             :             {
     551           0 :                 add_console_appender();
     552             :             }
     553             :             else
     554             :             {
     555           0 :                 add_syslog_appender(std::string());
     556             :             }
     557             :         }
     558             : 
     559       92588 :         appenders = f_appenders;
     560             :     }
     561             : 
     562      185176 :     for(auto a : appenders)
     563             :     {
     564       92588 :         a->send_message(msg);
     565             :     }
     566             : }
     567             : 
     568             : 
     569           0 : void logger::set_fatal_error_callback(std::function<void(void)> & f)
     570             : {
     571           0 :     f_fatal_error_callback = f;
     572           0 : }
     573             : 
     574             : 
     575           0 : void logger::call_fatal_error_callback()
     576             : {
     577           0 :     if(f_fatal_error_callback != nullptr)
     578             :     {
     579           0 :         f_fatal_error_callback();
     580             :     }
     581           0 : }
     582             : 
     583             : 
     584           0 : bool is_configured()
     585             : {
     586           0 :     guard g;
     587             : 
     588           0 :     if(g_instance == nullptr)
     589             :     {
     590           0 :         return false;
     591             :     }
     592             : 
     593           0 :     return (*g_instance)->is_configured();
     594             : }
     595             : 
     596             : 
     597           0 : bool has_appender(std::string const & type)
     598             : {
     599           0 :     guard g;
     600             : 
     601           0 :     if(g_instance == nullptr)
     602             :     {
     603           0 :         return false;
     604             :     }
     605             : 
     606           0 :     return (*g_instance)->has_appender(type);
     607             : }
     608             : 
     609             : 
     610           0 : void reopen()
     611             : {
     612           0 :     guard g;
     613             : 
     614           0 :     if(g_instance == nullptr)
     615             :     {
     616           0 :         return;
     617             :     }
     618             : 
     619           0 :     (*g_instance)->reopen();
     620             : }
     621             : 
     622             : 
     623           0 : bool configure_console(bool force)
     624             : {
     625           0 :     bool result(!is_configured() || (force && !has_appender("console")));
     626           0 :     if(result)
     627             :     {
     628           0 :         logger::get_instance()->add_console_appender();
     629             :     }
     630             : 
     631           0 :     return result;
     632             : }
     633             : 
     634             : 
     635           0 : bool configure_syslog(std::string const & identity)
     636             : {
     637           0 :     bool result(!is_configured());
     638           0 :     if(result)
     639             :     {
     640           0 :         logger::get_instance()->add_syslog_appender(identity);
     641             :     }
     642             : 
     643           0 :     return result;
     644             : }
     645             : 
     646             : 
     647           0 : bool configure_file(std::string const & filename)
     648             : {
     649           0 :     bool result(!is_configured());
     650           0 :     if(result)
     651             :     {
     652           0 :         logger::get_instance()->add_file_appender(filename);
     653             :     }
     654             : 
     655           0 :     return result;
     656             : }
     657             : 
     658             : 
     659           0 : bool configure_config(std::string const & config_filename)
     660             : {
     661           0 :     bool result(!is_configured());
     662           0 :     if(result)
     663             :     {
     664           0 :         logger::get_instance()->add_config(config_filename);
     665             :     }
     666             : 
     667           0 :     return result;
     668             : }
     669             : 
     670             : 
     671             : 
     672             : 
     673           6 : } // snaplogger namespace
     674             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12