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