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 <syslog.h>
29 : #include <unistd.h>
30 :
31 : #include "EnvVariableParser.h"
32 : #include "LogFileHandle.h"
33 : #include "RialtoLogging.h"
34 :
35 : namespace
36 : {
37 : /**
38 : * Log levels for each component. By default will print all fatals, errors, warnings & milestones.
39 : */
40 : std::atomic<RIALTO_DEBUG_LEVEL> g_rialtoLogLevels[RIALTO_COMPONENT_LAST] = {};
41 :
42 : /**
43 : * Default Log levels defined by RIALTO_DEBUG environment variable
44 : */
45 : const firebolt::rialto::logging::EnvVariableParser g_envVariableParser;
46 :
47 : /**
48 : * Log handler for each component. By default will use journaldLogHandler.
49 : */
50 : void fdLogHandler(int fd, RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, int line,
51 : const char *function, const char *message, size_t messageLen);
52 : void journaldLogHandler(RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, int line,
53 : const char *function, const char *message, size_t messageLen);
54 :
55 : firebolt::rialto::logging::LogHandler g_logHandler[RIALTO_COMPONENT_LAST] = {};
56 : bool g_ignoreLogLevels[RIALTO_COMPONENT_LAST] = {};
57 : std::mutex g_logHandlerMutex;
58 :
59 : std::string componentToString(RIALTO_COMPONENT component);
60 : std::string levelToString(RIALTO_DEBUG_LEVEL level);
61 :
62 8953 : std::string componentToString(RIALTO_COMPONENT component)
63 : {
64 8953 : switch (component)
65 : {
66 7 : case RIALTO_COMPONENT_DEFAULT:
67 14 : return "DEF";
68 : break;
69 2882 : case RIALTO_COMPONENT_CLIENT:
70 5764 : return "CLI";
71 : break;
72 5455 : case RIALTO_COMPONENT_SERVER:
73 10910 : return "SRV";
74 : break;
75 153 : case RIALTO_COMPONENT_IPC:
76 306 : return "IPC";
77 : break;
78 387 : case RIALTO_COMPONENT_SERVER_MANAGER:
79 774 : return "SMG";
80 : break;
81 69 : case RIALTO_COMPONENT_COMMON:
82 138 : return "COM";
83 : break;
84 0 : case RIALTO_COMPONENT_EXTERNAL:
85 0 : return "EXT";
86 : break;
87 0 : default:
88 0 : return "UNK";
89 : break;
90 : }
91 : }
92 :
93 8953 : std::string levelToString(RIALTO_DEBUG_LEVEL level)
94 : {
95 8953 : switch (level)
96 : {
97 2 : case RIALTO_DEBUG_LEVEL_FATAL:
98 4 : return "FTL";
99 : break;
100 946 : case RIALTO_DEBUG_LEVEL_ERROR:
101 1892 : return "ERR";
102 : break;
103 324 : case RIALTO_DEBUG_LEVEL_WARNING:
104 648 : return "WRN";
105 : break;
106 286 : case RIALTO_DEBUG_LEVEL_MILESTONE:
107 572 : return "MIL";
108 : break;
109 1281 : case RIALTO_DEBUG_LEVEL_INFO:
110 2562 : return "NFO";
111 : break;
112 6114 : case RIALTO_DEBUG_LEVEL_DEBUG:
113 12228 : return "DBG";
114 : break;
115 0 : case RIALTO_DEBUG_LEVEL_EXTERNAL:
116 0 : return "EXT";
117 : break;
118 0 : default:
119 0 : return ":";
120 : break;
121 : }
122 : }
123 :
124 : /**
125 : * File descriptor logging function for the library.
126 : */
127 8953 : void fdLogHandler(int fd, RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, int line,
128 : const char *function, const char *message, size_t messageLen)
129 : {
130 8953 : timespec ts = {0, 0};
131 8953 : clock_gettime(CLOCK_MONOTONIC, &ts);
132 : struct iovec iov[6];
133 : char tbuf[32];
134 :
135 8953 : iov[0].iov_base = tbuf;
136 8953 : iov[0].iov_len = snprintf(tbuf, sizeof(tbuf), "%.010lu.%.06lu ", ts.tv_sec, ts.tv_nsec / 1000);
137 8953 : iov[0].iov_len = std::min(iov[0].iov_len, sizeof(tbuf));
138 :
139 : char lbuf[8];
140 8953 : iov[1].iov_base = reinterpret_cast<void *>(lbuf);
141 8953 : iov[1].iov_len =
142 8953 : std::min(static_cast<size_t>(snprintf(lbuf, sizeof(lbuf), "%s: ", levelToString(level).c_str())), sizeof(lbuf));
143 :
144 : char cbuf[8];
145 8953 : iov[2].iov_base = reinterpret_cast<void *>(cbuf);
146 8953 : iov[2].iov_len =
147 8953 : std::min(static_cast<size_t>(snprintf(cbuf, sizeof(cbuf), "%s: ", componentToString(component).c_str())),
148 8953 : sizeof(cbuf));
149 :
150 : static thread_local pid_t threadId = 0;
151 8953 : if (threadId <= 0)
152 81 : threadId = syscall(SYS_gettid);
153 : char fbuf[180];
154 8953 : iov[3].iov_base = reinterpret_cast<void *>(fbuf);
155 :
156 8953 : if (RIALTO_DEBUG_LEVEL_EXTERNAL == level)
157 : {
158 0 : iov[3].iov_len = snprintf(fbuf, sizeof(fbuf), "< T:%d >", threadId);
159 : }
160 8953 : else if (!file || !function || (line <= 0))
161 : {
162 0 : iov[3].iov_len = snprintf(fbuf, sizeof(fbuf), "< T:%d M:? F:? L:? > ", threadId);
163 : }
164 : else
165 : {
166 8953 : iov[3].iov_len =
167 8953 : snprintf(fbuf, sizeof(fbuf), "< T:%d M:%.*s F:%.*s L:%d > ", threadId, 64, file, 64, function, line);
168 : }
169 8953 : iov[3].iov_len = std::min(iov[3].iov_len, sizeof(fbuf));
170 8953 : iov[4].iov_base = const_cast<void *>(reinterpret_cast<const void *>(message));
171 8953 : iov[4].iov_len = messageLen;
172 8953 : iov[5].iov_base = const_cast<void *>(reinterpret_cast<const void *>("\n"));
173 8953 : iov[5].iov_len = 1;
174 : // TODO(RIALTO-38): consider using standard write(2) and handle EINTR properly.
175 8953 : std::ignore = writev(fd, iov, 6);
176 : }
177 : /**
178 : * Journald logging function for the library.
179 : */
180 0 : void journaldLogHandler(RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, int line,
181 : const char *function, const char *message, size_t messageLen)
182 : {
183 : static thread_local pid_t threadId = 0;
184 0 : if (threadId <= 0)
185 0 : threadId = syscall(SYS_gettid);
186 : char fbuf[180];
187 0 : if (RIALTO_DEBUG_LEVEL_EXTERNAL == level)
188 : {
189 0 : snprintf(fbuf, sizeof(fbuf), "%s: %s: < T:%d >", levelToString(level).c_str(),
190 0 : componentToString(component).c_str(), threadId);
191 : }
192 0 : else if (!file || !function || (line <= 0))
193 : {
194 0 : snprintf(fbuf, sizeof(fbuf), "%s: %s: < T:%d M:? F:? L:? >", levelToString(level).c_str(),
195 0 : componentToString(component).c_str(), threadId);
196 : }
197 : else
198 : {
199 0 : snprintf(fbuf, sizeof(fbuf), "%s: %s: < T:%d M:%.*s F:%.*s L:%d >", levelToString(level).c_str(),
200 0 : componentToString(component).c_str(), threadId, 64, file, 64, function, line);
201 : }
202 :
203 0 : switch (level)
204 : {
205 0 : case RIALTO_DEBUG_LEVEL_FATAL:
206 0 : syslog(LOG_CRIT, "%s %s", fbuf, message);
207 0 : break;
208 0 : case RIALTO_DEBUG_LEVEL_ERROR:
209 0 : syslog(LOG_ERR, "%s %s", fbuf, message);
210 0 : break;
211 0 : case RIALTO_DEBUG_LEVEL_WARNING:
212 0 : syslog(LOG_WARNING, "%s %s", fbuf, message);
213 0 : break;
214 0 : case RIALTO_DEBUG_LEVEL_MILESTONE:
215 0 : syslog(LOG_NOTICE, "%s %s", fbuf, message);
216 0 : break;
217 0 : case RIALTO_DEBUG_LEVEL_INFO:
218 0 : syslog(LOG_INFO, "%s %s", fbuf, message);
219 0 : break;
220 0 : case RIALTO_DEBUG_LEVEL_DEBUG:
221 0 : syslog(LOG_DEBUG, "%s %s", fbuf, message);
222 0 : break;
223 0 : case RIALTO_DEBUG_LEVEL_EXTERNAL:
224 0 : syslog(LOG_INFO, "%s %s", fbuf, message);
225 0 : break;
226 0 : default:
227 0 : break;
228 : }
229 : }
230 :
231 9463 : void rialtoLog(RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, const char *func, int line,
232 : const char *fmt, va_list ap, const char *append)
233 : {
234 : /* Must be valid component */
235 9463 : if (component >= RIALTO_COMPONENT_LAST)
236 451 : return;
237 :
238 : /* If log levels have not been set, set to Default */
239 9463 : if (!g_rialtoLogLevels[component])
240 15 : g_rialtoLogLevels[component] = g_envVariableParser.getLevel(component);
241 :
242 9463 : if (!(level & g_rialtoLogLevels[component]) && !g_ignoreLogLevels[component])
243 451 : return;
244 : char mbuf[512];
245 : int len;
246 9012 : len = vsnprintf(mbuf, sizeof(mbuf), fmt, ap);
247 9012 : if (len < 1)
248 0 : return;
249 9012 : if (len > static_cast<int>(sizeof(mbuf) - 1))
250 0 : len = sizeof(mbuf) - 1;
251 9012 : if (mbuf[len - 1] == '\n')
252 5 : len--;
253 9012 : mbuf[len] = '\0';
254 9012 : if (append && (len < static_cast<int>(sizeof(mbuf) - 1)))
255 : {
256 49 : size_t extra = std::min<size_t>(strlen(append), (sizeof(mbuf) - len - 1));
257 49 : memcpy(mbuf + len, append, extra);
258 49 : len += static_cast<int>(extra);
259 49 : mbuf[len] = '\0';
260 : }
261 9012 : const char *kFname = nullptr;
262 9012 : if (file)
263 : {
264 9012 : if ((kFname = strrchr(file, '/')) == nullptr)
265 0 : kFname = file;
266 : else
267 9012 : kFname++;
268 : }
269 :
270 9012 : const auto &kLogFileHandle = firebolt::rialto::logging::LogFileHandle::instance();
271 : /* If log handler had not been set, use default */
272 9012 : firebolt::rialto::logging::LogHandler logHandler = g_logHandler[component]; // local copy for thread safety
273 9012 : if (logHandler)
274 : {
275 59 : logHandler(level, kFname, line, func, mbuf, len);
276 : }
277 8953 : else if (g_envVariableParser.isFileLoggingEnabled() && kLogFileHandle.isOpen())
278 : {
279 0 : fdLogHandler(kLogFileHandle.fd(), component, level, kFname, line, func, mbuf, len);
280 : }
281 8953 : else if (g_envVariableParser.isConsoleLoggingEnabled())
282 : {
283 8953 : fdLogHandler(STDERR_FILENO, component, level, kFname, line, func, mbuf, len);
284 : }
285 : else
286 : {
287 0 : journaldLogHandler(component, level, kFname, line, func, mbuf, len);
288 : }
289 9012 : }
290 :
291 : } // namespace
292 :
293 0 : void rialtoLogVPrintf(RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, const char *func,
294 : int line, const char *fmt, va_list ap)
295 : {
296 0 : rialtoLog(component, level, file, func, line, fmt, ap, nullptr);
297 : }
298 :
299 9376 : void rialtoLogPrintf(RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL level, const char *file, const char *func, int line,
300 : const char *fmt, ...)
301 : {
302 : va_list ap;
303 9376 : va_start(ap, fmt);
304 9376 : rialtoLog(component, level, file, func, line, fmt, ap, nullptr);
305 9376 : va_end(ap);
306 : }
307 :
308 87 : void rialtoLogSysPrintf(RIALTO_COMPONENT component, int err, RIALTO_DEBUG_LEVEL level, const char *file,
309 : const char *func, int line, const char *fmt, ...)
310 : {
311 : va_list ap;
312 87 : const char *kErrmsg{nullptr};
313 : char appendbuf[96];
314 87 : const char *kAppend = nullptr;
315 : #if defined(__linux__)
316 : char errbuf[64];
317 87 : kErrmsg = strerror_r(err, errbuf, sizeof(errbuf));
318 : #elif defined(__APPLE__)
319 : char errbuf[64];
320 : if (strerror_r(err, errbuf, sizeof(errbuf)) != 0)
321 : kErrmsg = "Unknown error";
322 : else
323 : kErrmsg = errbuf;
324 : #endif
325 87 : if (kErrmsg)
326 : {
327 87 : snprintf(appendbuf, sizeof(appendbuf), " (%d - %s)", err, kErrmsg);
328 87 : appendbuf[sizeof(appendbuf) - 1] = '\0';
329 87 : kAppend = appendbuf;
330 : }
331 87 : va_start(ap, fmt);
332 87 : rialtoLog(component, level, file, func, line, fmt, ap, kAppend);
333 87 : va_end(ap);
334 : }
335 :
336 : namespace firebolt::rialto::logging
337 : {
338 46 : RialtoLoggingStatus setLogLevels(RIALTO_COMPONENT component, RIALTO_DEBUG_LEVEL logLevels)
339 : {
340 46 : RialtoLoggingStatus status = RIALTO_LOGGING_STATUS_ERROR;
341 46 : if (component < RIALTO_COMPONENT_LAST && !g_ignoreLogLevels[component])
342 : {
343 45 : g_rialtoLogLevels[component] = logLevels;
344 45 : status = RIALTO_LOGGING_STATUS_OK;
345 : }
346 :
347 46 : return status;
348 : }
349 :
350 26 : RIALTO_DEBUG_LEVEL getLogLevels(RIALTO_COMPONENT component)
351 : {
352 26 : if (component < RIALTO_COMPONENT_LAST)
353 : {
354 26 : if (g_ignoreLogLevels[component])
355 1 : return RIALTO_DEBUG_LEVEL_EXTERNAL;
356 :
357 25 : if (!g_rialtoLogLevels[component])
358 0 : g_rialtoLogLevels[component] = g_envVariableParser.getLevel(component);
359 25 : return g_rialtoLogLevels[component];
360 : }
361 0 : return RIALTO_DEBUG_LEVEL_DEFAULT;
362 : }
363 :
364 63 : RialtoLoggingStatus setLogHandler(RIALTO_COMPONENT component, LogHandler handler, bool ignoreLogLevels)
365 : {
366 63 : RialtoLoggingStatus status = RIALTO_LOGGING_STATUS_ERROR;
367 63 : if (component < RIALTO_COMPONENT_LAST)
368 : {
369 62 : std::unique_lock<std::mutex> lock{g_logHandlerMutex};
370 :
371 62 : g_logHandler[component] = std::move(handler);
372 :
373 : // Ignoring log levels is only an option if we're registering
374 : // a non-null log handler
375 62 : g_ignoreLogLevels[component] = (g_logHandler[component]) ? ignoreLogLevels : false;
376 :
377 62 : status = RIALTO_LOGGING_STATUS_OK;
378 : }
379 :
380 63 : return status;
381 42 : }
382 :
383 3 : bool isConsoleLoggingEnabled()
384 : {
385 3 : return g_envVariableParser.isConsoleLoggingEnabled();
386 : }
387 :
388 : } // namespace firebolt::rialto::logging
|