LCOV - code coverage report
Current view: top level - snaplogger - date_variable.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 22.9 % 153 35
Test Date: 2025-07-26 11:53:05 Functions: 58.8 % 17 10
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 date, time, locale variables.
      22              :  *
      23              :  * This file implements a set of variables offering ways to display the
      24              :  * message timestamp date and time.
      25              :  */
      26              : 
      27              : // self
      28              : //
      29              : #include    "snaplogger/exception.h"
      30              : #include    "snaplogger/variable.h"
      31              : 
      32              : 
      33              : // last include
      34              : //
      35              : #include    <snapdev/poison.h>
      36              : 
      37              : 
      38              : 
      39              : namespace snaplogger
      40              : {
      41              : 
      42              : 
      43              : namespace
      44              : {
      45              : 
      46              : 
      47              : constexpr char const * g_day_name[] =
      48              : {
      49              :     "Sunday",
      50              :     "Monday",
      51              :     "Tuesday",
      52              :     "Wednesday",
      53              :     "Thursday",
      54              :     "Friday",
      55              :     "Saturday"
      56              : };
      57              : 
      58              : constexpr char const * g_month_name[] =
      59              : {
      60              :     "January",
      61              :     "February",
      62              :     "Marsh",
      63              :     "April",
      64              :     "May",
      65              :     "June",
      66              :     "July",
      67              :     "August",
      68              :     "September",
      69              :     "October",
      70              :     "November",
      71              :     "December"
      72              : };
      73              : 
      74            2 : timespec const g_start_date = []() { timespec tp; clock_gettime(CLOCK_REALTIME_COARSE, &tp); return tp; }();
      75              : 
      76              : 
      77              : 
      78              : 
      79              : 
      80           15 : DEFINE_LOGGER_VARIABLE_IGNORED_ON_NO_REPEAT(date)
      81              : {
      82            3 :     tm t;
      83            3 :     timespec const & timestamp(msg.get_timestamp());
      84            3 :     gmtime_r(&timestamp.tv_sec, &t);
      85              : 
      86            9 :     std::string date_format("%Y/%m/%d");
      87            3 :     auto params(get_params());
      88            3 :     if(!params.empty())
      89              :     {
      90            3 :         auto const p(params[0]->get_name());
      91            3 :         if(p == "day")
      92              :         {
      93            1 :             date_format = "%d";
      94              :         }
      95            2 :         else if(p == "day_of_week_name")
      96              :         {
      97            0 :             value += g_day_name[t.tm_wday];
      98            0 :             date_format.clear();
      99              :         }
     100            2 :         else if(p == "day_of_week")
     101              :         {
     102            0 :             date_format = "%w"; // TBD: people may want %u though...
     103              :         }
     104            2 :         else if(p == "year_week")
     105              :         {
     106            0 :             date_format = "%U"; // TBD: people may want %V or %W though...
     107              :         }
     108            2 :         else if(p == "year_day")
     109              :         {
     110            0 :             date_format = "%j";
     111              :         }
     112            2 :         else if(p == "month_name")
     113              :         {
     114            0 :             value += g_month_name[t.tm_mon];
     115            0 :             date_format.clear();
     116              :         }
     117            2 :         else if(p == "month")
     118              :         {
     119            1 :             date_format = "%m";
     120              :         }
     121            1 :         else if(p == "year")
     122              :         {
     123            1 :             date_format = "%Y";
     124              :         }
     125              :         else
     126              :         {
     127            0 :             throw invalid_variable("the ${hostbyname:...} variable first parameter must be its name parameter.");
     128              :         }
     129            3 :     }
     130              : 
     131            3 :     if(!date_format.empty())
     132              :     {
     133            3 :         char buf[256];
     134            3 :         strftime(buf, sizeof(buf), date_format.c_str(), &t);
     135            3 :         buf[sizeof(buf) - 1] = '\0';
     136            3 :         if(buf[0] == '0')
     137              :         {
     138            1 :             int n(0);
     139            2 :             for(; buf[n] == '0'; ++n);
     140            1 :             if(buf[n] == '\0')
     141              :             {
     142              :                 // keep at least one zero
     143              :                 //
     144            0 :                 value += '0';
     145              :             }
     146              :             else
     147              :             {
     148            1 :                 value += buf + n;
     149              :             }
     150              :         }
     151              :         else
     152              :         {
     153            2 :             value += buf;
     154              :         }
     155              :     }
     156              : 
     157            3 :     variable::process_value(msg, value);
     158            6 : }
     159              : 
     160              : 
     161            9 : DEFINE_LOGGER_VARIABLE_IGNORED_ON_NO_REPEAT(time)
     162              : {
     163            0 :     auto nanosec = [](timespec const & tp)
     164              :     {
     165            0 :         std::string sec(std::to_string(tp.tv_sec));
     166            0 :         std::string nsec(std::to_string(tp.tv_nsec));
     167            0 :         if(nsec.length() < 9)
     168              :         {
     169            0 :             nsec = std::string("000000000").substr(0, 9 - nsec.length()) + nsec;
     170              :         }
     171            0 :         return sec + nsec;
     172            0 :     };
     173              : 
     174            0 :     tm t;
     175            0 :     timespec timestamp(msg.get_timestamp());
     176            0 :     gmtime_r(&timestamp.tv_sec, &t);
     177              : 
     178            0 :     std::string time_format("%H:%M:%S");
     179            0 :     auto params(get_params());
     180            0 :     if(!params.empty())
     181              :     {
     182            0 :         auto const p(params[0]->get_name());
     183            0 :         if(p == "hour")
     184              :         {
     185            0 :             if(params[0]->get_value() == "12")
     186              :             {
     187            0 :                 time_format = "%I";
     188              :             }
     189              :             else
     190              :             {
     191            0 :                 time_format = "%H";
     192              :             }
     193              :         }
     194            0 :         else if(p == "minute")
     195              :         {
     196            0 :             time_format = "%M"; // TBD: people may want %u though...
     197              :         }
     198            0 :         else if(p == "second")
     199              :         {
     200            0 :             time_format = "%S"; // TBD: people may want %V or %W though...
     201              :         }
     202            0 :         else if(p == "millisecond")
     203              :         {
     204            0 :             value += std::to_string(timestamp.tv_nsec / 1000000LL);
     205            0 :             time_format.clear();
     206              :         }
     207            0 :         else if(p == "microsecond")
     208              :         {
     209            0 :             value += std::to_string(timestamp.tv_nsec / 1000LL);
     210            0 :             time_format.clear();
     211              :         }
     212            0 :         else if(p == "nanosecond")
     213              :         {
     214            0 :             value += std::to_string(timestamp.tv_nsec);
     215            0 :             time_format.clear();
     216              :         }
     217            0 :         else if(p == "unix")
     218              :         {
     219            0 :             value += std::to_string(timestamp.tv_sec);
     220            0 :             time_format.clear();
     221              :         }
     222            0 :         else if(p == "meridiem")
     223              :         {
     224              :             // TODO: we should force English here (AM or PM)
     225              :             //
     226            0 :             time_format = "%p";
     227              :         }
     228            0 :         else if(p == "offset")
     229              :         {
     230            0 :             timespec offset;
     231            0 :             offset.tv_nsec = timestamp.tv_nsec - g_start_date.tv_nsec;
     232            0 :             offset.tv_sec = timestamp.tv_sec - g_start_date.tv_sec;
     233            0 :             if(offset.tv_nsec < 0)
     234              :             {
     235            0 :                 --offset.tv_sec;
     236            0 :                 offset.tv_nsec += 1000000000;
     237              :             }
     238            0 :             value += nanosec(offset);
     239            0 :             time_format.clear();
     240              :         }
     241            0 :         else if(p == "process")
     242              :         {
     243            0 :             clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timestamp);
     244            0 :             value += nanosec(timestamp);
     245            0 :             time_format.clear();
     246              :         }
     247            0 :         else if(p == "thread")
     248              :         {
     249            0 :             clock_gettime(CLOCK_THREAD_CPUTIME_ID, &timestamp);
     250            0 :             value += nanosec(timestamp);
     251            0 :             time_format.clear();
     252              :         }
     253            0 :         else if(p == "process_ms")
     254              :         {
     255            0 :             clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timestamp);
     256            0 :             gmtime_r(&timestamp.tv_sec, &t);
     257            0 :             std::string ms(std::to_string(timestamp.tv_nsec / 1000000UL));
     258            0 :             if(ms.length() < 3)
     259              :             {
     260            0 :                 ms = std::string("000").substr(0, 3 - ms.length());
     261              :             }
     262            0 :             time_format += ".";
     263            0 :             time_format += ms;
     264            0 :         }
     265            0 :         else if(p == "thread_ms")
     266              :         {
     267            0 :             clock_gettime(CLOCK_THREAD_CPUTIME_ID, &timestamp);
     268            0 :             gmtime_r(&timestamp.tv_sec, &t);
     269            0 :             std::string ms(std::to_string(timestamp.tv_nsec / 1000000UL));
     270            0 :             if(ms.length() < 3)
     271              :             {
     272            0 :                 ms = std::string("000").substr(0, 3 - ms.length());
     273              :             }
     274            0 :             time_format += ".";
     275            0 :             time_format += ms;
     276            0 :         }
     277              :         else
     278              :         {
     279            0 :             throw invalid_variable("the ${hostbyname:...} variable first parameter must be its name parameter.");
     280              :         }
     281            0 :     }
     282              : 
     283            0 :     if(!time_format.empty())
     284              :     {
     285            0 :         char buf[256];
     286            0 :         strftime(buf, sizeof(buf), time_format.c_str(), &t);
     287            0 :         buf[sizeof(buf) - 1] = '\0';
     288            0 :         value += buf;
     289              :     }
     290              : 
     291              : 
     292            0 :     variable::process_value(msg, value);
     293            0 : }
     294              : 
     295              : 
     296            8 : DEFINE_LOGGER_VARIABLE_IGNORED_ON_NO_REPEAT(locale)
     297              : {
     298            0 :     tm t;
     299            0 :     timespec const & timestamp(msg.get_timestamp());
     300            0 :     localtime_r(&timestamp.tv_sec, &t);
     301              : 
     302            0 :     std::string locale_format("%c");
     303            0 :     auto params(get_params());
     304            0 :     if(!params.empty())
     305              :     {
     306            0 :         auto const p(params[0]->get_name());
     307            0 :         if(p == "day_of_week_name")
     308              :         {
     309            0 :             locale_format = "%A";   // TBD: support %a as well
     310              :         }
     311            0 :         else if(p == "month_name")
     312              :         {
     313            0 :             locale_format = "%B";     // TBD: support %b as well
     314              :         }
     315            0 :         else if(p == "date")
     316              :         {
     317            0 :             locale_format = "%x";
     318              :         }
     319            0 :         else if(p == "time")
     320              :         {
     321            0 :             locale_format = "%X";
     322              :         }
     323            0 :         else if(p == "meridiem")
     324              :         {
     325            0 :             locale_format = "%p";
     326              :         }
     327            0 :         else if(p == "timezone")
     328              :         {
     329            0 :             locale_format = "%Z";
     330              :         }
     331            0 :         else if(p == "timezone_offset")
     332              :         {
     333            0 :             locale_format = "%z";
     334              :         }
     335              :         // else -- assume params are system parameters
     336            0 :     }
     337              : 
     338            0 :     if(!locale_format.empty())
     339              :     {
     340            0 :         char buf[256];
     341            0 :         strftime(buf, sizeof(buf), locale_format.c_str(), &t);
     342            0 :         buf[sizeof(buf) - 1] = '\0';
     343            0 :         value += buf;
     344              :     }
     345              : 
     346            0 :     variable::process_value(msg, value);
     347            0 : }
     348              : 
     349              : 
     350              : }
     351              : // no name namespace
     352              : 
     353              : 
     354              : } // snaplogger namespace
     355              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

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