LCOV - code coverage report
Current view: top level - snaplogger - message.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 105 182 57.7 %
Date: 2022-07-01 22:43:09 Functions: 30 40 75.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2013-2022  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       99165 : int get_next_id()
      76             : {
      77      198330 :     guard g;
      78             : 
      79       99165 :     ++g_message_id;
      80       99165 :     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      198330 :     return g_message_id;
      86             : }
      87             : 
      88             : 
      89             : 
      90             : }
      91             : // no name namespace
      92             : 
      93             : 
      94             : 
      95             : 
      96     1204931 : int null_buffer::overflow(int c)
      97             : {
      98     1204931 :     return c;
      99             : }
     100             : 
     101             : 
     102             : 
     103             : 
     104             : 
     105       99165 : message::message(
     106             :           severity_t sev
     107             :         , char const * file
     108             :         , char const * func
     109       99165 :         , 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             :     , f_environment(create_environment())
     116       99165 :     , f_fields(f_logger->get_default_fields())
     117             : {
     118       99165 :     clock_gettime(CLOCK_REALTIME_COARSE, &f_timestamp);
     119             : 
     120       99165 :     add_field("id", std::to_string(get_next_id()));
     121             : 
     122      198330 :     if(f_severity < f_logger->get_lowest_severity()
     123       99165 :     || f_severity == severity_t::SEVERITY_OFF)
     124             :     {
     125       50919 :         f_null.reset(new null_buffer);
     126       50919 :         std::ostream & ref = *this;
     127       50919 :         f_saved_buffer = ref.rdbuf(f_null.get());
     128             :     }
     129       99165 : }
     130             : 
     131             : 
     132           2 : message::message(std::basic_stringstream<char> const & m, message const & msg)
     133             :     : f_logger(msg.f_logger)
     134             :     , f_timestamp(msg.f_timestamp)
     135           2 :     , f_severity(msg.f_severity)
     136             :     , f_filename(msg.f_filename)
     137             :     , f_funcname(msg.f_funcname)
     138           2 :     , f_line(msg.f_line)
     139           2 :     , f_recursive_message(msg.f_recursive_message)
     140             :     , f_environment(msg.f_environment)
     141             :     , f_components(msg.f_components)
     142             :     , f_fields(msg.f_fields)
     143             :     , f_null(null_buffer::pointer_t())
     144             :     , f_saved_buffer(nullptr)
     145           8 :     , f_copy(true)
     146             : {
     147           2 :     *this << m.rdbuf();
     148           2 : }
     149             : 
     150             : 
     151      198334 : message::~message()
     152             : {
     153       99167 :     if(f_saved_buffer != nullptr)
     154             :     {
     155       50919 :         std::ostream & ref = *this;
     156       50919 :         ref.rdbuf(f_saved_buffer);
     157             :     }
     158       99167 : }
     159             : 
     160             : 
     161           0 : severity_t message::default_severity()
     162             : {
     163           0 :     return logger::get_instance()->get_default_severity();
     164             : }
     165             : 
     166             : 
     167       33561 : void message::set_severity(severity_t severity)
     168             : {
     169       33561 :     f_severity = severity;
     170       33561 : }
     171             : 
     172             : 
     173           1 : void message::set_filename(std::string const & filename)
     174             : {
     175           1 :     f_filename = filename;
     176           1 : }
     177             : 
     178             : 
     179           1 : void message::set_function(std::string const & funcname)
     180             : {
     181           1 :     f_funcname = funcname;
     182           1 : }
     183             : 
     184             : 
     185           1 : void message::set_line(int line)
     186             : {
     187           1 :     f_line = line;
     188           1 : }
     189             : 
     190             : 
     191          24 : void message::set_recursive_message(bool state) const
     192             : {
     193          24 :     f_recursive_message = state;
     194          24 : }
     195             : 
     196             : 
     197           1 : void message::set_precise_time()
     198             : {
     199           1 :     clock_gettime(CLOCK_REALTIME, &f_timestamp);
     200           1 : }
     201             : 
     202             : 
     203           0 : void message::set_timestamp(timespec const & timestamp)
     204             : {
     205           0 :     f_timestamp = timestamp;
     206           0 : }
     207             : 
     208             : 
     209          15 : bool message::can_add_component(component::pointer_t c) const
     210             : {
     211          15 :     if(c != nullptr)
     212             :     {
     213          15 :         return !c->is_mutually_exclusive(f_components);
     214             :     }
     215             : 
     216           0 :     return false;
     217             : }
     218             : 
     219             : 
     220          15 : void message::add_component(component::pointer_t c)
     221             : {
     222          15 :     if(c != nullptr)
     223             :     {
     224          15 :         if(!can_add_component(c))
     225             :         {
     226             :             throw conflict_error(
     227             :                   "component \""
     228           0 :                 + c->get_name()
     229           0 :                 + "\" cannot be added to this message as it is mutually exclusive with one or more of the other components that were already added to this message.");
     230             :         }
     231             : 
     232          15 :         f_components.insert(c);
     233             :     }
     234          15 : }
     235             : 
     236             : 
     237       99171 : void message::add_field(std::string const & name, std::string const & value)
     238             : {
     239       99171 :     if(!name.empty())
     240             :     {
     241       99171 :         if(name[0] == '_')
     242             :         {
     243             :             throw invalid_parameter(
     244             :                   "field name \""
     245           0 :                 + name
     246           0 :                 + "\" is a system name (whether reserved or already defined) and as such is read-only."
     247           0 :                   " Do not start your field names with an underscore (_).");
     248             :         }
     249             : 
     250       99171 :         f_fields[name] = value;
     251             :     }
     252       99171 : }
     253             : 
     254             : 
     255       48716 : std::shared_ptr<logger> message::get_logger() const
     256             : {
     257       48716 :     return f_logger;
     258             : }
     259             : 
     260             : 
     261      198408 : severity_t message::get_severity() const
     262             : {
     263      198408 :     return f_severity;
     264             : }
     265             : 
     266             : 
     267           4 : timespec const & message::get_timestamp() const
     268             : {
     269           4 :     return f_timestamp;
     270             : }
     271             : 
     272             : 
     273           2 : std::string const & message::get_filename() const
     274             : {
     275           2 :     return f_filename;
     276             : }
     277             : 
     278             : 
     279           2 : std::string const & message::get_function() const
     280             : {
     281           2 :     return f_funcname;
     282             : }
     283             : 
     284             : 
     285           2 : int message::get_line() const
     286             : {
     287           2 :     return f_line;
     288             : }
     289             : 
     290             : 
     291       48537 : bool message::get_recursive_message() const
     292             : {
     293       48537 :     return f_recursive_message;
     294             : }
     295             : 
     296             : 
     297           0 : bool message::has_component(component::pointer_t c) const
     298             : {
     299           0 :     return f_components.find(c) != f_components.end();
     300             : }
     301             : 
     302             : 
     303      147711 : component::set_t const & message::get_components() const
     304             : {
     305      147711 :     return f_components;
     306             : }
     307             : 
     308             : 
     309          47 : environment::pointer_t message::get_environment() const
     310             : {
     311          47 :     return f_environment;
     312             : }
     313             : 
     314             : 
     315       48535 : std::string message::get_message() const
     316             : {
     317       48535 :     std::string s(str());
     318             : 
     319       97070 :     if(!s.empty()
     320       48535 :     && s.back() == '\n')
     321             :     {
     322           4 :         s.pop_back();
     323             :     }
     324             : 
     325       97070 :     if(!s.empty()
     326       48535 :     && s.back() == '\r')
     327             :     {
     328           2 :         s.pop_back();
     329             :     }
     330             : 
     331       48535 :     return s;
     332             : }
     333             : 
     334             : 
     335           0 : char const * message::get_system_field_name(system_field_t field)
     336             : {
     337           0 :     std::size_t const idx(static_cast<std::size_t>(field));
     338           0 :     if(idx >= static_cast<std::size_t>(system_field_t::SYSTEM_FIELD_max))
     339             :     {
     340           0 :         return "_unknown";
     341             :     }
     342           0 :     return g_system_field_names[idx];
     343             : }
     344             : 
     345             : 
     346           0 : system_field_t message::get_system_field_from_name(std::string const & name)
     347             : {
     348           0 :     if(name.length() >= 2
     349           0 :     && name[0] == '_')
     350             :     {
     351           0 :         switch(name[1])
     352             :         {
     353           0 :         case 'f':
     354           0 :             if(name == "_filename")
     355             :             {
     356           0 :                 return system_field_t::SYSTEM_FIELD_FILENAME;
     357             :             }
     358           0 :             if(name == "_function_name")
     359             :             {
     360           0 :                 return system_field_t::SYSTEM_FIELD_FUNCTION_NAME;
     361             :             }
     362           0 :             break;
     363             : 
     364           0 :         case 'l':
     365           0 :             if(name == "_line")
     366             :             {
     367           0 :                 return system_field_t::SYSTEM_FIELD_LINE;
     368             :             }
     369           0 :             break;
     370             : 
     371           0 :         case 'm':
     372           0 :             if(name == "_message")
     373             :             {
     374           0 :                 return system_field_t::SYSTEM_FIELD_MESSAGE;
     375             :             }
     376           0 :             break;
     377             : 
     378           0 :         case 's':
     379           0 :             if(name == "_severity")
     380             :             {
     381           0 :                 return system_field_t::SYSTEM_FIELD_SEVERITY;
     382             :             }
     383           0 :             break;
     384             : 
     385           0 :         case 't':
     386           0 :             if(name == "_timestamp")
     387             :             {
     388           0 :                 return system_field_t::SYSTEM_FIELD_TIMESTAMP;
     389             :             }
     390           0 :             break;
     391             : 
     392             :         }
     393             :     }
     394             : 
     395           0 :     return system_field_t::SYSTEM_FIELD_UNDEFINED;
     396             : }
     397             : 
     398             : 
     399           2 : std::string message::get_field(std::string const & name) const
     400             : {
     401           4 :     if(!name.empty()
     402           2 :     && name[0] == '_')
     403             :     {
     404           0 :         switch(get_system_field_from_name(name))
     405             :         {
     406           0 :         case system_field_t::SYSTEM_FIELD_MESSAGE:
     407           0 :             return get_message();
     408             : 
     409           0 :         case system_field_t::SYSTEM_FIELD_TIMESTAMP:
     410             :             // TODO: offer ways to get the date & time converted to strings
     411             :             {
     412           0 :                 std::string timestamp(std::to_string(f_timestamp.tv_sec));
     413           0 :                 if(f_timestamp.tv_nsec != 0)
     414             :                 {
     415           0 :                     std::string nsec(std::to_string(f_timestamp.tv_nsec));
     416           0 :                     while(nsec.length() < 9)
     417             :                     {
     418           0 :                         nsec = '0' + nsec;
     419             :                     }
     420           0 :                     while(nsec.back() == '0')
     421             :                     {
     422           0 :                         nsec.pop_back();
     423             :                     }
     424           0 :                     timestamp += '.';
     425           0 :                     timestamp += nsec;
     426             :                 }
     427           0 :                 return timestamp;
     428             :             }
     429             : 
     430           0 :         case system_field_t::SYSTEM_FIELD_SEVERITY:
     431             :             {
     432           0 :                 severity::pointer_t sev(snaplogger::get_severity(f_severity));
     433           0 :                 return sev == nullptr ? "<unknown>" : sev->get_name();
     434             :             }
     435             : 
     436           0 :         case system_field_t::SYSTEM_FIELD_FILENAME:
     437           0 :             return f_filename;
     438             : 
     439           0 :         case system_field_t::SYSTEM_FIELD_FUNCTION_NAME:
     440           0 :             return f_funcname;
     441             : 
     442           0 :         case system_field_t::SYSTEM_FIELD_LINE:
     443           0 :             return std::to_string(f_line);
     444             : 
     445           0 :         default:
     446           0 :             return std::string();
     447             : 
     448             :         }
     449             :     }
     450             : 
     451           2 :     auto it(f_fields.find(name));
     452           2 :     if(it == f_fields.end())
     453             :     {
     454           0 :         return std::string();
     455             :     }
     456           2 :     return it->second;
     457             : }
     458             : 
     459             : 
     460           1 : field_map_t message::get_fields() const
     461             : {
     462           1 :     return f_fields;
     463             : }
     464             : 
     465             : 
     466       65594 : message::pointer_t create_message(
     467             :       severity_t sev
     468             :     , char const * file
     469             :     , char const * func
     470             :     , int line)
     471             : {
     472       65594 :     return std::make_shared<message>(sev, file, func, line);
     473             : }
     474             : 
     475             : 
     476       99158 : void send_message(std::basic_ostream<char> & out)
     477             : {
     478       99158 :     message * msg(dynamic_cast<message *>(&out));
     479       99158 :     if(msg == nullptr)
     480             :     {
     481           1 :         throw not_a_message("the 'out' parameter to the send_message() function is expected to be a snaplogger::message object.");
     482             :     }
     483             : 
     484       99158 :     logger::get_instance()->log_message(*msg);
     485       99156 : }
     486             : 
     487             : 
     488           0 : void send_stack_trace(libexcept::exception_base_t const & e)
     489             : {
     490           0 :     libexcept::stack_trace_t const & stack(e.get_stack_trace());
     491           0 :     for(auto const & s : stack)
     492             :     {
     493           0 :         SNAP_LOG_EXCEPTION
     494             :             << s
     495             :             << SNAP_LOG_SEND;
     496             :     }
     497           0 : }
     498             : 
     499             : 
     500           6 : } // snaplogger namespace
     501             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13