LCOV - code coverage report
Current view: top level - snaplogger - message.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 100 172 58.1 %
Date: 2021-08-20 16:41:45 Functions: 28 37 75.7 %
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 declares the base appender class.
      24             :  */
      25             : 
      26             : // self
      27             : // 
      28             : #include    "snaplogger/message.h"
      29             : 
      30             : #include    "snaplogger/exception.h"
      31             : #include    "snaplogger/guard.h"
      32             : #include    "snaplogger/logger.h"
      33             : 
      34             : 
      35             : // C++ lib
      36             : //
      37             : #include    <iostream>
      38             : 
      39             : 
      40             : // C lib
      41             : //
      42             : #include    <sys/time.h>
      43             : 
      44             : 
      45             : // last include
      46             : //
      47             : #include    <snapdev/poison.h>
      48             : 
      49             : 
      50             : 
      51             : namespace snaplogger
      52             : {
      53             : 
      54             : 
      55             : namespace
      56             : {
      57             : 
      58             : #pragma GCC diagnostic push
      59             : #pragma GCC diagnostic ignored "-Wpedantic"
      60             : constexpr char const * const g_system_field_names[] =
      61             : {
      62             :     [static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_MESSAGE      )] = "_message",
      63             :     [static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_TIMESTAMP    )] = "_timestamp",
      64             :     [static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_SEVERITY     )] = "_severity",
      65             :     [static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_FILENAME     )] = "_filename",
      66             :     [static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_FUNCTION_NAME)] = "_function_name",
      67             :     [static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_LINE         )] = "_line",
      68             : };
      69             : #pragma GCC diagnostic pop
      70             : 
      71             : 
      72             : int         g_message_id = 0;
      73             : 
      74             : 
      75       92586 : int get_next_id()
      76             : {
      77      185172 :     guard g;
      78             : 
      79       92586 :     ++g_message_id;
      80       92586 :     if(g_message_id == 0)
      81             :     {
      82             :         // never use 0 as the id; of course, it's very unlikely that this happens
      83           0 :         g_message_id = 1;
      84             :     }
      85      185172 :     return g_message_id;
      86             : }
      87             : 
      88             : 
      89             : 
      90             : }
      91             : // no name namespace
      92             : 
      93             : 
      94             : 
      95             : 
      96     1045165 : int null_buffer::overflow(int c)
      97             : {
      98     1045165 :     return c;
      99             : }
     100             : 
     101             : 
     102             : 
     103             : 
     104             : 
     105       92586 : message::message(
     106             :           severity_t sev
     107             :         , char const * file
     108             :         , char const * func
     109       92586 :         , int line)
     110             :     : f_logger(logger::get_instance())
     111             :     , f_severity(sev)
     112             :     , f_filename(file == nullptr ? std::string() : std::string(file))
     113             :     , f_funcname(func == nullptr ? std::string() : std::string(func))
     114             :     , f_line(line)
     115       92586 :     , f_environment(create_environment())
     116             : {
     117       92586 :     clock_gettime(CLOCK_REALTIME_COARSE, &f_timestamp);
     118             : 
     119       92586 :     add_field("id", std::to_string(get_next_id()));
     120             : 
     121      185172 :     if(f_severity < f_logger->get_lowest_severity()
     122       92586 :     || f_severity == severity_t::SEVERITY_OFF)
     123             :     {
     124       46220 :         f_null.reset(new null_buffer);
     125       46220 :         std::ostream & ref = *this;
     126       46220 :         f_saved_buffer = ref.rdbuf(f_null.get());
     127             :     }
     128       92586 : }
     129             : 
     130             : 
     131           2 : message::message(std::basic_stringstream<char> const & m, message const & msg)
     132             :     : f_logger(msg.f_logger)
     133             :     , f_timestamp(msg.f_timestamp)
     134           2 :     , f_severity(msg.f_severity)
     135             :     , f_filename(msg.f_filename)
     136             :     , f_funcname(msg.f_funcname)
     137           2 :     , f_line(msg.f_line)
     138           2 :     , f_recursive_message(msg.f_recursive_message)
     139             :     , f_environment(msg.f_environment)
     140             :     , f_components(msg.f_components)
     141             :     , f_fields(msg.f_fields)
     142             :     , f_null(null_buffer::pointer_t())
     143             :     , f_saved_buffer(nullptr)
     144           8 :     , f_copy(true)
     145             : {
     146           2 :     *this << m.rdbuf();
     147           2 : }
     148             : 
     149             : 
     150      185176 : message::~message()
     151             : {
     152       92588 :     if(f_saved_buffer != nullptr)
     153             :     {
     154       46220 :         std::ostream & ref = *this;
     155       46220 :         ref.rdbuf(f_saved_buffer);
     156             :     }
     157       92588 : }
     158             : 
     159             : 
     160           0 : severity_t message::default_severity()
     161             : {
     162           0 :     return logger::get_instance()->get_default_severity();
     163             : }
     164             : 
     165             : 
     166       26987 : void message::set_severity(severity_t severity)
     167             : {
     168       26987 :     f_severity = severity;
     169       26987 : }
     170             : 
     171             : 
     172           1 : void message::set_filename(std::string const & filename)
     173             : {
     174           1 :     f_filename = filename;
     175           1 : }
     176             : 
     177             : 
     178           1 : void message::set_function(std::string const & funcname)
     179             : {
     180           1 :     f_funcname = funcname;
     181           1 : }
     182             : 
     183             : 
     184           1 : void message::set_line(int line)
     185             : {
     186           1 :     f_line = line;
     187           1 : }
     188             : 
     189             : 
     190          34 : void message::set_recursive_message(bool state) const
     191             : {
     192          34 :     f_recursive_message = state;
     193          34 : }
     194             : 
     195             : 
     196           8 : bool message::can_add_component(component::pointer_t c) const
     197             : {
     198           8 :     if(c != nullptr)
     199             :     {
     200           8 :         return !c->is_mutually_exclusive(f_components);
     201             :     }
     202             : 
     203           0 :     return false;
     204             : }
     205             : 
     206             : 
     207           8 : void message::add_component(component::pointer_t c)
     208             : {
     209           8 :     if(c != nullptr)
     210             :     {
     211           8 :         if(!can_add_component(c))
     212             :         {
     213             :             throw duplicate_error(
     214             :                   "component \""
     215           0 :                 + c->get_name()
     216           0 :                 + "\" cannot be added to this message as it is mutually exclusive with one or more of the other components that were added to this message.");
     217             :         }
     218             : 
     219           8 :         f_components.insert(c);
     220             :     }
     221           8 : }
     222             : 
     223             : 
     224       92589 : void message::add_field(std::string const & name, std::string const & value)
     225             : {
     226       92589 :     if(!name.empty())
     227             :     {
     228       92589 :         if(name[0] == '_')
     229             :         {
     230             :             throw invalid_parameter(
     231             :                   "field name \""
     232           0 :                 + name
     233           0 :                 + "\" is a system name (whether reserved or already defined) and as such is read-only."
     234           0 :                   " Do not start your field names with an underscore.");
     235             :         }
     236             : 
     237       92589 :         f_fields[name] = value;
     238             :     }
     239       92589 : }
     240             : 
     241             : 
     242       47064 : std::shared_ptr<logger> message::get_logger() const
     243             : {
     244       47064 :     return f_logger;
     245             : }
     246             : 
     247             : 
     248      185250 : severity_t message::get_severity() const
     249             : {
     250      185250 :     return f_severity;
     251             : }
     252             : 
     253             : 
     254           0 : void message::set_precise_time()
     255             : {
     256           0 :     clock_gettime(CLOCK_REALTIME, &f_timestamp);
     257           0 : }
     258             : 
     259             : 
     260           4 : timespec const & message::get_timestamp() const
     261             : {
     262           4 :     return f_timestamp;
     263             : }
     264             : 
     265             : 
     266           2 : std::string const & message::get_filename() const
     267             : {
     268           2 :     return f_filename;
     269             : }
     270             : 
     271             : 
     272           2 : std::string const & message::get_function() const
     273             : {
     274           2 :     return f_funcname;
     275             : }
     276             : 
     277             : 
     278           2 : int message::get_line() const
     279             : {
     280           2 :     return f_line;
     281             : }
     282             : 
     283             : 
     284       46858 : bool message::get_recursive_message() const
     285             : {
     286       46858 :     return f_recursive_message;
     287             : }
     288             : 
     289             : 
     290           0 : bool message::has_component(component::pointer_t c) const
     291             : {
     292           0 :     return f_components.find(c) != f_components.end();
     293             : }
     294             : 
     295             : 
     296      139449 : component::set_t const & message::get_components() const
     297             : {
     298      139449 :     return f_components;
     299             : }
     300             : 
     301             : 
     302          47 : environment::pointer_t message::get_environment() const
     303             : {
     304          47 :     return f_environment;
     305             : }
     306             : 
     307             : 
     308       46856 : std::string message::get_message() const
     309             : {
     310       46856 :     std::string s(str());
     311             : 
     312       93712 :     if(!s.empty()
     313       46856 :     && s.back() == '\n')
     314             :     {
     315           4 :         s.pop_back();
     316             :     }
     317             : 
     318       93712 :     if(!s.empty()
     319       46856 :     && s.back() == '\r')
     320             :     {
     321           2 :         s.pop_back();
     322             :     }
     323             : 
     324       46856 :     return s;
     325             : }
     326             : 
     327             : 
     328           0 : char const * message::get_system_field_name(system_field_t field)
     329             : {
     330           0 :     std::size_t const idx(static_cast<std::size_t>(field));
     331           0 :     if(idx >= static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_max))
     332             :     {
     333           0 :         return "_unknown";
     334             :     }
     335           0 :     return g_system_field_names[idx];
     336             : }
     337             : 
     338             : 
     339           0 : system_field_t message::get_system_field_from_name(std::string const & name)
     340             : {
     341           0 :     if(name.length() >= 2
     342           0 :     && name[0] == '_')
     343             :     {
     344           0 :         switch(name[1])
     345             :         {
     346           0 :         case 'f':
     347           0 :             if(name == "_filename")
     348             :             {
     349           0 :                 return system_field_t::SYSTEM_FIELD_FILENAME;
     350             :             }
     351           0 :             if(name == "_function_name")
     352             :             {
     353           0 :                 return system_field_t::SYSTEM_FIELD_FUNCTION_NAME;
     354             :             }
     355           0 :             break;
     356             : 
     357           0 :         case 'l':
     358           0 :             if(name == "_line")
     359             :             {
     360           0 :                 return system_field_t::SYSTEM_FIELD_LINE;
     361             :             }
     362           0 :             break;
     363             : 
     364           0 :         case 'm':
     365           0 :             if(name == "_message")
     366             :             {
     367           0 :                 return system_field_t::SYSTEM_FIELD_MESSAGE;
     368             :             }
     369           0 :             break;
     370             : 
     371           0 :         case 's':
     372           0 :             if(name == "_severity")
     373             :             {
     374           0 :                 return system_field_t::SYSTEM_FIELD_SEVERITY;
     375             :             }
     376           0 :             break;
     377             : 
     378           0 :         case 't':
     379           0 :             if(name == "_timestamp")
     380             :             {
     381           0 :                 return system_field_t::SYSTEM_FIELD_TIMESTAMP;
     382             :             }
     383           0 :             break;
     384             : 
     385             :         }
     386             :     }
     387             : 
     388           0 :     return system_field_t::SYSTEM_FIELD_UNDEFINED;
     389             : }
     390             : 
     391             : 
     392           1 : std::string message::get_field(std::string const & name) const
     393             : {
     394           2 :     if(!name.empty()
     395           1 :     && name[0] == '_')
     396             :     {
     397           0 :         switch(get_system_field_from_name(name))
     398             :         {
     399           0 :         case system_field_t::SYSTEM_FIELD_MESSAGE:
     400           0 :             return get_message();
     401             : 
     402           0 :         case system_field_t::SYSTEM_FIELD_TIMESTAMP:
     403             :             // TODO: offer ways to get the date & time converted to strings
     404             :             {
     405           0 :                 std::string timestamp(std::to_string(f_timestamp.tv_sec));
     406           0 :                 if(f_timestamp.tv_nsec != 0)
     407             :                 {
     408           0 :                     std::string nsec(std::to_string(f_timestamp.tv_nsec));
     409           0 :                     while(nsec.length() < 9)
     410             :                     {
     411           0 :                         nsec = '0' + nsec;
     412             :                     }
     413           0 :                     while(nsec.back() == '0')
     414             :                     {
     415           0 :                         nsec.pop_back();
     416             :                     }
     417           0 :                     timestamp += '.';
     418           0 :                     timestamp += nsec;
     419             :                 }
     420           0 :                 return timestamp;
     421             :             }
     422             : 
     423           0 :         case system_field_t::SYSTEM_FIELD_SEVERITY:
     424             :             {
     425           0 :                 severity::pointer_t sev(snaplogger::get_severity(f_severity));
     426           0 :                 return sev == nullptr ? "<unknown>" : sev->get_name();
     427             :             }
     428             : 
     429           0 :         case system_field_t::SYSTEM_FIELD_FILENAME:
     430           0 :             return f_filename;
     431             : 
     432           0 :         case system_field_t::SYSTEM_FIELD_FUNCTION_NAME:
     433           0 :             return f_funcname;
     434             : 
     435           0 :         case system_field_t::SYSTEM_FIELD_LINE:
     436           0 :             return std::to_string(f_line);
     437             : 
     438           0 :         default:
     439           0 :             return std::string();
     440             : 
     441             :         }
     442             :     }
     443             : 
     444           1 :     auto it(f_fields.find(name));
     445           1 :     if(it == f_fields.end())
     446             :     {
     447           0 :         return std::string();
     448             :     }
     449           1 :     return it->second;
     450             : }
     451             : 
     452             : 
     453           1 : message::field_map_t message::get_fields() const
     454             : {
     455           1 :     return f_fields;
     456             : }
     457             : 
     458             : 
     459       92579 : void send_message(std::basic_ostream<char> & out)
     460             : {
     461       92579 :     message * msg(dynamic_cast<message *>(&out));
     462       92579 :     if(msg == nullptr)
     463             :     {
     464           1 :         throw not_a_message("the 'out' parameter to the send_message() function is expected to be a snaplogger::message object.");
     465             :     }
     466             : 
     467       92579 :     logger::get_instance()->log_message(*msg);
     468       92577 : }
     469             : 
     470             : 
     471             : 
     472             : 
     473           6 : } // snaplogger namespace
     474             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13