LCOV - code coverage report
Current view: top level - snaplogger - logger_variable.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 67.8 % 264 179
Test Date: 2025-07-26 11:53:05 Functions: 67.5 % 80 54
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 Implementation of the various logger variable support.
      22              :  *
      23              :  * This file implements the few logger variables which retrieves their
      24              :  * value from the message variable.
      25              :  */
      26              : 
      27              : 
      28              : // self
      29              : //
      30              : #include    "snaplogger/exception.h"
      31              : #include    "snaplogger/format.h"
      32              : #include    "snaplogger/map_diagnostic.h"
      33              : #include    "snaplogger/nested_diagnostic.h"
      34              : #include    "snaplogger/syslog_appender.h"
      35              : #include    "snaplogger/trace_diagnostic.h"
      36              : #include    "snaplogger/variable.h"
      37              : 
      38              : 
      39              : // C++
      40              : //
      41              : #include    <iostream>
      42              : 
      43              : 
      44              : // last include
      45              : //
      46              : #include    <snapdev/poison.h>
      47              : 
      48              : 
      49              : 
      50              : namespace snaplogger
      51              : {
      52              : 
      53              : 
      54              : namespace
      55              : {
      56              : 
      57              : 
      58              : 
      59           55 : DEFINE_LOGGER_VARIABLE(severity)
      60              : {
      61              :     enum class format_t
      62              :     {
      63              :         FORMAT_ALPHA,
      64              :         FORMAT_NUMBER,
      65              :         FORMAT_SYSTEMD,
      66              :     };
      67              : 
      68           34 :     format_t format(format_t::FORMAT_ALPHA);
      69              : 
      70           34 :     auto params(get_params());
      71           34 :     if(!params.empty())
      72              :     {
      73           26 :         if(params[0]->get_name() == "format")
      74              :         {
      75           26 :             auto v(params[0]->get_value());
      76           26 :             if(v == "alpha")
      77              :             {
      78            9 :                 format = format_t::FORMAT_ALPHA;
      79              :             }
      80           17 :             else if(v == "number")
      81              :             {
      82            7 :                 format = format_t::FORMAT_NUMBER;
      83              :             }
      84           10 :             else if(v == "systemd")
      85              :             {
      86            9 :                 format = format_t::FORMAT_SYSTEMD;
      87              :             }
      88              :             else
      89              :             {
      90              :                 throw invalid_variable(
      91              :                               "the ${severity:format=alpha|number|systemd} variable cannot be set to \""
      92            2 :                             + v
      93            3 :                             + "\".");
      94              :             }
      95           26 :         }
      96              :     }
      97              : 
      98           33 :     severity_t sev(msg.get_severity());
      99           33 :     switch(format)
     100              :     {
     101           17 :     case format_t::FORMAT_ALPHA:
     102              :         {
     103           17 :             severity::pointer_t severity(get_severity(msg, sev));
     104           17 :             if(severity != nullptr)
     105              :             {
     106           17 :                 value += severity->get_description();
     107           17 :                 break;
     108              :             }
     109           17 :         }
     110              :         [[fallthrough]];
     111              :     case format_t::FORMAT_NUMBER:
     112            7 :         value += std::to_string(static_cast<int>(sev));
     113            7 :         break;
     114              : 
     115            9 :     case format_t::FORMAT_SYSTEMD:
     116              :         // see https://www.freedesktop.org/software/systemd/man/sd-daemon.html
     117              :         //
     118            9 :         value += '<';
     119            9 :         value += std::to_string(syslog_appender::message_severity_to_syslog_priority(sev));
     120            9 :         value += '>';
     121            9 :         break;
     122              : 
     123              :     }
     124              : 
     125           33 :     variable::process_value(msg, value);
     126           67 : }
     127              : 
     128              : 
     129              : 
     130        41705 : DEFINE_LOGGER_VARIABLE(message)
     131              : {
     132        41648 :     if(msg.get_recursive_message())
     133              :     {
     134              :         // do nothing if we find a ${message} inside the message itself
     135            2 :         return;
     136              :     }
     137              : 
     138        41646 :     std::string const m(msg.get_message());
     139        41646 :     if(m.find("${") == std::string::npos)
     140              :     {
     141        41634 :         value += m;
     142              :     }
     143              :     else
     144              :     {
     145           12 :         msg.set_recursive_message(true);
     146           12 :         format f(m);
     147           12 :         value += f.process_message(msg);
     148           12 :         msg.set_recursive_message(false);
     149           12 :     }
     150              : 
     151        41646 :     variable::process_value(msg, value);
     152        41646 : }
     153              : 
     154              : 
     155              : 
     156           12 : DEFINE_LOGGER_VARIABLE_IGNORED_ON_NO_REPEAT(field)
     157              : {
     158            2 :     auto params(get_params());
     159            2 :     if(!params.empty())
     160              :     {
     161            2 :         if(params[0]->get_name() == "name")
     162              :         {
     163            2 :             auto name(params[0]->get_value());
     164            2 :             value += msg.get_field(name);
     165            2 :         }
     166              :     }
     167              : 
     168            2 :     variable::process_value(msg, value);
     169            4 : }
     170              : 
     171              : 
     172              : 
     173           10 : DEFINE_LOGGER_VARIABLE_IGNORED_ON_NO_REPEAT(fields)
     174              : {
     175              :     enum class format_t
     176              :     {
     177              :         FORMAT_JSON,
     178              :         FORMAT_SHELL
     179              :     };
     180              :     enum class json_t
     181              :     {
     182              :         START_COMMA,
     183              :         END_COMMA,
     184              :         OBJECT
     185              :     };
     186              : 
     187            1 :     format_t format(format_t::FORMAT_JSON);
     188            1 :     json_t json(json_t::OBJECT);
     189              : 
     190            1 :     auto params(get_params());
     191            1 :     for(auto p : params)
     192              :     {
     193            0 :         auto const v(p->get_value());
     194            0 :         if(p->get_name() == "format")
     195              :         {
     196            0 :             if(v == "json")
     197              :             {
     198            0 :                 format = format_t::FORMAT_JSON;
     199              :             }
     200            0 :             else if(v == "shell")
     201              :             {
     202            0 :                 format = format_t::FORMAT_SHELL;
     203              :             }
     204              :             else
     205              :             {
     206              :                 throw invalid_variable(
     207              :                               "the ${fields:format=json|shell} variable cannot be set to \""
     208            0 :                             + v
     209            0 :                             + "\".");
     210              :             }
     211              :         }
     212            0 :         else if(p->get_name() == "json")
     213              :         {
     214            0 :             if(v == "start-comma")
     215              :             {
     216            0 :                 json = json_t::START_COMMA;
     217              :             }
     218            0 :             else if(v == "end-comma")
     219              :             {
     220            0 :                 json = json_t::END_COMMA;
     221              :             }
     222            0 :             else if(v == "object")
     223              :             {
     224            0 :                 json = json_t::OBJECT;
     225              :             }
     226              :             else
     227              :             {
     228              :                 throw invalid_variable(
     229              :                               "the ${fields:json=start-comma|end-comma|object} variable cannot be set to \""
     230            0 :                             + v
     231            0 :                             + "\".");
     232              :             }
     233              :         }
     234            0 :     }
     235              : 
     236            1 :     int start_comma(0);
     237            1 :     switch(format)
     238              :     {
     239            1 :     case format_t::FORMAT_JSON:
     240            1 :         if(json == json_t::OBJECT)
     241              :         {
     242            1 :             value += "{";
     243            1 :             start_comma = 1;
     244              :         }
     245            1 :         break;
     246              : 
     247            0 :     case format_t::FORMAT_SHELL:
     248              :         // nothing for this one
     249            0 :         break;
     250              : 
     251              :     }
     252              : 
     253            4 :     for(auto f : msg.get_fields())
     254              :     {
     255            3 :         switch(format)
     256              :         {
     257            3 :         case format_t::FORMAT_JSON:
     258            3 :             if(json == json_t::START_COMMA || start_comma == 2)
     259              :             {
     260            2 :                 value += ",";
     261              :             }
     262              :             // TODO: need to test for quotes inside "first" or "second"
     263            3 :             value += "\"" + f.first + "\":" + "\"" + f.second + "\"";
     264            3 :             if(json == json_t::END_COMMA)
     265              :             {
     266            0 :                 value += ",";
     267              :             }
     268            3 :             else if(json == json_t::OBJECT)
     269              :             {
     270              :                 // if more than one field, make sure to add commas
     271              :                 // between each field
     272              :                 //
     273            3 :                 start_comma = 2;
     274              :             }
     275            3 :             break;
     276              : 
     277            0 :         case format_t::FORMAT_SHELL:
     278            0 :             value += f.first + "=" + f.second + "\n";
     279            0 :             break;
     280              : 
     281              :         }
     282            4 :     }
     283            1 :     switch(format)
     284              :     {
     285            1 :     case format_t::FORMAT_JSON:
     286            1 :         if(json == json_t::OBJECT)
     287              :         {
     288            1 :             value += "}";
     289              :         }
     290            1 :         break;
     291              : 
     292            0 :     case format_t::FORMAT_SHELL:
     293              :         // nothing for this one
     294            0 :         break;
     295              : 
     296              :     }
     297              : 
     298            1 :     variable::process_value(msg, value);
     299            2 : }
     300              : 
     301              : 
     302              : 
     303           25 : DEFINE_LOGGER_VARIABLE(project_name)
     304              : {
     305              :     // when the advgetopt is properly connected to the logger, then the
     306              :     // logger will save the project name in its diagnostic map
     307              :     //
     308           11 :     map_diagnostics_t map(get_map_diagnostics(msg));
     309           33 :     auto it(map.find(DIAG_KEY_PROJECT_NAME));
     310           11 :     if(it != map.end())
     311              :     {
     312           11 :         value += it->second;
     313              :     }
     314              : 
     315           11 :     variable::process_value(msg, value);
     316           22 : }
     317              : 
     318              : 
     319              : 
     320           13 : DEFINE_LOGGER_VARIABLE(progname)
     321              : {
     322              :     // when the advgetopt is properly connected to the logger, then the
     323              :     // logger will save the program name in its diagnostic map
     324              :     //
     325            3 :     map_diagnostics_t map(get_map_diagnostics(msg));
     326            9 :     auto it(map.find(DIAG_KEY_PROGNAME));
     327            3 :     if(it != map.end())
     328              :     {
     329            3 :         value += it->second;
     330              :     }
     331              : 
     332            3 :     variable::process_value(msg, value);
     333            6 : }
     334              : 
     335              : 
     336              : 
     337           29 : DEFINE_LOGGER_VARIABLE(version)
     338              : {
     339              :     // when the advgetopt is properly connected to the logger, then the
     340              :     // logger will save the program version in its diagnostic map
     341              :     //
     342           14 :     map_diagnostics_t map(get_map_diagnostics(msg));
     343           42 :     auto it(map.find(DIAG_KEY_VERSION));
     344           14 :     if(it != map.end())
     345              :     {
     346           12 :         value += it->second;
     347              :     }
     348              : 
     349           14 :     variable::process_value(msg, value);
     350           28 : }
     351              : 
     352              : 
     353              : 
     354            8 : DEFINE_LOGGER_VARIABLE(build_date)
     355              : {
     356            0 :     map_diagnostics_t map(get_map_diagnostics(msg));
     357            0 :     auto it(map.find(DIAG_KEY_BUILD_DATE));
     358            0 :     if(it != map.end())
     359              :     {
     360            0 :         value += it->second;
     361              :     }
     362              : 
     363            0 :     variable::process_value(msg, value);
     364            0 : }
     365              : 
     366              : 
     367              : 
     368            8 : DEFINE_LOGGER_VARIABLE(build_time)
     369              : {
     370            0 :     map_diagnostics_t map(get_map_diagnostics(msg));
     371            0 :     auto it(map.find(DIAG_KEY_BUILD_TIME));
     372            0 :     if(it != map.end())
     373              :     {
     374            0 :         value += it->second;
     375              :     }
     376              : 
     377            0 :     variable::process_value(msg, value);
     378            0 : }
     379              : 
     380              : 
     381              : 
     382            8 : DEFINE_LOGGER_VARIABLE(filename)
     383              : {
     384            0 :     value += msg.get_filename();
     385              : 
     386            0 :     variable::process_value(msg, value);
     387            0 : }
     388              : 
     389              : 
     390              : 
     391            9 : DEFINE_LOGGER_VARIABLE(basename)
     392              : {
     393            0 :     std::string const filename(msg.get_filename());
     394            0 :     std::string::size_type const pos(filename.rfind('/'));
     395            0 :     if(pos == std::string::npos)
     396              :     {
     397            0 :         value += filename;
     398              :     }
     399              :     else
     400              :     {
     401            0 :         value += filename.substr(pos + 1);
     402              :     }
     403              : 
     404            0 :     variable::process_value(msg, value);
     405            0 : }
     406              : 
     407              : 
     408              : 
     409            8 : DEFINE_LOGGER_VARIABLE(path)
     410              : {
     411            0 :     std::string const filename(msg.get_filename());
     412            0 :     std::string::size_type const pos(filename.rfind('/'));
     413            0 :     if(pos != std::string::npos)
     414              :     {
     415            0 :         value += filename.substr(0, pos);
     416              :     }
     417              : 
     418            0 :     variable::process_value(msg, value);
     419            0 : }
     420              : 
     421              : 
     422              : 
     423            9 : DEFINE_LOGGER_VARIABLE(function)
     424              : {
     425            0 :     value += msg.get_function();
     426              : 
     427            0 :     variable::process_value(msg, value);
     428            0 : }
     429              : 
     430              : 
     431              : 
     432           11 : DEFINE_LOGGER_VARIABLE(line)
     433              : {
     434            1 :     value += std::to_string(msg.get_line());
     435              : 
     436            1 :     variable::process_value(msg, value);
     437            1 : }
     438              : 
     439              : 
     440              : 
     441           26 : DEFINE_LOGGER_VARIABLE(diagnostic)
     442              : {
     443            9 :     constexpr int FLAG_NESTED = 0x01;
     444            9 :     constexpr int FLAG_MAP    = 0x02;
     445            9 :     constexpr int FLAG_TRACE  = 0x04;
     446              : 
     447            9 :     std::int64_t nested_depth(-1);
     448            9 :     std::int64_t trace_count(-1);
     449            9 :     std::string key;
     450            9 :     int flags(0);
     451              : 
     452           18 :     for(auto const & p : get_params())
     453              :     {
     454            9 :         if(p->get_name() == "nested")
     455              :         {
     456            4 :             flags |= FLAG_NESTED;
     457              : 
     458              :             // the integer is optional
     459              :             //
     460            4 :             if(!p->empty())
     461              :             {
     462            4 :                 nested_depth = p->get_integer();
     463              :             }
     464              :         }
     465            5 :         else if(p->get_name() == "map")
     466              :         {
     467            5 :             flags |= FLAG_MAP;
     468            5 :             key = p->get_value();
     469              :         }
     470            0 :         else if(p->get_name() == "trace")
     471              :         {
     472            0 :             flags |= FLAG_TRACE;
     473              : 
     474              :             // the integer is optional
     475              :             //
     476            0 :             if(!p->empty())
     477              :             {
     478            0 :                 trace_count = p->get_integer();
     479              :             }
     480              :         }
     481            9 :     }
     482              : 
     483            9 :     if(flags == 0
     484            9 :     || (flags & FLAG_NESTED) != 0)
     485              :     {
     486            4 :         char sep('{');
     487            4 :         string_vector_t nested(get_nested_diagnostics(msg));
     488            4 :         size_t idx(0);
     489            8 :         if((flags & FLAG_NESTED) != 0
     490            4 :         && nested_depth != -1
     491            8 :         && static_cast<ssize_t>(nested.size()) > nested_depth)
     492              :         {
     493            1 :             idx = nested.size() - nested_depth;
     494            1 :             value += sep;
     495            1 :             value += "...";
     496            1 :             sep = '/';
     497              :         }
     498              : 
     499           12 :         for(; idx < nested.size(); ++idx)
     500              :         {
     501            8 :             value += sep;
     502            8 :             sep = '/';
     503            8 :             value += nested[idx];
     504              :         }
     505            4 :         value += "}";
     506            4 :     }
     507              : 
     508            9 :     if(flags == 0
     509            9 :     || (flags & FLAG_TRACE) != 0)
     510              :     {
     511            0 :         trace_diagnostics_t trace(get_trace_diagnostics());
     512            0 :         if(trace.empty())
     513              :         {
     514            0 :             value += "[<no trace>]";
     515              :         }
     516              :         else
     517              :         {
     518            0 :             auto it(trace.begin());
     519            0 :             size_t sz(trace.size());
     520            0 :             if((flags & FLAG_TRACE) != 0
     521            0 :             && trace_count > 0)
     522              :             {
     523            0 :                 for(; static_cast<int64_t>(sz) > trace_count && it != trace.end(); ++it, --sz);
     524              :             }
     525              : 
     526            0 :             char sep('[');
     527            0 :             for(; it != trace.end(); ++it)
     528              :             {
     529            0 :                 value += sep;
     530            0 :                 sep = '/';
     531            0 :                 value += *it;
     532              :             }
     533            0 :             value += "]";
     534              :         }
     535            0 :     }
     536              : 
     537            9 :     if(flags == 0
     538            9 :     || (flags & FLAG_MAP) != 0)
     539              :     {
     540            5 :         auto const diagnostics(get_map_diagnostics(msg));
     541            5 :         if(!diagnostics.empty())
     542              :         {
     543            5 :             if(key.empty())
     544              :             {
     545            0 :                 char sep('<');
     546            0 :                 for(auto d : diagnostics)
     547              :                 {
     548            0 :                     value += sep;
     549            0 :                     sep = ':';
     550            0 :                     value += d.first;
     551            0 :                     value += '=';
     552            0 :                     value += d.second;
     553            0 :                 }
     554            0 :                 value += '>';
     555              :             }
     556              :             else
     557              :             {
     558            5 :                 auto it(diagnostics.find(key));
     559            5 :                 if(it != diagnostics.end())
     560              :                 {
     561            5 :                     value += '<';
     562            5 :                     value += key;
     563            5 :                     value += '=';
     564            5 :                     value += it->second;
     565            5 :                     value += '>';
     566              :                 }
     567              :             }
     568              :         }
     569            5 :     }
     570              : 
     571            9 :     variable::process_value(msg, value);
     572           18 : }
     573              : 
     574              : 
     575           12 : DEFINE_LOGGER_VARIABLE(components)
     576              : {
     577            3 :     component::set_t const & components(msg.get_components());
     578            3 :     if(!components.empty())
     579              :     {
     580            3 :         char sep('[');
     581           10 :         for(auto c : components)
     582              :         {
     583            7 :             value += sep;
     584            7 :             sep = ',';
     585            7 :             value += c->get_name();
     586            7 :         }
     587            3 :         value += ']';
     588              : 
     589            3 :         variable::process_value(msg, value);
     590              :     }
     591            3 : }
     592              : 
     593              : 
     594              : }
     595              : // no name namespace
     596              : 
     597              : 
     598              : } // snaplogger namespace
     599              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

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