LCOV - code coverage report
Current view: top level - snaplogger - appender.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 93 218 42.7 %
Date: 2021-06-04 12:34:06 Functions: 19 29 65.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2013-2021  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/snaplogger
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      19             : 
      20             : /** \file
      21             :  * \brief Appenders are used to append data to somewhere.
      22             :  *
      23             :  * This file implements the base appender class.
      24             :  */
      25             : 
      26             : 
      27             : // self
      28             : //
      29             : #include    "snaplogger/appender.h"
      30             : 
      31             : #include    "snaplogger/exception.h"
      32             : #include    "snaplogger/guard.h"
      33             : #include    "snaplogger/private_logger.h"
      34             : 
      35             : 
      36             : // snapdev lib
      37             : //
      38             : #include    <snapdev/empty_set_intersection.h>
      39             : #include    <snapdev/not_used.h>
      40             : 
      41             : 
      42             : // C++ lib
      43             : //
      44             : #include    <iostream>
      45             : 
      46             : 
      47             : // last include
      48             : //
      49             : #include    <snapdev/poison.h>
      50             : 
      51             : 
      52             : 
      53             : namespace snaplogger
      54             : {
      55             : 
      56             : 
      57             : namespace
      58             : {
      59             : 
      60             : 
      61             : 
      62             : // if we want to be able to reference such we need to create it TBD
      63             : // (and it should probably be in the null_appender.cpp file instead)
      64             : //APPENDER_FACTORY(null);
      65             : 
      66             : 
      67             : }
      68             : 
      69             : 
      70          18 : appender::appender(std::string const & name, std::string const & type)
      71             :     : f_type(type)
      72             :     , f_name(name)
      73          18 :     , f_normal_component(get_component(COMPONENT_NORMAL))
      74             : {
      75          36 :     guard g;
      76             : 
      77          18 :     f_format = get_private_logger()->get_default_format();
      78          18 : }
      79             : 
      80             : 
      81          18 : appender::~appender()
      82             : {
      83          18 : }
      84             : 
      85             : 
      86           3 : std::string const & appender::get_type() const
      87             : {
      88           3 :     return f_type;
      89             : }
      90             : 
      91             : 
      92           0 : void appender::set_name(std::string const & name)
      93             : {
      94           0 :     if(f_name != "console"
      95           0 :     && f_name != "syslog")
      96             :     {
      97             :         throw invalid_parameter(
      98           0 :                   "the appender set_name() can only be used for the console & syslog appenders to rename them to your own appender name (and done internally only).");
      99             :     }
     100             : 
     101           0 :     f_name = name;
     102           0 : }
     103             : 
     104             : 
     105           1 : std::string const & appender::get_name() const
     106             : {
     107           1 :     return f_name;
     108             : }
     109             : 
     110             : 
     111       89038 : bool appender::is_enabled() const
     112             : {
     113       89038 :     return f_enabled;
     114             : }
     115             : 
     116             : 
     117           0 : void appender::set_enabled(bool status)
     118             : {
     119           0 :     guard g;
     120             : 
     121           0 :     f_enabled = status;
     122           0 : }
     123             : 
     124             : 
     125          18 : bool appender::unique() const
     126             : {
     127          18 :     return false;
     128             : }
     129             : 
     130             : 
     131         297 : severity_t appender::get_severity() const
     132             : {
     133         594 :     guard g;
     134             : 
     135         594 :     return f_severity;
     136             : }
     137             : 
     138             : 
     139         281 : void appender::set_severity(severity_t severity_level)
     140             : {
     141         562 :     guard g;
     142             : 
     143         281 :     f_severity = severity_level;
     144         281 :     logger::get_instance()->severity_changed(severity_level);
     145         281 : }
     146             : 
     147             : 
     148           0 : void appender::reduce_severity(severity_t severity_level)
     149             : {
     150           0 :     guard g;
     151             : 
     152           0 :     if(severity_level < f_severity)
     153             :     {
     154           0 :         set_severity(severity_level);
     155             :     }
     156           0 : }
     157             : 
     158             : 
     159           0 : bool appender::operator < (appender const & rhs) const
     160             : {
     161           0 :     return f_severity < rhs.f_severity;
     162             : }
     163             : 
     164             : 
     165          18 : void appender::set_config(advgetopt::getopt const & opts)
     166             : {
     167          36 :     guard g;
     168             : 
     169             :     // ENABLE
     170             :     //
     171             :     {
     172          36 :         std::string const specialized_enabled(f_name + "::enabled");
     173          18 :         if(opts.is_defined(specialized_enabled))
     174             :         {
     175           0 :             f_enabled = opts.get_string(specialized_enabled) != "false";
     176             :         }
     177          18 :         else if(opts.is_defined("enabled"))
     178             :         {
     179           0 :             f_enabled = opts.get_string("enabled") != "false";
     180             :         }
     181             :         else
     182             :         {
     183          18 :             f_enabled = true;
     184             :         }
     185             :     }
     186             : 
     187             :     // FORMAT
     188             :     //
     189             :     {
     190          36 :         std::string const specialized_format(f_name + "::format");
     191          18 :         if(opts.is_defined(specialized_format))
     192             :         {
     193           0 :             f_format = std::make_shared<format>(opts.get_string(specialized_format));
     194             :         }
     195          18 :         else if(opts.is_defined("format"))
     196             :         {
     197           0 :             f_format = std::make_shared<format>(opts.get_string("format"));
     198             :         }
     199             :     }
     200             : 
     201             :     // SEVERITY
     202             :     //
     203          36 :     std::string const specialized_severity(f_name + "::severity");
     204          18 :     if(opts.is_defined(specialized_severity))
     205             :     {
     206           0 :         std::string const severity_name(opts.get_string(specialized_severity));
     207           0 :         severity::pointer_t sev(snaplogger::get_severity(severity_name));
     208           0 :         if(sev != nullptr)
     209             :         {
     210           0 :             set_severity(sev->get_severity());
     211             :         }
     212             :         else
     213             :         {
     214             :             throw invalid_severity(
     215             :                           "severity level named \""
     216           0 :                         + severity_name
     217           0 :                         + "\" not found.");
     218             :         }
     219             :     }
     220          18 :     else if(opts.is_defined("severity"))
     221             :     {
     222           0 :         std::string const severity_name(opts.get_string("severity"));
     223           0 :         severity::pointer_t sev(snaplogger::get_severity(severity_name));
     224           0 :         if(sev != nullptr)
     225             :         {
     226           0 :             set_severity(sev->get_severity());
     227             :         }
     228             :         else
     229             :         {
     230             :             throw invalid_severity(
     231             :                           "severity level named \""
     232           0 :                         + severity_name
     233           0 :                         + "\" not found.");
     234             :         }
     235             :     }
     236             : 
     237             :     // COMPONENTS
     238             :     //
     239          36 :     std::string comp;
     240          36 :     std::string const components(f_name + "::components");
     241          18 :     if(opts.is_defined(components))
     242             :     {
     243           0 :         comp = opts.get_string(components);
     244             :     }
     245          18 :     else if(opts.is_defined("components"))
     246             :     {
     247           0 :         comp = opts.get_string("components");
     248             :     }
     249          18 :     if(comp.empty())
     250             :     {
     251          18 :         add_component(f_normal_component);
     252             :     }
     253             :     else
     254             :     {
     255           0 :         advgetopt::string_list_t component_names;
     256           0 :         advgetopt::split_string(comp, component_names, {","});
     257           0 :         for(auto name : component_names)
     258             :         {
     259           0 :             add_component(get_component(name));
     260             :         }
     261             :     }
     262             : 
     263             :     // FILTER
     264             :     //
     265             :     {
     266          36 :         std::string filter;
     267          36 :         std::string const specialized_filter(f_name + "::filter");
     268          18 :         if(opts.is_defined(specialized_filter))
     269             :         {
     270           0 :             filter = opts.get_string(specialized_filter);
     271             :         }
     272          18 :         else if(opts.is_defined("filter"))
     273             :         {
     274           0 :             filter = opts.get_string("filter");
     275             :         }
     276          18 :         if(!filter.empty())
     277             :         {
     278           0 :             std::regex_constants::syntax_option_type flags(std::regex::nosubs | std::regex::optimize);
     279           0 :             std::regex_constants::syntax_option_type type(std::regex::extended);
     280           0 :             if(filter[0] == '/')
     281             :             {
     282           0 :                 std::string::size_type pos(filter.rfind('/'));
     283           0 :                 if(pos == 0)
     284             :                 {
     285             :                     throw invalid_variable(
     286             :                                   "invalid filter \""
     287           0 :                                 + filter
     288           0 :                                 + "\"; missing ending '/'.");
     289             :                 }
     290           0 :                 std::string const flag_list(filter.substr(pos + 1));
     291           0 :                 filter = filter.substr(1, pos - 2);
     292           0 :                 if(filter.empty())
     293             :                 {
     294             :                     throw invalid_variable(
     295             :                                   "invalid filter \""
     296           0 :                                 + filter
     297           0 :                                 + "\"; the regular expression is empty.");
     298             :                 }
     299             :                 // TODO: for errors we would need to iterate using the libutf8
     300             :                 //       (since we could have a Unicode character after the /)
     301             :                 //
     302             :                 // TODO: if two type flags are found, err too
     303             :                 //
     304           0 :                 int count(0);
     305           0 :                 for(auto f : flag_list)
     306             :                 {
     307           0 :                     switch(f)
     308             :                     {
     309           0 :                     case 'i':
     310           0 :                         flags |= std::regex::icase;
     311           0 :                         break;
     312             : 
     313           0 :                     case 'c':
     314           0 :                         flags |= std::regex::collate;
     315           0 :                         break;
     316             : 
     317           0 :                     case 'j':
     318           0 :                         type = std::regex::ECMAScript;
     319           0 :                         ++count;
     320           0 :                         break;
     321             : 
     322           0 :                     case 'b':
     323           0 :                         type = std::regex::basic;
     324           0 :                         ++count;
     325           0 :                         break;
     326             : 
     327           0 :                     case 'x':
     328           0 :                         type = std::regex::extended;
     329           0 :                         ++count;
     330           0 :                         break;
     331             : 
     332           0 :                     case 'a':
     333           0 :                         type = std::regex::awk;
     334           0 :                         ++count;
     335           0 :                         break;
     336             : 
     337           0 :                     case 'g':
     338           0 :                         type = std::regex::grep;
     339           0 :                         ++count;
     340           0 :                         break;
     341             : 
     342           0 :                     case 'e':
     343           0 :                         type = std::regex::egrep;
     344           0 :                         ++count;
     345           0 :                         break;
     346             : 
     347           0 :                     default:
     348             :                         throw invalid_variable(
     349             :                                       "in \""
     350           0 :                                     + filter
     351           0 :                                     + "\", found invalid flag '"
     352           0 :                                     + f
     353           0 :                                     + "'.");
     354             : 
     355             :                     }
     356           0 :                     if(count > 1)
     357             :                     {
     358             :                         throw invalid_variable(
     359             :                                       "found multiple types in \""
     360           0 :                                     + filter
     361           0 :                                     + "\".");
     362             :                     }
     363             :                 }
     364             :             }
     365           0 :             f_filter = std::make_shared<std::regex>(filter, flags | type);
     366             :         }
     367             :     }
     368             : 
     369             :     // REPEAT
     370             :     //
     371             :     {
     372          36 :         std::string no_repeat(f_name + "::no-repeat");
     373          18 :         if(!opts.is_defined(no_repeat))
     374             :         {
     375          18 :             if(opts.is_defined("no-repeat"))
     376             :             {
     377           0 :                 no_repeat = "no-repeat";
     378             :             }
     379             :             else
     380             :             {
     381          18 :                 no_repeat.clear();
     382             :             }
     383             :         }
     384          18 :         if(!no_repeat.empty())
     385             :         {
     386           0 :             std::string const value(opts.get_string(no_repeat));
     387           0 :             if(value != "off")
     388             :             {
     389           0 :                 if(value == "max"
     390           0 :                 || value == "maximum")
     391             :                 {
     392           0 :                     f_no_repeat_size = NO_REPEAT_MAXIMUM;
     393             :                 }
     394           0 :                 else if(value == "default")
     395             :                 {
     396           0 :                     f_no_repeat_size = NO_REPEAT_DEFAULT;
     397             :                 }
     398             :                 else
     399             :                 {
     400           0 :                     f_no_repeat_size = opts.get_long("no-repeat", 0, 0, NO_REPEAT_MAXIMUM);
     401             :                 }
     402             :             }
     403             :         }
     404             :     }
     405          18 : }
     406             : 
     407             : 
     408           0 : void appender::reopen()
     409             : {
     410           0 : }
     411             : 
     412             : 
     413          19 : void appender::add_component(component::pointer_t comp)
     414             : {
     415          19 :     f_components.insert(comp);
     416          19 : }
     417             : 
     418             : 
     419          40 : format::pointer_t appender::set_format(format::pointer_t new_format)
     420             : {
     421          80 :     guard g;
     422             : 
     423          40 :     format::pointer_t old(f_format);
     424          40 :     f_format = new_format;
     425          80 :     return old;
     426             : }
     427             : 
     428             : 
     429       89038 : void appender::send_message(message const & msg)
     430             : {
     431      178076 :     if(!is_enabled()
     432       89038 :     || msg.get_severity() < f_severity)
     433             :     {
     434       91690 :         return;
     435             :     }
     436             : 
     437       45374 :     component::set_t const & components(msg.get_components());
     438       45374 :     if(components.empty())
     439             :     {
     440             :         // user did not supply any component in 'msg', check for
     441             :         // the normal component
     442             :         //
     443       90740 :         if(!f_components.empty()
     444       45370 :         && f_components.find(f_normal_component) == f_components.end())
     445             :         {
     446           0 :             return;
     447             :         }
     448             :     }
     449             :     else
     450             :     {
     451           4 :         if(snap::empty_set_intersection(f_components, components))
     452             :         {
     453           2 :             return;
     454             :         }
     455             :     }
     456             : 
     457       86379 :     std::string formatted_message(f_format->process_message(msg));
     458       45367 :     if(formatted_message.empty())
     459             :     {
     460        4360 :         return;
     461             :     }
     462             : 
     463       82014 :     if(f_filter != nullptr
     464       41007 :     && !std::regex_match(formatted_message, *f_filter))
     465             :     {
     466           0 :         return;
     467             :     }
     468             : 
     469       82014 :     if(formatted_message.back() != '\n'
     470       41007 :     && formatted_message.back() != '\r')
     471             :     {
     472             :         // TODO: add support to define line terminator (cr, nl, cr nl)
     473             :         //
     474       41007 :         formatted_message += '\n';
     475             :     }
     476             : 
     477       41007 :     if(f_no_repeat_size > 0)
     478             :     {
     479           0 :         std::string const non_changing_message(f_format->process_message(msg, true));
     480           0 :         auto it(std::find(f_last_messages.rbegin(), f_last_messages.rend(), non_changing_message));
     481           0 :         if(it != f_last_messages.rend())
     482             :         {
     483             :             // TODO: look into a way to count said messages and print out
     484             :             //       the total number or something of the sort...
     485             :             //       (maybe we store those messages in a buffer and once
     486             :             //       we are to replace a message, that's when we forward
     487             :             //       it and that can include the count? also that extra
     488             :             //       write can be based on time and/or count)
     489             :             //
     490           0 :             f_last_messages.erase(std::next(it).base());    // erase() expects an iterator, not a reverse iterator
     491             :         }
     492           0 :         f_last_messages.push_back(non_changing_message);
     493           0 :         if(f_last_messages.size() > f_no_repeat_size)
     494             :         {
     495           0 :             f_last_messages.pop_front();
     496             :         }
     497             :     }
     498             : 
     499       41007 :     process_message(msg, formatted_message);
     500             : }
     501             : 
     502             : 
     503           0 : void appender::process_message(message const & msg, std::string const & formatted_message)
     504             : {
     505             :     // the default is a "null appender" -- do nothing
     506           0 :     snap::NOTUSED(msg);
     507           0 :     snap::NOTUSED(formatted_message);
     508           0 : }
     509             : 
     510             : 
     511             : 
     512             : 
     513             : 
     514           8 : appender_factory::appender_factory(std::string const & type)
     515           8 :     : f_type(type)
     516             : {
     517           8 : }
     518             : 
     519             : 
     520           8 : appender_factory::~appender_factory()
     521             : {
     522           8 : }
     523             : 
     524             : 
     525          16 : std::string const & appender_factory::get_type() const
     526             : {
     527          16 :     return f_type;
     528             : }
     529             : 
     530             : 
     531             : 
     532             : 
     533           8 : void register_appender_factory(appender_factory::pointer_t factory)
     534             : {
     535           8 :     get_private_logger()->register_appender_factory(factory);
     536           8 : }
     537             : 
     538             : 
     539           2 : appender::pointer_t create_appender(std::string const & type, std::string const & name)
     540             : {
     541           2 :     return get_private_logger()->create_appender(type, name);
     542             : }
     543             : 
     544             : 
     545             : 
     546             : 
     547             : 
     548             : 
     549           0 : safe_format::safe_format(appender::pointer_t a, format::pointer_t new_format)
     550             :     : f_appender(a)
     551           0 :     , f_old_format(a->set_format(new_format))
     552             : {
     553           0 : }
     554             : 
     555             : 
     556           0 : safe_format::~safe_format()
     557             : {
     558           0 :     snap::NOTUSED(f_appender->set_format(f_old_format));
     559           0 : }
     560             : 
     561             : 
     562             : 
     563             : 
     564             : 
     565             : 
     566             : 
     567           6 : } // snaplogger namespace
     568             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13