LCOV - code coverage report
Current view: top level - snaplogger - message.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 59.1 % 215 127
Test Date: 2025-07-26 11:53:05 Functions: 81.1 % 37 30
Legend: Lines: hit not hit

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

Generated by: LCOV version 2.0-1

Snap C++ | List of projects | List of versions