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 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 4 : timespec const g_start_date = []() { timespec tp; clock_gettime(CLOCK_REALTIME_COARSE, &tp); return tp; }();
75 :
76 :
77 :
78 :
79 :
80 21 : 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(×tamp.tv_sec, &t);
85 :
86 6 : std::string date_format("%Y/%m/%d");
87 6 : auto params(get_params());
88 3 : if(!params.empty())
89 : {
90 6 : 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 : }
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 2 : int n(0);
139 4 : for(; buf[n] == '0'; ++n);
140 2 : if(buf[n] == '\0')
141 : {
142 : // keep at least one zero
143 : //
144 0 : value += '0';
145 : }
146 : else
147 : {
148 2 : value += buf + n;
149 : }
150 : }
151 : else
152 : {
153 1 : value += buf;
154 : }
155 : }
156 :
157 3 : variable::process_value(msg, value);
158 3 : }
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 : };
173 :
174 0 : tm t;
175 0 : timespec timestamp(msg.get_timestamp());
176 0 : gmtime_r(×tamp.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, ×tamp);
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, ×tamp);
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, ×tamp);
256 0 : gmtime_r(×tamp.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 : }
265 0 : else if(p == "thread_ms")
266 : {
267 0 : clock_gettime(CLOCK_THREAD_CPUTIME_ID, ×tamp);
268 0 : gmtime_r(×tamp.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 : }
277 : else
278 : {
279 0 : throw invalid_variable("the ${hostbyname:...} variable first parameter must be its name parameter.");
280 : }
281 : }
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 7 : 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(×tamp.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 : }
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 6 : } // snaplogger namespace
355 : // vim: ts=4 sw=4 et
|