master
c 358 lines 11 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "nd_log-internals.h"
4
5 // --------------------------------------------------------------------------------------------------------------------
6
7 __attribute__((constructor)) void initialize_invocation_id(void) {
8 // check for a NETDATA_INVOCATION_ID
9 if(uuid_parse_flexi(getenv("NETDATA_INVOCATION_ID"), nd_log.invocation_id) != 0) {
10 // not found, check for systemd set INVOCATION_ID
11 if(uuid_parse_flexi(getenv("INVOCATION_ID"), nd_log.invocation_id) != 0) {
12 // not found, generate a new one
13 uuid_generate_random(nd_log.invocation_id);
14 }
15 }
16
17 char uuid[UUID_COMPACT_STR_LEN];
18 uuid_unparse_lower_compact(nd_log.invocation_id, uuid);
19 nd_setenv("NETDATA_INVOCATION_ID", uuid, 1);
20 }
21
22 ND_UUID nd_log_get_invocation_id(void) {
23 ND_UUID rc;
24 uuid_copy(rc.uuid, nd_log.invocation_id);
25 return rc;
26 }
27
28 // --------------------------------------------------------------------------------------------------------------------
29
30 static void nd_log_make_stream_unbuffered(FILE *fp, int fd, const char *filename) {
31 if(fp && setvbuf(fp, NULL, _IONBF, 0) != 0)
32 netdata_log_error("Cannot disable buffering on fd %d ('%s')", fd, filename ? filename : "stdio");
33 }
34
35 // --------------------------------------------------------------------------------------------------------------------
36
37 void nd_log_initialize_mutexes(void) {
38 FUNCTION_RUN_ONCE();
39
40 for(size_t i = 0 ; i < _NDLS_MAX ; i++)
41 netdata_mutex_init(&nd_log.sources[i].mutex);
42
43 netdata_mutex_init(&nd_log.std_output.mutex);
44 netdata_mutex_init(&nd_log.std_error.mutex);
45 __atomic_store_n(&nd_log.mutexes_initialized, true, __ATOMIC_RELEASE);
46 }
47
48 // --------------------------------------------------------------------------------------------------------------------
49
50 void nd_log_initialize_for_external_plugins(const char *name) {
51 nd_log_initialize_mutexes();
52
53 // if we don't run under Netdata, log to stderr,
54 // otherwise, use the logging method Netdata wants us to use.
55 #if defined(OS_WINDOWS)
56 #if defined(HAVE_ETW)
57 nd_setenv("NETDATA_LOG_METHOD", ETW_NAME, 0);
58 nd_setenv("NETDATA_LOG_FORMAT", ETW_NAME, 0);
59 #elif defined(HAVE_WEL)
60 nd_setenv("NETDATA_LOG_METHOD", WEL_NAME, 0);
61 nd_setenv("NETDATA_LOG_FORMAT", WEL_NAME, 0);
62 #else
63 nd_setenv("NETDATA_LOG_METHOD", "stderr", 0);
64 nd_setenv("NETDATA_LOG_FORMAT", "logfmt", 0);
65 #endif
66 #else
67 nd_setenv("NETDATA_LOG_METHOD", "stderr", 0);
68 nd_setenv("NETDATA_LOG_FORMAT", "logfmt", 0);
69 #endif
70
71 nd_log.overwrite_process_source = NDLS_COLLECTORS;
72 program_name = name;
73
74 for(size_t i = 0; i < _NDLS_MAX ;i++) {
75 nd_log.sources[i].method = NDLM_DEFAULT;
76 nd_log.sources[i].fd = -1;
77 nd_log.sources[i].fp = NULL;
78 }
79
80 nd_log_set_priority_level(getenv("NETDATA_LOG_LEVEL"));
81 nd_log_set_facility(getenv("NETDATA_SYSLOG_FACILITY"));
82
83 time_t period = 1200;
84 size_t logs = 200;
85 const char *s = getenv("NETDATA_ERRORS_THROTTLE_PERIOD");
86 if(s && *s >= '0' && *s <= '9') {
87 period = str2l(s);
88 if(period < 0) period = 0;
89 }
90
91 s = getenv("NETDATA_ERRORS_PER_PERIOD");
92 if(s && *s >= '0' && *s <= '9')
93 logs = str2u(s);
94
95 nd_log_set_flood_protection(logs, period);
96
97 if(!netdata_configured_host_prefix) {
98 s = getenv("NETDATA_HOST_PREFIX");
99 if(s && *s)
100 netdata_configured_host_prefix = (char *)s;
101 }
102
103 ND_LOG_METHOD method = nd_log_method2id(getenv("NETDATA_LOG_METHOD"));
104 ND_LOG_FORMAT format = nd_log_format2id(getenv("NETDATA_LOG_FORMAT"));
105
106 if(!IS_VALID_LOG_METHOD_FOR_EXTERNAL_PLUGINS(method)) {
107 if(is_stderr_connected_to_journal()) {
108 nd_log(NDLS_COLLECTORS, NDLP_WARNING, "NETDATA_LOG_METHOD is not set. Using journal.");
109 method = NDLM_JOURNAL;
110 }
111 else {
112 nd_log(NDLS_COLLECTORS, NDLP_WARNING, "NETDATA_LOG_METHOD is not set. Using stderr.");
113 method = NDLM_STDERR;
114 }
115 }
116
117 switch(method) {
118 case NDLM_JOURNAL:
119 if(!nd_log_journal_direct_init(getenv("NETDATA_SYSTEMD_JOURNAL_PATH")) &&
120 !nd_log_journal_direct_init(NULL) && !nd_log_journal_systemd_init()) {
121 nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to initialize journal. Using stderr.");
122 method = NDLM_STDERR;
123 }
124 break;
125
126 #if defined(OS_WINDOWS)
127 #if defined(HAVE_ETW)
128 case NDLM_ETW:
129 if(!nd_log_init_etw()) {
130 nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to initialize Events Tracing for Windows (ETW). Using stderr.");
131 method = NDLM_STDERR;
132 }
133 break;
134 #endif
135 #if defined(HAVE_WEL)
136 case NDLM_WEL:
137 if(!nd_log_init_wel()) {
138 nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to initialize Windows Event Log (WEL). Using stderr.");
139 method = NDLM_STDERR;
140 }
141 break;
142 #endif
143 #endif
144
145 case NDLM_SYSLOG:
146 nd_log_init_syslog();
147 break;
148
149 default:
150 method = NDLM_STDERR;
151 break;
152 }
153
154 nd_log.sources[NDLS_COLLECTORS].method = method;
155 nd_log.sources[NDLS_COLLECTORS].format = format;
156 nd_log.sources[NDLS_COLLECTORS].fd = -1;
157 nd_log.sources[NDLS_COLLECTORS].fp = NULL;
158
159 // nd_log(NDLS_COLLECTORS, NDLP_NOTICE, "FINAL_LOG_METHOD: %s", nd_log_id2method(method));
160
161 #if defined(HAVE_LIBBACKTRACE)
162 stacktrace_init();
163 #endif
164 }
165
166 // --------------------------------------------------------------------------------------------------------------------
167
168 void nd_log_open(struct nd_log_source *e, ND_LOG_SOURCES source) {
169 if(e->method == NDLM_DEFAULT)
170 nd_log_set_user_settings(source, e->filename);
171
172 if((e->method == NDLM_FILE && !e->filename) ||
173 (e->method == NDLM_DEVNULL && e->fd == -1))
174 e->method = NDLM_DISABLED;
175
176 if(e->fp)
177 fflush(e->fp);
178
179 switch(e->method) {
180 case NDLM_SYSLOG:
181 nd_log_init_syslog();
182 break;
183
184 case NDLM_JOURNAL:
185 nd_log_journal_direct_init(NULL);
186 nd_log_journal_systemd_init();
187 break;
188
189 #if defined(OS_WINDOWS)
190 #if defined(HAVE_ETW)
191 case NDLM_ETW:
192 nd_log_init_etw();
193 break;
194 #endif
195 #if defined(HAVE_WEL)
196 case NDLM_WEL:
197 nd_log_init_wel();
198 break;
199 #endif
200 #endif
201
202 case NDLM_STDOUT:
203 e->fp = stdout;
204 e->fd = STDOUT_FILENO;
205 nd_log_make_stream_unbuffered(e->fp, e->fd, "stdout");
206 break;
207
208 case NDLM_DISABLED:
209 break;
210
211 case NDLM_DEFAULT:
212 case NDLM_STDERR:
213 e->method = NDLM_STDERR;
214 e->fp = stderr;
215 e->fd = STDERR_FILENO;
216 nd_log_make_stream_unbuffered(e->fp, e->fd, "stderr");
217 break;
218
219 case NDLM_DEVNULL:
220 case NDLM_FILE: {
221 int fd = open(e->filename, O_WRONLY | O_APPEND | O_CREAT, 0664);
222 if(fd == -1) {
223 if(e->fd != STDOUT_FILENO && e->fd != STDERR_FILENO) {
224 e->fd = STDERR_FILENO;
225 e->method = NDLM_STDERR;
226 netdata_log_error("Cannot open log file '%s'. Falling back to stderr.", e->filename);
227 }
228 else
229 netdata_log_error("Cannot open log file '%s'. Leaving fd %d as-is.", e->filename, e->fd);
230 }
231 else {
232 if (!nd_log_replace_existing_fd(e, fd)) {
233 if(e->fd == STDOUT_FILENO || e->fd == STDERR_FILENO) {
234 if(e->fd == STDOUT_FILENO)
235 e->method = NDLM_STDOUT;
236 else if(e->fd == STDERR_FILENO)
237 e->method = NDLM_STDERR;
238
239 // we have dup2() fd, so we can close the one we opened
240 if(fd != STDOUT_FILENO && fd != STDERR_FILENO)
241 close(fd);
242 }
243 else
244 e->fd = fd;
245 }
246 }
247
248 // at this point we have e->fd set properly
249
250 if(e->fd == STDOUT_FILENO)
251 e->fp = stdout;
252 else if(e->fd == STDERR_FILENO)
253 e->fp = stderr;
254
255 if(!e->fp) {
256 e->fp = fdopen(e->fd, "a");
257 if (!e->fp) {
258 netdata_log_error("Cannot fdopen() fd %d ('%s')", e->fd, e->filename);
259
260 if(e->fd != STDOUT_FILENO && e->fd != STDERR_FILENO)
261 close(e->fd);
262
263 e->fp = stderr;
264 e->fd = STDERR_FILENO;
265 }
266 }
267
268 nd_log_make_stream_unbuffered(e->fp, e->fd, e->filename);
269 }
270 break;
271 }
272 }
273
274 // --------------------------------------------------------------------------------------------------------------------
275
276 void nd_log_stdin_init(int fd, const char *filename) {
277 int f = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0664);
278 if(f == -1)
279 return;
280
281 if(f != fd) {
282 dup2(f, fd);
283 close(f);
284 }
285 }
286
287 void nd_log_initialize(void) {
288 nd_log_initialize_mutexes();
289 nd_log_stdin_init(STDIN_FILENO, "/dev/null");
290
291 for(size_t i = 0 ; i < _NDLS_MAX ; i++)
292 nd_log_open(&nd_log.sources[i], i);
293
294 #if defined(HAVE_LIBBACKTRACE)
295 stacktrace_init();
296 #endif
297 }
298
299 void nd_log_reopen_log_files(bool log) {
300 if(log)
301 netdata_log_info("Reopening all log files.");
302
303 nd_log_initialize();
304
305 if(log)
306 netdata_log_info("Log files re-opened.");
307 }
308
309 int nd_log_systemd_journal_fd(void) {
310 return nd_log.journal.fd;
311 }
312
313 void nd_log_reopen_log_files_for_spawn_server(const char *name) {
314 nd_log.fatal_hook_cb = NULL;
315 nd_log.fatal_final_cb = NULL;
316 nd_log.single_threaded_child = true;
317
318 gettid_uncached();
319 #if defined(HAVE_LIBBACKTRACE)
320 stacktrace_flush();
321 stacktrace_forked();
322 #endif
323
324 if(nd_log.syslog.initialized) {
325 closelog();
326 nd_log.syslog.initialized = false;
327 nd_log_init_syslog();
328 }
329
330 if(nd_log.journal_direct.initialized) {
331 close(nd_log.journal_direct.fd);
332 nd_log.journal_direct.fd = -1;
333 nd_log.journal_direct.initialized = false;
334 }
335
336 // Keep flood protection in the post-fork child by resetting the
337 // per-source throttle spinlocks and counters.
338 for(size_t i = 0; i < _NDLS_MAX ;i++) {
339 spinlock_init(&nd_log.sources[i].limits.spinlock);
340 nd_log.sources[i].limits.started_monotonic_ut = 0;
341 nd_log.sources[i].limits.counter = 0;
342 nd_log.sources[i].limits.prevented = 0;
343 nd_log.sources[i].method = NDLM_DEFAULT;
344 nd_log.sources[i].fd = -1;
345 nd_log.sources[i].fp = NULL;
346 nd_log.sources[i].pending_msg = NULL;
347 #if defined(OS_WINDOWS)
348 nd_log.sources[i].hEventLog = NULL;
349 #endif
350 }
351
352 nd_log.syslog.initialized = false;
353 nd_log.eventlog.initialized = false;
354 nd_log.std_output.initialized = false;
355 nd_log.std_error.initialized = false;
356
357 nd_log_initialize_for_external_plugins(name);
358 }