LCOV - code coverage report
Current view: top level - snaplogger - appender.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 93 213 43.7 %
Date: 2021-05-29 11:58:38 Functions: 19 29 65.5 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13