Line data Source code
1 : /*
2 : * If not stated otherwise in this file or this component's LICENSE file the
3 : * following copyright and licenses apply:
4 : *
5 : * Copyright 2022 Sky UK
6 : *
7 : * Licensed under the Apache License, Version 2.0 (the "License");
8 : * you may not use this file except in compliance with the License.
9 : * You may obtain a copy of the License at
10 : *
11 : * http://www.apache.org/licenses/LICENSE-2.0
12 : *
13 : * Unless required by applicable law or agreed to in writing, software
14 : * distributed under the License is distributed on an "AS IS" BASIS,
15 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 : * See the License for the specific language governing permissions and
17 : * limitations under the License.
18 : */
19 :
20 : #include <atomic>
21 : #include <cstdarg>
22 : #include <cstdio>
23 : #include <cstring>
24 : #include <ctime>
25 : #include <mutex>
26 : #include <sys/syscall.h>
27 : #include <sys/uio.h>
28 : #include <unistd.h>
29 :
30 : #include "EnvVariableParser.h"
31 : #include "LogFileHandle.h"
32 : #include "RialtoLogging.h"
33 :
34 : #ifdef USE_ETHANLOG
35 :
36 : #include <ethanlog.h>
37 :
38 : #define SYSTEM_LOG_FATAL(filename, function, line, ...) ethanlog(ETHAN_LOG_FATAL, filename, function, line, __VA_ARGS__)
39 : #define SYSTEM_LOG_ERROR(filename, function, line, ...) ethanlog(ETHAN_LOG_ERROR, filename, function, line, __VA_ARGS__)
40 : #define SYSTEM_LOG_WARN(filename, function, line, ...) \
41 : ethanlog(ETHAN_LOG_WARNING, filename, function, line, __VA_ARGS__)
42 : #define SYSTEM_LOG_MIL(filename, function, line, ...) \
43 : ethanlog(ETHAN_LOG_MILESTONE, filename, function, line, __VA_ARGS__)
44 : #define SYSTEM_LOG_INFO(filename, function, line, ...) ethanlog(ETHAN_LOG_INFO, filename, function, line, __VA_ARGS__)
45 : #define SYSTEM_LOG_DEBUG(filename, function, line, ...) ethanlog(ETHAN_LOG_DEBUG, filename, function, line, __VA_ARGS__)
46 :
47 : #else
48 :
49 : #include <syslog.h>
50 :
51 : #define SYSTEM_LOG_FATAL(filename, function, line, ...) syslog(LOG_CRIT, __VA_ARGS__)
52 : #define SYSTEM_LOG_ERROR(filename, function, line, ...) syslog(LOG_ERR, __VA_ARGS__)
53 : #define SYSTEM_LOG_WARN(filename, function, line, ...) syslog(LOG_WARNING, __VA_ARGS__)
54 : #define SYSTEM_LOG_MIL(filename, function, line, ...) syslog(LOG_NOTICE, __VA_ARGS__)
55 : #define SYSTEM_LOG_INFO(filename, function, line, ...) syslog(LOG_INFO, __VA_ARGS__)
56 : #define SYSTEM_LOG_DEBUG(filename, function, line, ...) syslog(LOG_DEBUG, __VA_ARGS__)
57 :
58 : #endif
59 :
60 : namespace
61 : {
62 : /**
63 : * Log levels for each component. By default will print all fatals, errors, warnings & milestones.
64 : */
65 : std::atomic<RIALTO_DEBUG_LEVEL> g_rialtoLogLevels[RIALTO_COMPONENT_LAST] = {};
66 :
67 : /**
68 : * Default Log levels defined by RIALTO_DEBUG environment variable
69 : */
70 : const firebolt::rialto::logging::EnvVariableParser g_envVariableParser;
71 :
72 : /**
73 : * Log handler for each component. By default will use journaldLogHandler.
74 : */
75 : void fdLogHandler(int fd, RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, int line,
76 : const char *function, const char *message, size_t messageLen);
77 : void journaldLogHandler(RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, int line,
78 : const char *function, const char *message, size_t messageLen);
79 :
80 : firebolt::rialto::logging::LogHandler g_logHandler[RIALTO_COMPONENT_LAST] = {};
81 : bool g_ignoreLogLevels[RIALTO_COMPONENT_LAST] = {};
82 : std::mutex g_logHandlerMutex;
83 :
84 : std::string componentToString(RIALTO_COMPONENT component);
85 : std::string levelToString(RIALTO_DEBUG_LEVEL level);
86 :
87 9167 : std::string componentToString(RIALTO_COMPONENT component)
88 : {
89 9167 : switch (component)
90 : {
91 7 : case RIALTO_COMPONENT_DEFAULT:
92 14 : return "DEF";
93 : break;
94 2940 : case RIALTO_COMPONENT_CLIENT:
95 5880 : return "CLI";
96 : break;
97 5611 : case RIALTO_COMPONENT_SERVER:
98 11222 : return "SRV";
99 : break;
100 153 : case RIALTO_COMPONENT_IPC:
101 306 : return "IPC";
102 : break;
103 387 : case RIALTO_COMPONENT_SERVER_MANAGER:
104 774 : return "SMG";
105 : break;
106 69 : case RIALTO_COMPONENT_COMMON:
107 138 : return "COM";
108 : break;
109 0 : case RIALTO_COMPONENT_EXTERNAL:
110 0 : return "EXT";
111 : break;
112 0 : default:
113 0 : return "UNK";
114 : break;
115 : }
116 : }
117 :
118 9167 : std::string levelToString(RIALTO_DEBUG_LEVEL level)
119 : {
120 9167 : switch (level)
121 : {
122 2 : case RIALTO_DEBUG_LEVEL_FATAL:
123 4 : return "FTL";
124 : break;
125 963 : case RIALTO_DEBUG_LEVEL_ERROR:
126 1926 : return "ERR";
127 : break;
128 341 : case RIALTO_DEBUG_LEVEL_WARNING:
129 682 : return "WRN";
130 : break;
131 296 : case RIALTO_DEBUG_LEVEL_MILESTONE:
132 592 : return "MIL";
133 : break;
134 1309 : case RIALTO_DEBUG_LEVEL_INFO:
135 2618 : return "NFO";
136 : break;
137 6256 : case RIALTO_DEBUG_LEVEL_DEBUG:
138 12512 : return "DBG";
139 : break;
140 0 : case RIALTO_DEBUG_LEVEL_EXTERNAL:
141 0 : return "EXT";
142 : break;
143 0 : default:
144 0 : return ":";
145 : break;
146 : }
147 : }
148 :
149 : /**
150 : * File descriptor logging function for the library.
151 : */
152 9167 : void fdLogHandler(int fd, RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, int line,
153 : const char *function, const char *message, size_t messageLen)
154 : {
155 9167 : timespec ts = {0, 0};
156 9167 : clock_gettime(CLOCK_MONOTONIC, &ts);
157 : struct iovec iov[6];
158 : char tbuf[32];
159 :
160 9167 : iov[0].iov_base = tbuf;
161 9167 : iov[0].iov_len = snprintf(tbuf, sizeof(tbuf), "%.010lu.%.06lu ", ts.tv_sec, ts.tv_nsec / 1000);
162 9167 : iov[0].iov_len = std::min(iov[0].iov_len, sizeof(tbuf));
163 :
164 : char lbuf[8];
165 9167 : iov[1].iov_base = reinterpret_cast<void *>(lbuf);
166 9167 : iov[1].iov_len =
167 9167 : std::min(static_cast<size_t>(snprintf(lbuf, sizeof(lbuf), "%s: ", levelToString(level).c_str())), sizeof(lbuf));
168 :
169 : char cbuf[8];
170 9167 : iov[2].iov_base = reinterpret_cast<void *>(cbuf);
171 9167 : iov[2].iov_len =
172 9167 : std::min(static_cast<size_t>(snprintf(cbuf, sizeof(cbuf), "%s: ", componentToString(component).c_str())),
173 9167 : sizeof(cbuf));
174 :
175 : static thread_local pid_t threadId = 0;
176 9167 : if (threadId <= 0)
177 80 : threadId = syscall(SYS_gettid);
178 : char fbuf[180];
179 9167 : iov[3].iov_base = reinterpret_cast<void *>(fbuf);
180 :
181 9167 : if (RIALTO_DEBUG_LEVEL_EXTERNAL == level)
182 : {
183 0 : iov[3].iov_len = snprintf(fbuf, sizeof(fbuf), "< T:%d >", threadId);
184 : }
185 9167 : else if (!file || !function || (line <= 0))
186 : {
187 0 : iov[3].iov_len = snprintf(fbuf, sizeof(fbuf), "< T:%d M:? F:? L:? > ", threadId);
188 : }
189 : else
190 : {
191 9167 : iov[3].iov_len =
192 9167 : snprintf(fbuf, sizeof(fbuf), "< T:%d M:%.*s F:%.*s L:%d > ", threadId, 64, file, 64, function, line);
193 : }
194 9167 : iov[3].iov_len = std::min(iov[3].iov_len, sizeof(fbuf));
195 9167 : iov[4].iov_base = const_cast<void *>(reinterpret_cast<const void *>(message));
196 9167 : iov[4].iov_len = messageLen;
197 9167 : iov[5].iov_base = const_cast<void *>(reinterpret_cast<const void *>("\n"));
198 9167 : iov[5].iov_len = 1;
199 : // TODO(RIALTO-38): consider using standard write(2) and handle EINTR properly.
200 9167 : std::ignore = writev(fd, iov, 6);
201 : }
202 : /**
203 : * Journald logging function for the library.
204 : */
205 0 : void journaldLogHandler(RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, int line,
206 : const char *function, const char *message, size_t messageLen)
207 : {
208 : static thread_local pid_t threadId = 0;
209 0 : if (threadId <= 0)
210 0 : threadId = syscall(SYS_gettid);
211 : char fbuf[180];
212 0 : if (RIALTO_DEBUG_LEVEL_EXTERNAL == level)
213 : {
214 0 : snprintf(fbuf, sizeof(fbuf), "%s: %s: < T:%d >", levelToString(level).c_str(),
215 0 : componentToString(component).c_str(), threadId);
216 : }
217 0 : else if (!file || !function || (line <= 0))
218 : {
219 0 : snprintf(fbuf, sizeof(fbuf), "%s: %s: < T:%d M:? F:? L:? >", levelToString(level).c_str(),
220 0 : componentToString(component).c_str(), threadId);
221 : }
222 : else
223 : {
224 0 : snprintf(fbuf, sizeof(fbuf), "%s: %s: < T:%d M:%.*s F:%.*s L:%d >", levelToString(level).c_str(),
225 0 : componentToString(component).c_str(), threadId, 64, file, 64, function, line);
226 : }
227 :
228 0 : switch (level)
229 : {
230 0 : case RIALTO_DEBUG_LEVEL_FATAL:
231 0 : SYSTEM_LOG_FATAL(file, function, line, "%s %s", fbuf, message);
232 0 : break;
233 0 : case RIALTO_DEBUG_LEVEL_ERROR:
234 0 : SYSTEM_LOG_ERROR(file, function, line, "%s %s", fbuf, message);
235 0 : break;
236 0 : case RIALTO_DEBUG_LEVEL_WARNING:
237 0 : SYSTEM_LOG_WARN(file, function, line, "%s %s", fbuf, message);
238 0 : break;
239 0 : case RIALTO_DEBUG_LEVEL_MILESTONE:
240 0 : SYSTEM_LOG_MIL(file, function, line, "%s %s", fbuf, message);
241 0 : break;
242 0 : case RIALTO_DEBUG_LEVEL_INFO:
243 0 : SYSTEM_LOG_INFO(file, function, line, "%s %s", fbuf, message);
244 0 : break;
245 0 : case RIALTO_DEBUG_LEVEL_DEBUG:
246 0 : SYSTEM_LOG_DEBUG(file, function, line, "%s %s", fbuf, message);
247 0 : break;
248 0 : case RIALTO_DEBUG_LEVEL_EXTERNAL:
249 0 : SYSTEM_LOG_INFO(file, function, line, "%s %s", fbuf, message);
250 0 : break;
251 0 : default:
252 0 : break;
253 : }
254 : }
255 :
256 9677 : void rialtoLog(RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, const char *func, int line,
257 : const char *fmt, va_list ap, const char *append)
258 : {
259 : /* Must be valid component */
260 9677 : if (component >= RIALTO_COMPONENT_LAST)
261 451 : return;
262 :
263 : /* If log levels have not been set, set to Default */
264 9677 : if (!g_rialtoLogLevels[component])
265 15 : g_rialtoLogLevels[component] = g_envVariableParser.getLevel(component);
266 :
267 9677 : if (!(level & g_rialtoLogLevels[component]) && !g_ignoreLogLevels[component])
268 451 : return;
269 : char mbuf[512];
270 : int len;
271 9226 : len = vsnprintf(mbuf, sizeof(mbuf), fmt, ap);
272 9226 : if (len < 1)
273 0 : return;
274 9226 : if (len > static_cast<int>(sizeof(mbuf) - 1))
275 0 : len = sizeof(mbuf) - 1;
276 9226 : if (mbuf[len - 1] == '\n')
277 5 : len--;
278 9226 : mbuf[len] = '\0';
279 9226 : if (append && (len < static_cast<int>(sizeof(mbuf) - 1)))
280 : {
281 49 : size_t extra = std::min<size_t>(strlen(append), (sizeof(mbuf) - len - 1));
282 49 : memcpy(mbuf + len, append, extra);
283 49 : len += static_cast<int>(extra);
284 49 : mbuf[len] = '\0';
285 : }
286 9226 : const char *kFname = nullptr;
287 9226 : if (file)
288 : {
289 9226 : if ((kFname = strrchr(file, '/')) == nullptr)
290 0 : kFname = file;
291 : else
292 9226 : kFname++;
293 : }
294 :
295 9226 : const auto &kLogFileHandle = firebolt::rialto::logging::LogFileHandle::instance();
296 : /* If log handler had not been set, use default */
297 9226 : firebolt::rialto::logging::LogHandler logHandler = g_logHandler[component]; // local copy for thread safety
298 9226 : if (logHandler)
299 : {
300 59 : logHandler(level, kFname, line, func, mbuf, len);
301 : }
302 9167 : else if (g_envVariableParser.isFileLoggingEnabled() && kLogFileHandle.isOpen())
303 : {
304 0 : fdLogHandler(kLogFileHandle.fd(), component, level, kFname, line, func, mbuf, len);
305 : }
306 9167 : else if (g_envVariableParser.isConsoleLoggingEnabled())
307 : {
308 9167 : fdLogHandler(STDERR_FILENO, component, level, kFname, line, func, mbuf, len);
309 : }
310 : else
311 : {
312 0 : journaldLogHandler(component, level, kFname, line, func, mbuf, len);
313 : }
314 9226 : }
315 :
316 : } // namespace
317 :
318 0 : void rialtoLogVPrintf(RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, const char *func,
319 : int line, const char *fmt, va_list ap)
320 : {
321 0 : rialtoLog(component, level, file, func, line, fmt, ap, nullptr);
322 : }
323 :
324 9590 : void rialtoLogPrintf(RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, const char *func, int line,
325 : const char *fmt, ...)
326 : {
327 : va_list ap;
328 9590 : va_start(ap, fmt);
329 9590 : rialtoLog(component, level, file, func, line, fmt, ap, nullptr);
330 9590 : va_end(ap);
331 : }
332 :
333 87 : void rialtoLogSysPrintf(RIALTO_COMPONENT component, int err, RIALTO_DEBUG_LEVEL level, const char *file,
334 : const char *func, int line, const char *fmt, ...)
335 : {
336 : va_list ap;
337 87 : const char *kErrmsg{nullptr};
338 : char appendbuf[96];
339 87 : const char *kAppend = nullptr;
340 : #if defined(__linux__)
341 : char errbuf[64];
342 87 : kErrmsg = strerror_r(err, errbuf, sizeof(errbuf));
343 : #elif defined(__APPLE__)
344 : char errbuf[64];
345 : if (strerror_r(err, errbuf, sizeof(errbuf)) != 0)
346 : kErrmsg = "Unknown error";
347 : else
348 : kErrmsg = errbuf;
349 : #endif
350 87 : if (kErrmsg)
351 : {
352 87 : snprintf(appendbuf, sizeof(appendbuf), " (%d - %s)", err, kErrmsg);
353 87 : appendbuf[sizeof(appendbuf) - 1] = '\0';
354 87 : kAppend = appendbuf;
355 : }
356 87 : va_start(ap, fmt);
357 87 : rialtoLog(component, level, file, func, line, fmt, ap, kAppend);
358 87 : va_end(ap);
359 : }
360 :
361 : namespace firebolt::rialto::logging
362 : {
363 46 : RialtoLoggingStatus setLogLevels(RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL logLevels)
364 : {
365 46 : RialtoLoggingStatus status = RIALTO_LOGGING_STATUS_ERROR;
366 46 : if (component < RIALTO_COMPONENT_LAST && !g_ignoreLogLevels[component])
367 : {
368 45 : g_rialtoLogLevels[component] = logLevels;
369 45 : status = RIALTO_LOGGING_STATUS_OK;
370 : }
371 :
372 46 : return status;
373 : }
374 :
375 26 : RIALTO_DEBUG_LEVEL getLogLevels(RIALTO_COMPONENT component)
376 : {
377 26 : if (component < RIALTO_COMPONENT_LAST)
378 : {
379 26 : if (g_ignoreLogLevels[component])
380 1 : return RIALTO_DEBUG_LEVEL_EXTERNAL;
381 :
382 25 : if (!g_rialtoLogLevels[component])
383 0 : g_rialtoLogLevels[component] = g_envVariableParser.getLevel(component);
384 25 : return g_rialtoLogLevels[component];
385 : }
386 0 : return RIALTO_DEBUG_LEVEL_DEFAULT;
387 : }
388 :
389 63 : RialtoLoggingStatus setLogHandler(RIALTO_COMPONENT component, LogHandler handler, bool ignoreLogLevels)
390 : {
391 63 : RialtoLoggingStatus status = RIALTO_LOGGING_STATUS_ERROR;
392 63 : if (component < RIALTO_COMPONENT_LAST)
393 : {
394 62 : std::unique_lock<std::mutex> lock{g_logHandlerMutex};
395 :
396 62 : g_logHandler[component] = std::move(handler);
397 :
398 : // Ignoring log levels is only an option if we're registering
399 : // a non-null log handler
400 62 : g_ignoreLogLevels[component] = (g_logHandler[component]) ? ignoreLogLevels : false;
401 :
402 62 : status = RIALTO_LOGGING_STATUS_OK;
403 : }
404 :
405 63 : return status;
406 42 : }
407 :
408 3 : bool isConsoleLoggingEnabled()
409 : {
410 3 : return g_envVariableParser.isConsoleLoggingEnabled();
411 : }
412 :
413 : } // namespace firebolt::rialto::logging
|