Line data Source code
1 : /*
2 : * Copyright (c) 2013-2019 Made to Order Software Corp. All Rights Reserved
3 : *
4 : * https://snapwebsites.org/project/snaplogger
5 : * contact@m2osw.com
6 : *
7 : * This program is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU General Public License as published by
9 : * the Free Software Foundation; either version 2 of the License, or
10 : * (at your option) any later version.
11 : *
12 : * This program is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU General Public License along
18 : * with this program; if not, write to the Free Software Foundation, Inc.,
19 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 : */
21 :
22 : /** \file
23 : * \brief Implementation of the date, time, locale variables.
24 : *
25 : * This file implements a set of variables offering ways to display the
26 : * message timestamp date and time.
27 : */
28 :
29 : // self
30 : //
31 : #include "snaplogger/exception.h"
32 : #include "snaplogger/variable.h"
33 :
34 :
35 : // last include
36 : //
37 : #include <snapdev/poison.h>
38 :
39 :
40 :
41 : namespace snaplogger
42 : {
43 :
44 :
45 : namespace
46 : {
47 :
48 :
49 : constexpr char const * g_day_name[] =
50 : {
51 : "Sunday",
52 : "Monday",
53 : "Tuesday",
54 : "Wednesday",
55 : "Thursday",
56 : "Friday",
57 : "Saturday"
58 : };
59 :
60 : constexpr char const * g_month_name[] =
61 : {
62 : "January",
63 : "February",
64 : "Marsh",
65 : "April",
66 : "May",
67 : "June",
68 : "July",
69 : "August",
70 : "September",
71 : "October",
72 : "November",
73 : "December"
74 : };
75 :
76 4 : timespec const g_start_date = []() { timespec tp; clock_gettime(CLOCK_REALTIME_COARSE, &tp); return tp; }();
77 :
78 :
79 :
80 :
81 :
82 23 : DEFINE_LOGGER_VARIABLE(date)
83 : {
84 : tm t;
85 3 : timespec const & timestamp(msg.get_timestamp());
86 3 : gmtime_r(×tamp.tv_sec, &t);
87 :
88 6 : std::string date_format("%Y/%m/%d");
89 6 : auto params(get_params());
90 3 : if(!params.empty())
91 : {
92 6 : auto const p(params[0]->get_name());
93 3 : if(p == "day")
94 : {
95 1 : date_format = "%d";
96 : }
97 2 : else if(p == "day_of_week_name")
98 : {
99 0 : value += g_day_name[t.tm_wday];
100 0 : date_format.clear();
101 : }
102 2 : else if(p == "day_of_week")
103 : {
104 0 : date_format = "%w"; // TBD: people may want %u though...
105 : }
106 2 : else if(p == "year_week")
107 : {
108 0 : date_format = "%U"; // TBD: people may want %V or %W though...
109 : }
110 2 : else if(p == "year_day")
111 : {
112 0 : date_format = "%j";
113 : }
114 2 : else if(p == "month_name")
115 : {
116 0 : value += g_month_name[t.tm_mon];
117 0 : date_format.clear();
118 : }
119 2 : else if(p == "month")
120 : {
121 1 : date_format = "%m";
122 : }
123 1 : else if(p == "year")
124 : {
125 1 : date_format = "%Y";
126 : }
127 : else
128 : {
129 0 : throw invalid_variable("the ${hostbyname:...} variable first parameter must be its name parameter.");
130 : }
131 : }
132 :
133 3 : if(!date_format.empty())
134 : {
135 : char buf[256];
136 3 : strftime(buf, sizeof(buf), date_format.c_str(), &t);
137 3 : buf[sizeof(buf) - 1] = '\0';
138 3 : if(buf[0] == '0')
139 : {
140 0 : int n(0);
141 0 : for(; buf[n] == '0'; ++n);
142 0 : if(buf[n] == '\0')
143 : {
144 : // keep at least one zero
145 : //
146 0 : value += '0';
147 : }
148 : else
149 : {
150 0 : value += buf + n;
151 : }
152 : }
153 : else
154 : {
155 3 : value += buf;
156 : }
157 : }
158 :
159 3 : variable::process_value(msg, value);
160 3 : }
161 :
162 :
163 11 : DEFINE_LOGGER_VARIABLE(time)
164 : {
165 0 : auto nanosec = [](timespec const & tp)
166 : {
167 0 : std::string sec(std::to_string(tp.tv_sec));
168 0 : std::string nsec(std::to_string(tp.tv_nsec));
169 0 : if(nsec.length() < 9)
170 : {
171 0 : nsec = std::string("000000000").substr(0, 9 - nsec.length()) + nsec;
172 : }
173 0 : return sec + nsec;
174 : };
175 :
176 : tm t;
177 0 : timespec timestamp(msg.get_timestamp());
178 0 : gmtime_r(×tamp.tv_sec, &t);
179 :
180 0 : std::string time_format("%H:%M:%S");
181 0 : auto params(get_params());
182 0 : if(!params.empty())
183 : {
184 0 : auto const p(params[0]->get_name());
185 0 : if(p == "hour")
186 : {
187 0 : if(params[0]->get_value() == "12")
188 : {
189 0 : time_format = "%I";
190 : }
191 : else
192 : {
193 0 : time_format = "%H";
194 : }
195 : }
196 0 : else if(p == "minute")
197 : {
198 0 : time_format = "%M"; // TBD: people may want %u though...
199 : }
200 0 : else if(p == "second")
201 : {
202 0 : time_format = "%S"; // TBD: people may want %V or %W though...
203 : }
204 0 : else if(p == "nanosecond")
205 : {
206 0 : value += std::to_string(timestamp.tv_nsec);
207 0 : time_format.clear();
208 : }
209 0 : else if(p == "unix")
210 : {
211 0 : value += std::to_string(timestamp.tv_sec);
212 0 : time_format.clear();
213 : }
214 0 : else if(p == "meridiem")
215 : {
216 : // TODO: we should force English here (AM or PM)
217 : //
218 0 : time_format = "%p";
219 : }
220 0 : else if(p == "offset")
221 : {
222 : timespec offset;
223 0 : offset.tv_nsec = timestamp.tv_nsec - g_start_date.tv_nsec;
224 0 : offset.tv_sec = timestamp.tv_sec - g_start_date.tv_sec;
225 0 : if(offset.tv_nsec < 0)
226 : {
227 0 : --offset.tv_sec;
228 0 : offset.tv_nsec += 1000000000;
229 : }
230 0 : value += nanosec(offset);
231 0 : time_format.clear();
232 : }
233 0 : else if(p == "process")
234 : {
235 0 : clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ×tamp);
236 0 : value += nanosec(timestamp);
237 0 : time_format.clear();
238 : }
239 0 : else if(p == "thread")
240 : {
241 0 : clock_gettime(CLOCK_THREAD_CPUTIME_ID, ×tamp);
242 0 : value += nanosec(timestamp);
243 0 : time_format.clear();
244 : }
245 0 : else if(p == "process_ms")
246 : {
247 0 : clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ×tamp);
248 0 : gmtime_r(×tamp.tv_sec, &t);
249 0 : std::string ms(std::to_string(timestamp.tv_nsec / 1000000UL));
250 0 : if(ms.length() < 3)
251 : {
252 0 : ms = std::string("000").substr(0, 3 - ms.length());
253 : }
254 0 : time_format += ".";
255 0 : time_format += ms;
256 : }
257 0 : else if(p == "thread_ms")
258 : {
259 0 : clock_gettime(CLOCK_THREAD_CPUTIME_ID, ×tamp);
260 0 : gmtime_r(×tamp.tv_sec, &t);
261 0 : std::string ms(std::to_string(timestamp.tv_nsec / 1000000UL));
262 0 : if(ms.length() < 3)
263 : {
264 0 : ms = std::string("000").substr(0, 3 - ms.length());
265 : }
266 0 : time_format += ".";
267 0 : time_format += ms;
268 : }
269 : else
270 : {
271 0 : throw invalid_variable("the ${hostbyname:...} variable first parameter must be its name parameter.");
272 : }
273 : }
274 :
275 0 : if(!time_format.empty())
276 : {
277 : char buf[256];
278 0 : strftime(buf, sizeof(buf), time_format.c_str(), &t);
279 0 : buf[sizeof(buf) - 1] = '\0';
280 0 : value += buf;
281 : }
282 :
283 :
284 0 : variable::process_value(msg, value);
285 0 : }
286 :
287 :
288 8 : DEFINE_LOGGER_VARIABLE(locale)
289 : {
290 : tm t;
291 0 : timespec const & timestamp(msg.get_timestamp());
292 0 : localtime_r(×tamp.tv_sec, &t);
293 :
294 0 : std::string locale_format("%c");
295 0 : auto params(get_params());
296 0 : if(!params.empty())
297 : {
298 0 : auto const p(params[0]->get_name());
299 0 : if(p == "day_of_week_name")
300 : {
301 0 : locale_format = "%A"; // TBD: support %a as well
302 : }
303 0 : else if(p == "month_name")
304 : {
305 0 : locale_format = "%B"; // TBD: support %b as well
306 : }
307 0 : else if(p == "date")
308 : {
309 0 : locale_format = "%x";
310 : }
311 0 : else if(p == "time")
312 : {
313 0 : locale_format = "%X";
314 : }
315 0 : else if(p == "meridiem")
316 : {
317 0 : locale_format = "%p";
318 : }
319 0 : else if(p == "timezone")
320 : {
321 0 : locale_format = "%Z";
322 : }
323 0 : else if(p == "timezone_offset")
324 : {
325 0 : locale_format = "%z";
326 : }
327 : // else -- assume params are system parameters
328 : }
329 :
330 0 : if(!locale_format.empty())
331 : {
332 : char buf[256];
333 0 : strftime(buf, sizeof(buf), locale_format.c_str(), &t);
334 0 : buf[sizeof(buf) - 1] = '\0';
335 0 : value += buf;
336 : }
337 :
338 0 : variable::process_value(msg, value);
339 0 : }
340 :
341 :
342 : }
343 : // no name namespace
344 :
345 :
346 6 : } // snaplogger namespace
347 : // vim: ts=4 sw=4 et
|