1 /* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 /*
24 NB This module has an unusual amount of failsafes, OOM checks, and
25 so on as it implements a public API. This makes a fair number
26 of minor code paths cases of "we should never get here (unless
27 someone's going out of their way to break to API)". :)
28 */
29
30 #define LOG_SUBSYSTEM_TAG "Server"
31
32 #include "log_builtins_filter_imp.h"
33 #include "log_builtins_imp.h" // internal structs
34 // connection_events_loop_aborted()
35
36 #include "my_dir.h"
37 #include "mysys_err.h"
38
39 #include <mysql/components/services/log_service.h>
40 #include <mysql/components/services/log_shared.h> // data types
41
42 #include "sql/current_thd.h" // current_thd
43 #include "sql/log.h" // make_iso8601_timestamp, log_write_errstream,
44 // log_get_thread_id, mysql_errno_to_symbol,
45 // mysql_symbol_to_errno, log_vmessage,
46 // error_message_for_error_log
47 #include "sql/mysqld.h" // opt_log_(timestamps|error_services),
48 #include "sql/sql_class.h" // THD
49
50 // Must come after sql/log.h.
51 #include "mysql/components/services/log_builtins.h"
52
53 #ifndef _WIN32
54 #include <syslog.h>
55 #else
56 #include <stdio.h>
57
58 #include "my_sys.h"
59
60 extern CHARSET_INFO my_charset_utf16le_bin; // used in Windows EventLog
61 static HANDLE hEventLog = NULL; // global
62 #define MSG_DEFAULT 0xC0000064L
63 #endif
64
65 extern log_filter_ruleset *log_filter_builtin_rules; // what it says on the tin
66
67 PSI_memory_key key_memory_log_error_loaded_services;
68 PSI_memory_key key_memory_log_error_stack;
69
70 using std::string;
71 using std::unique_ptr;
72
73 struct log_service_cache_entry;
74
75 struct log_service_cache_entry_free {
76 /**
77 Release an entry in the hash of log services.
78
79 @param sce the entry to free
80 */
81 void operator()(log_service_cache_entry *sce) const;
82 };
83
84 /**
85 We're caching handles to the services used in error logging
86 as looking them up is costly.
87 */
88 using cache_entry_with_deleter =
89 unique_ptr<log_service_cache_entry, log_service_cache_entry_free>;
90 static collation_unordered_map<string, cache_entry_with_deleter>
91 *log_service_cache;
92
93 /**
94 Lock for the log "stack" (i.e. the list of active log-services).
95 X-locked while stack is changed/configured.
96 S-locked while stack is used.
97 */
98 static mysql_rwlock_t THR_LOCK_log_stack;
99
100 /**
101 Make sure only one instance of syslog/Eventlog code runs at a time.
102 */
103 static mysql_mutex_t THR_LOCK_log_syseventlog;
104
105 /**
106 Make sure only one instance of the buffered "writer" runs at a time.
107
108 In normal operation, the log-event will be created dynamically, then
109 it will be fed through the pipeline, and then it will be released.
110 Since the event is allocated in the caller, we can be sure it won't
111 go away wholesale during processing, and since the event is local to
112 the caller, no other thread will tangle with it. It is therefore safe
113 in those cases not to wrap a lock around the event.
114 (The log-pipeline will still grab a shared lock, THR_LOCK_log_stack,
115 to protect the pipeline (not the event) and the log-services cache from
116 being changed while the pipeline is being applied.
117 Likewise, log-services may protect their resources (file-writers will
118 usually take a lock to serialize their writes; the built-in filter will
119 take a lock on its rule-set as that is shared between concurrent
120 threads running the filter, and so on).
121 None of these are intended to protect the event itself though.
122
123 In buffered mode on the other hand, we copy each log-event (the
124 original of which, see above, is owned by the caller and local
125 to the thread, and therefore safe without locking) to a global
126 buffer / backlog. As this backlog can be added to by all threads,
127 it must be protected by a lock (once we have fully initialized
128 the subsystem with log_builtins_init() and support multi-threaded
129 mode anyway, as indicated by log_builtins_inited being true, see
130 below). This is that lock.
131 */
132 static mysql_mutex_t THR_LOCK_log_buffered;
133
134 /**
135 Subsystem initialized and ready to use?
136 */
137 static bool log_builtins_inited = false;
138
139 /**
140 Name of the interface that log-services implements.
141 */
142 #define LOG_SERVICES_PREFIX "log_service"
143
144 struct log_line_buffer {
145 log_line ll; ///< log-event we're buffering
146 log_line_buffer *next; ///< chronologically next log-event
147 };
148 /// Pointer to the first element in the list of buffered log messages
149 static log_line_buffer *log_line_buffer_start = nullptr;
150 /// Where to write the pointer to the newly-created tail-element of the list
151 static log_line_buffer **log_line_buffer_tail = &log_line_buffer_start;
152
153 /**
154 Timestamp: During buffered logging, when should we next consider flushing?
155 This variable is set to the time we'll next check whether we'll want to
156 flush buffered log events.
157 I.e. it is set to a future time (current time + window length); once
158 the value expires (turns into present/past), we check whether we need
159 to flush and update the variable to the next time we should check.
160 */
161 static ulonglong log_buffering_timeout = 0;
162
163 /// If after this many seconds we're still buffering, flush!
164 #ifndef LOG_BUFFERING_TIMEOUT_AFTER
165 #define LOG_BUFFERING_TIMEOUT_AFTER (60)
166 #endif
167 /// Thereafter, if still buffering after this many more seconds, flush again!
168 #ifndef LOG_BUFFERING_TIMEOUT_EVERY
169 #define LOG_BUFFERING_TIMEOUT_EVERY (10)
170 #endif
171 /// Time function returns microseconds, we want seconds.
172 #ifndef LOG_BUFFERING_TIME_SCALE
173 #define LOG_BUFFERING_TIME_SCALE 1000000
174 #endif
175
176 /// Does buffer contain SYSTEM or ERROR prio messages? Flush early only then!
177 static int log_buffering_flushworthy = false;
178
179 /**
180 Timestamp of when we last flushed to traditional error-log
181 (built-in sink). Log lines with timestamps older than this
182 have already been flushed to the default log-sink, so we
183 won't do so again to prevent duplicates. (The buffered events
184 are still kept around until buffered logging ends however in
185 case we need to flush them to other log-writers then.
186 This timestamp is updated to the present and then drifts into
187 the past, in contrast to the time-out value (see there).
188 */
189 static ulonglong log_sink_trad_last = 0;
190
191 /**
192 Timestamp of the last event we put into the error-log buffer
193 during buffered mode (while starting up). New items must
194 receive a LOG_ITEM_LOG_BUFFERED timestamp greater than this.
195 */
196 static ulonglong log_sink_buffer_last = 0;
197
198 /**
199 Finding and acquiring a service in the component framework is
200 expensive, and we may use services a log (depending on how many
201 events are logged per second), so we cache the relevant data.
202 This struct describes a given service.
203 */
204 struct log_service_cache_entry {
205 char *name; ///< name of this service
206 size_t name_len; ///< service-name's length
207 my_h_service service; ///< handle (service framework)
208 int opened; ///< currently open instances
209 int requested; ///< requested instances
210 int chistics; ///< multi-open supported, etc.
211 };
212
213 /**
214 State of a given instance of a service. A service may support being
215 opened several times.
216 */
217 typedef struct _log_service_instance {
218 log_service_cache_entry *sce; ///< the service in question
219 void *instance; ///< instance handle (multi-open)
220 struct _log_service_instance *next; ///< next instance (any service)
221 } log_service_instance;
222
223 static log_service_instance *log_service_instances = nullptr; ///< anchor
224
225 /**
226 An error-stream.
227 Rather than implement its own file operations, a log-service may use
228 convenience functions defined in this file. These functions use the
229 log_errstream struct to describe their log-files. These structs are
230 opaque to the log-services.
231 */
232 struct log_errstream {
233 FILE *file{nullptr}; ///< file to log to
234 mysql_mutex_t LOCK_errstream; ///< lock for logging
235 };
236
237 /// What mode is error-logging in (e.g. are loadable services available yet)?
238 static enum log_error_stage log_error_stage_current =
239 LOG_ERROR_STAGE_BUFFERING_EARLY;
240
241 /// Set error-logging stage hint (e.g. are loadable services available yet?).
log_error_stage_set(enum log_error_stage les)242 void log_error_stage_set(enum log_error_stage les) {
243 log_error_stage_current = les;
244 }
245
246 /// What mode is error-logging in (e.g. are loadable services available yet)?
log_error_stage_get()247 enum log_error_stage log_error_stage_get() { return log_error_stage_current; }
248
249 /**
250 Test whether a given log-service name refers to a built-in
251 service (built-in filter or built-in sink at this point).
252
253 @param name the name -- either just the component's, or
254 a fully qualified service.component
255 @param len the length of the aforementioned name
256 @retval if built-in filter: flags for built-in|singleton|filter
257 @retval if built-in sink: flags for built-in|singleton|sink
258 @retval otherwise: LOG_SERVICE_UNSPECIFIED
259 */
log_service_check_if_builtin(const char * name,size_t len)260 static int log_service_check_if_builtin(const char *name, size_t len) {
261 const size_t builtin_len = sizeof(LOG_SERVICES_PREFIX) - 1;
262
263 if ((len > (builtin_len + 1)) && (name[builtin_len] == '.') &&
264 (0 == strncmp(name, LOG_SERVICES_PREFIX, builtin_len))) {
265 name += builtin_len;
266 len -= builtin_len;
267 }
268
269 if ((len == sizeof(LOG_BUILTINS_FILTER) - 1) &&
270 (0 == strncmp(name, LOG_BUILTINS_FILTER, len)))
271 return LOG_SERVICE_BUILTIN | LOG_SERVICE_FILTER | LOG_SERVICE_SINGLETON;
272
273 if ((len == sizeof(LOG_BUILTINS_SINK) - 1) &&
274 (0 == strncmp(name, LOG_BUILTINS_SINK, len)))
275 return LOG_SERVICE_BUILTIN | LOG_SERVICE_SINK | LOG_SERVICE_SINGLETON;
276
277 if ((len == sizeof(LOG_BUILTINS_BUFFER) - 1) &&
278 (0 == strncmp(name, LOG_BUILTINS_BUFFER, len)))
279 return LOG_SERVICE_BUILTIN | LOG_SERVICE_SINK | LOG_SERVICE_SINGLETON |
280 LOG_SERVICE_BUFFER;
281
282 return LOG_SERVICE_UNSPECIFIED;
283 }
284
285 /**
286 Test whether given service has *all* of the given characteristics.
287 (See log_service_chistics for a list!)
288
289 @param sce service cache entry for the service in question
290 @param required_flags flags we're interested in (bitwise or'd)
291
292 @retval true if all given flags are present, false otherwise
293 */
log_service_has_characteristics(log_service_cache_entry * sce,int required_flags)294 static inline bool log_service_has_characteristics(log_service_cache_entry *sce,
295 int required_flags) {
296 return ((sce->chistics & required_flags) == required_flags);
297 }
298
299 /**
300 Pre-defined "well-known" keys, as opposed to ad hoc ones,
301 for key/value pairs in logging.
302 */
303 typedef struct _log_item_wellknown_key {
304 const char *name; ///< key name
305 size_t name_len; ///< length of key's name
306 log_item_class item_class; ///< item class (float/int/string)
307 log_item_type item_type; ///< exact type, 1:1 relationship with name
308 } log_item_wellknown_key;
309
310 /**
311 We support a number of predefined keys, such as "error-code" or
312 "message". These are defined here. We also support user-defined
313 "ad hoc" (or "generic") keys that let users of the error stack
314 add values with arbitrary keys (as long as those keys don't coincide
315 with the wellknown ones, anyway).
316
317 The idea here is that we want the flexibility of arbitrary keys,
318 while being able to do certain optimizations for the common case.
319 This also allows us to relatively cheaply add some convenience
320 features, e.g. we know that error symbol ("ER_STARTUP") and
321 error code (1451) are related, and can supply one as the other
322 is submitted. Likewise of course, we can use the error code to
323 fetch the associated registered error message string for that
324 error code. Et cetera!
325 */
326 static const log_item_wellknown_key log_item_wellknown_keys[] = {
327 {STRING_WITH_LEN("--ERROR--"), LOG_UNTYPED, LOG_ITEM_END},
328 {STRING_WITH_LEN("log_type"), LOG_INTEGER, LOG_ITEM_LOG_TYPE},
329 {STRING_WITH_LEN("err_code"), LOG_INTEGER, LOG_ITEM_SQL_ERRCODE},
330 {STRING_WITH_LEN("err_symbol"), LOG_CSTRING, LOG_ITEM_SQL_ERRSYMBOL},
331 {STRING_WITH_LEN("SQL_state"), LOG_CSTRING, LOG_ITEM_SQL_STATE},
332 {STRING_WITH_LEN("OS_errno"), LOG_INTEGER, LOG_ITEM_SYS_ERRNO},
333 {STRING_WITH_LEN("OS_errmsg"), LOG_CSTRING, LOG_ITEM_SYS_STRERROR},
334 {STRING_WITH_LEN("source_file"), LOG_CSTRING, LOG_ITEM_SRC_FILE},
335 {STRING_WITH_LEN("source_line"), LOG_INTEGER, LOG_ITEM_SRC_LINE},
336 {STRING_WITH_LEN("function"), LOG_CSTRING, LOG_ITEM_SRC_FUNC},
337 {STRING_WITH_LEN("subsystem"), LOG_CSTRING, LOG_ITEM_SRV_SUBSYS},
338 {STRING_WITH_LEN("component"), LOG_CSTRING, LOG_ITEM_SRV_COMPONENT},
339 {STRING_WITH_LEN("user"), LOG_LEX_STRING, LOG_ITEM_MSC_USER},
340 {STRING_WITH_LEN("host"), LOG_LEX_STRING, LOG_ITEM_MSC_HOST},
341 {STRING_WITH_LEN("thread"), LOG_INTEGER, LOG_ITEM_SRV_THREAD},
342 {STRING_WITH_LEN("query_id"), LOG_INTEGER, LOG_ITEM_SQL_QUERY_ID},
343 {STRING_WITH_LEN("table"), LOG_CSTRING, LOG_ITEM_SQL_TABLE_NAME},
344 {STRING_WITH_LEN("prio"), LOG_INTEGER, LOG_ITEM_LOG_PRIO},
345 {STRING_WITH_LEN("label"), LOG_CSTRING, LOG_ITEM_LOG_LABEL},
346 {STRING_WITH_LEN("verbatim"), LOG_CSTRING, LOG_ITEM_LOG_VERBATIM},
347 {STRING_WITH_LEN("msg"), LOG_CSTRING, LOG_ITEM_LOG_MESSAGE},
348 {STRING_WITH_LEN("msg_id"), LOG_INTEGER, LOG_ITEM_LOG_LOOKUP},
349 {STRING_WITH_LEN("time"), LOG_CSTRING, LOG_ITEM_LOG_TIMESTAMP},
350 {STRING_WITH_LEN("ts"), LOG_INTEGER, LOG_ITEM_LOG_TS},
351 {STRING_WITH_LEN("buffered"), LOG_INTEGER, LOG_ITEM_LOG_BUFFERED},
352 {STRING_WITH_LEN("and_n_more"), LOG_INTEGER, LOG_ITEM_LOG_SUPPRESSED},
353 /*
354 We should never see the following key names in normal operations
355 (but see the user-specified key instead). These have entries all
356 the same, covering the entirety of log_item_type, so we can use the
357 usual mechanisms for type-to-class mapping etc.
358 We could set the names to nullptr, but they're not much overhead, add
359 readability, and allow for easily creating debug info of the form,
360 "%s:%s=\"%s\"", wellknown_name, item->key, item->value
361 */
362 {STRING_WITH_LEN("misc_float"), LOG_FLOAT, LOG_ITEM_GEN_FLOAT},
363 {STRING_WITH_LEN("misc_integer"), LOG_INTEGER, LOG_ITEM_GEN_INTEGER},
364 {STRING_WITH_LEN("misc_string"), LOG_LEX_STRING, LOG_ITEM_GEN_LEX_STRING},
365 {STRING_WITH_LEN("misc_cstring"), LOG_CSTRING, LOG_ITEM_GEN_CSTRING}};
366
367 static uint log_item_wellknown_keys_count =
368 (sizeof(log_item_wellknown_keys) / sizeof(log_item_wellknown_key));
369
370 /*
371 string helpers
372 */
373
374 /**
375 Compare two NUL-terminated byte strings
376
377 Note that when comparing without length limit, the long string
378 is greater if they're equal up to the length of the shorter
379 string, but the shorter string will be considered greater if
380 its "value" up to that point is greater:
381
382 compare 'abc','abcd': -100 (longer wins if otherwise same)
383 compare 'abca','abcd': -3 (higher value wins)
384 compare 'abcaaaaa','abcd': -3 (higher value wins)
385
386 @param a the first string
387 @param b the second string
388 @param len compare at most this many characters --
389 0 for no limit
390 @param case_insensitive ignore upper/lower case in comparison
391
392 @retval -1 a < b
393 @retval 0 a == b
394 @retval 1 a > b
395 */
log_string_compare(const char * a,const char * b,size_t len,bool case_insensitive)396 int log_string_compare(const char *a, const char *b, size_t len,
397 bool case_insensitive) {
398 if (a == nullptr) /* purecov: begin inspected */
399 return (b == nullptr) ? 0 : -1;
400 else if (b == nullptr)
401 return 1; /* purecov: end */
402 else if (len < 1) // no length limit for comparison
403 {
404 return case_insensitive ? native_strcasecmp(a, b) : strcmp(a, b);
405 }
406
407 return case_insensitive ? native_strncasecmp(a, b, len) : strncmp(a, b, len);
408 }
409
410 /*
411 log item helpers
412 */
413
414 /**
415 Predicate used to determine whether a type is generic
416 (generic string, generic float, generic integer) rather
417 than a well-known type.
418
419 @param t log item type to examine
420
421 @retval true if generic type
422 @retval false if wellknown type
423 */
log_item_generic_type(log_item_type t)424 bool log_item_generic_type(log_item_type t) {
425 return (t & (LOG_ITEM_GEN_CSTRING | LOG_ITEM_GEN_LEX_STRING |
426 LOG_ITEM_GEN_INTEGER | LOG_ITEM_GEN_FLOAT));
427 }
428
429 /**
430 Predicate used to determine whether a class is a string
431 class (C-string or Lex-string).
432
433 @param c log item class to examine
434
435 @retval true if of a string class
436 @retval false if not of a string class
437 */
log_item_string_class(log_item_class c)438 bool log_item_string_class(log_item_class c) {
439 return ((c == LOG_CSTRING) || (c == LOG_LEX_STRING));
440 }
441
442 /**
443 Predicate used to determine whether a class is a numeric
444 class (integer or float).
445
446 @param c log item class to examine
447
448 @retval true if of a numeric class
449 @retval false if not of a numeric class
450 */
log_item_numeric_class(log_item_class c)451 bool log_item_numeric_class(log_item_class c) {
452 return ((c == LOG_INTEGER) || (c == LOG_FLOAT));
453 }
454
455 /**
456 Get an integer value from a log-item of float or integer type.
457
458 @param li log item to get the value from
459 @param[out] i longlong to store the value in
460 */
log_item_get_int(log_item * li,longlong * i)461 void log_item_get_int(log_item *li, longlong *i) /* purecov: begin inspected */
462 {
463 if (li->item_class == LOG_FLOAT)
464 *i = (longlong)li->data.data_float;
465 else
466 *i = (longlong)li->data.data_integer;
467 } /* purecov: end */
468
469 /**
470 Get a float value from a log-item of float or integer type.
471
472 @param li log item to get the value from
473 @param[out] f float to store the value in
474 */
log_item_get_float(log_item * li,double * f)475 void log_item_get_float(log_item *li, double *f) {
476 if (li->item_class == LOG_FLOAT)
477 *f = (float)li->data.data_float;
478 else
479 *f = (float)li->data.data_integer;
480 }
481
482 /**
483 Get a string value from a log-item of C-string or Lex string type.
484
485 @param li log item to get the value from
486 @param[out] str char-pointer to store the pointer to the value in
487 @param[out] len size_t pointer to store the length of the value in
488 */
log_item_get_string(log_item * li,char ** str,size_t * len)489 void log_item_get_string(log_item *li, char **str, size_t *len) {
490 if ((*str = const_cast<char *>(li->data.data_string.str)) == nullptr)
491 *len = 0;
492 else if (li->item_class & LOG_CSTRING)
493 *len = strlen(li->data.data_string.str);
494 else
495 *len = li->data.data_string.length;
496 }
497
498 /**
499 See whether a string is a wellknown field name.
500
501 @param key potential key starts here
502 @param len length of the string to examine
503
504 @retval LOG_ITEM_TYPE_RESERVED: reserved, but not "wellknown" key
505 @retval LOG_ITEM_TYPE_NOT_FOUND: key not found
506 @retval >0: index in array of wellknowns
507 */
log_item_wellknown_by_name(const char * key,size_t len)508 int log_item_wellknown_by_name(const char *key, size_t len) {
509 uint c;
510 // optimize and safeify lookup
511 for (c = 0; (c < log_item_wellknown_keys_count); c++) {
512 if ((log_item_wellknown_keys[c].name_len == len) &&
513 (0 == native_strncasecmp(log_item_wellknown_keys[c].name, key, len))) {
514 if (log_item_generic_type(log_item_wellknown_keys[c].item_type) ||
515 (log_item_wellknown_keys[c].item_type == LOG_ITEM_END))
516 return LOG_ITEM_TYPE_RESERVED;
517 return c;
518 }
519 }
520 return LOG_ITEM_TYPE_NOT_FOUND;
521 }
522
523 /**
524 See whether a type is wellknown.
525
526 @param t log item type to examine
527
528 @retval LOG_ITEM_TYPE_NOT_FOUND: key not found
529 @retval >0: index in array of wellknowns
530 */
log_item_wellknown_by_type(log_item_type t)531 int log_item_wellknown_by_type(log_item_type t) {
532 uint c;
533 // optimize and safeify lookup
534 for (c = 0; (c < log_item_wellknown_keys_count); c++) {
535 if (log_item_wellknown_keys[c].item_type == t) return c;
536 }
537 DBUG_PRINT("warning", ("wellknown_by_type: type %d is not well-known."
538 " Or, you know, known.",
539 t));
540 return LOG_ITEM_TYPE_NOT_FOUND;
541 }
542
543 /**
544 Accessor: from a record describing a wellknown key, get its name
545
546 @param idx index in array of wellknowns, see log_item_wellknown_by_...()
547
548 @retval name (NTBS)
549 */
log_item_wellknown_get_name(uint idx)550 const char *log_item_wellknown_get_name(uint idx) {
551 return log_item_wellknown_keys[idx].name;
552 }
553
554 /**
555 Accessor: from a record describing a wellknown key, get its type
556
557 @param idx index in array of wellknowns, see log_item_wellknown_by_...()
558
559 @retval the log item type for the wellknown key
560 */
log_item_wellknown_get_type(uint idx)561 log_item_type log_item_wellknown_get_type(uint idx) {
562 return log_item_wellknown_keys[idx].item_type;
563 }
564
565 /**
566 Accessor: from a record describing a wellknown key, get its class
567
568 @param idx index in array of wellknowns, see log_item_wellknown_by_...()
569
570 @retval the log item class for the wellknown key
571 */
log_item_wellknown_get_class(uint idx)572 log_item_class log_item_wellknown_get_class(uint idx) {
573 return log_item_wellknown_keys[idx].item_class;
574 }
575
576 /**
577 Sanity check an item.
578 Certain log sinks have very low requirements with regard to the data
579 they receive; they write keys as strings, and then data according to
580 the item's class (string, integer, or float), formatted to the sink's
581 standards (e.g. JSON, XML, ...).
582 Code that has higher requirements can use this check to see whether
583 the given item is of a known type (whether generic or wellknown),
584 whether the given type and class agree, and whether in case of a
585 well-known type, the given key is correct for that type.
586 If your code generates items that don't pass this check, you should
587 probably go meditate on it.
588
589 @param li the log_item to check
590
591 @retval LOG_ITEM_OK no problems
592 @retval LOG_ITEM_TYPE_NOT_FOUND unknown item type
593 @retval LOG_ITEM_CLASS_MISMATCH item_class derived from type isn't
594 what's set on the item
595 @retval LOG_ITEM_KEY_MISMATCH class not generic, so key should
596 match wellknown
597 @retval LOG_ITEM_STRING_NULL class is string, pointer is nullptr
598 @retval LOG_ITEM_KEY_NULL no key set (this is legal e.g. on aux
599 items of filter rules, but should not
600 occur in a log_line, i.e., log_sinks are
601 within their rights to discard such items)
602 */
log_item_inconsistent(log_item * li)603 int log_item_inconsistent(log_item *li) {
604 int w, c;
605
606 // invalid type
607 if ((w = log_item_wellknown_by_type(li->type)) == LOG_ITEM_TYPE_NOT_FOUND)
608 return LOG_ITEM_TYPE_NOT_FOUND;
609
610 // fetch expected storage class for this type
611 if ((c = log_item_wellknown_keys[w].item_class) == LOG_CSTRING)
612 c = LOG_LEX_STRING;
613
614 // class and type don't match
615 if (c != li->item_class) return LOG_ITEM_CLASS_MISMATCH;
616
617 // no key set
618 if (li->key == nullptr) return LOG_ITEM_KEY_NULL;
619
620 // it's not a generic, and key and type don't match
621 if (!log_item_generic_type(li->type) &&
622 (0 != strcmp(li->key, log_item_wellknown_keys[w].name)))
623 return LOG_ITEM_KEY_MISMATCH;
624
625 // strings should have non-nullptr
626 if ((c == LOG_LEX_STRING) && (li->data.data_string.str == nullptr))
627 return LOG_ITEM_STRING_NULL;
628
629 return LOG_ITEM_OK;
630 }
631
632 /**
633 Release any of key and value on a log-item that were dynamically allocated.
634
635 @param li log-item to release the payload of
636 */
log_item_free(log_item * li)637 void log_item_free(log_item *li) {
638 if (li->alloc & LOG_ITEM_FREE_KEY) my_free(const_cast<char *>(li->key));
639
640 if (li->alloc & LOG_ITEM_FREE_VALUE) {
641 if (li->item_class == LOG_LEX_STRING)
642 my_free(const_cast<char *>(li->data.data_string.str));
643 else
644 DBUG_ASSERT(false);
645 }
646
647 li->alloc = LOG_ITEM_FREE_NONE;
648 }
649
650 /**
651 Dynamically allocate and initialize a log_line.
652
653 @retval nullptr could not set up buffer (too small?)
654 @retval other address of the newly initialized log_line
655 */
log_line_init()656 log_line *log_line_init() {
657 log_line *ll;
658 if ((ll = (log_line *)my_malloc(key_memory_log_error_stack, sizeof(log_line),
659 MYF(0))) != nullptr)
660 memset(ll, 0, sizeof(log_line));
661 return ll;
662 }
663
664 /**
665 Release a log_line allocated with line_init()
666
667 @param ll a log_line previously allocated with line_init()
668 */
log_line_exit(log_line * ll)669 void log_line_exit(log_line *ll) {
670 if (ll != nullptr) my_free(ll);
671 }
672
673 /**
674 Predicate indicating whether a log line is "willing" to accept any more
675 key/value pairs.
676
677 @param ll the log-line to examine
678
679 @retval false if not full / if able to accept another log_item
680 @retval true if full
681 */
log_line_full(log_line * ll)682 bool log_line_full(log_line *ll) {
683 return ((ll == nullptr) || (ll->count >= LOG_ITEM_MAX));
684 }
685
686 /**
687 Duplicate a log-event. This is a deep copy where the items (key/value pairs)
688 have their own allocated memory separate from that in the source item.
689
690 @param dst log_line that will hold the copy
691 @param src log_line we copy from
692
693 @retval false on success
694 @retval true if out of memory
695 */
log_line_duplicate(log_line * dst,log_line * src)696 bool log_line_duplicate(log_line *dst, log_line *src) {
697 int c;
698
699 DBUG_ASSERT((dst != nullptr) && (src != nullptr));
700
701 *dst = *src;
702
703 for (c = 0; c < src->count; c++) {
704 dst->item[c].alloc = LOG_ITEM_FREE_NONE;
705
706 if ((dst->item[c].key =
707 my_strndup(key_memory_log_error_loaded_services, src->item[c].key,
708 strlen(src->item[c].key), MYF(0))) != nullptr) {
709 // We just allocated a key, remember to free it later:
710 dst->item[c].alloc = LOG_ITEM_FREE_KEY;
711
712 // If the value is a string, duplicate it, and remember to free it later!
713 if (log_item_string_class(src->item[c].item_class) &&
714 (src->item[c].data.data_string.str != nullptr)) {
715 if ((dst->item[c].data.data_string.str = my_strndup(
716 key_memory_log_error_loaded_services,
717 src->item[c].data.data_string.str,
718 src->item[c].data.data_string.length, MYF(0))) != nullptr)
719 dst->item[c].alloc |= LOG_ITEM_FREE_VALUE;
720 else
721 goto fail; /* purecov: inspected */
722 }
723 } else
724 goto fail; /* purecov: inspected */
725 }
726
727 return false;
728
729 fail: /* purecov: begin inspected */
730 dst->count = c + 1; // consider only the items we actually processed
731 log_line_item_free_all(dst); // free those items
732 return true; // flag failure
733 /* purecov: end */
734 }
735
736 /**
737 How many items are currently set on the given log_line?
738
739 @param ll the log-line to examine
740
741 @retval the number of items set
742 */
log_line_item_count(log_line * ll)743 int log_line_item_count(log_line *ll) { return ll->count; }
744
745 /**
746 Test whether a given type is presumed present on the log line.
747
748 @param ll the log_line to examine
749 @param m the log_type to test for
750
751 @retval 0 not present
752 @retval !=0 present
753 */
log_line_item_types_seen(log_line * ll,log_item_type_mask m)754 log_item_type_mask log_line_item_types_seen(log_line *ll,
755 log_item_type_mask m) {
756 return (ll != nullptr) ? (ll->seen & m) : 0;
757 }
758
759 /**
760 Release log line item (key/value pair) with the index elem in log line ll.
761 This frees whichever of key and value were dynamically allocated.
762 This leaves a "gap" in the bag that may immediately be overwritten
763 with an updated element. If the intention is to remove the item without
764 replacing it, use log_line_item_remove() instead!
765
766 @param ll log_line
767 @param elem index of the key/value pair to release
768 */
log_line_item_free(log_line * ll,size_t elem)769 void log_line_item_free(log_line *ll, size_t elem) {
770 DBUG_ASSERT(ll->count > 0);
771 log_item_free(&(ll->item[elem]));
772 }
773
774 /**
775 Release all log line items (key/value pairs) in log line ll.
776 This frees whichever keys and values were dynamically allocated.
777
778 @param ll log_line
779 */
log_line_item_free_all(log_line * ll)780 void log_line_item_free_all(log_line *ll) {
781 while (ll->count > 0) log_item_free(&(ll->item[--ll->count]));
782 ll->seen = LOG_ITEM_END;
783 }
784
785 /**
786 Release log line item (key/value pair) with the index elem in log line ll.
787 This frees whichever of key and value were dynamically allocated.
788 This moves any trailing items to fill the "gap" and decreases the counter
789 of elements in the log line. If the intention is to leave a "gap" in the
790 bag that may immediately be overwritten with an updated element, use
791 log_line_item_free() instead!
792
793 @param ll log_line
794 @param elem index of the key/value pair to release
795 */
log_line_item_remove(log_line * ll,int elem)796 void log_line_item_remove(log_line *ll, int elem) {
797 DBUG_ASSERT(ll->count > 0);
798
799 log_line_item_free(ll, elem);
800
801 // Fill the gap if needed (if there are more elements and we're not the tail)
802 if ((ll->count > 1) && (elem < (ll->count - 1)))
803 ll->item[elem] = ll->item[ll->count - 1];
804
805 ll->count--;
806 }
807
808 /**
809 Find the (index of the) last key/value pair of the given name
810 in the log line.
811
812 @param ll log line
813 @param key the key to look for
814
815 @retval -1: none found
816 @retval -2: invalid search-key given
817 @retval -3: no log_line given
818 @retval >=0: index of the key/value pair in the log line
819 */
log_line_index_by_name(log_line * ll,const char * key)820 int log_line_index_by_name(log_line *ll, const char *key) {
821 uint32 count = ll->count;
822
823 if (ll == nullptr) /* purecov: begin inspected */
824 return -3;
825 else if ((key == nullptr) || (key[0] == '\0'))
826 return -2; /* purecov: end */
827
828 /*
829 As later items overwrite earlier ones, return the rightmost match!
830 */
831 while (count > 0) {
832 if (0 == strcmp(ll->item[--count].key, key)) return count;
833 }
834
835 return -1;
836 }
837
838 /**
839 Find the last item matching the given key in the log line.
840
841 @param ll log line
842 @param key the key to look for
843
844 @retval nullptr item not found
845 @retval otherwise pointer to the item (not a copy thereof!)
846 */
log_line_item_by_name(log_line * ll,const char * key)847 log_item *log_line_item_by_name(log_line *ll, const char *key) {
848 int i = log_line_index_by_name(ll, key);
849 return (i < 0) ? nullptr : &ll->item[i];
850 }
851
852 /**
853 Find the (index of the) last key/value pair of the given type
854 in the log line.
855
856 @param ll log line
857 @param t the log item type to look for
858
859 @retval <0: none found
860 @retval >=0: index of the key/value pair in the log line
861 */
log_line_index_by_type(log_line * ll,log_item_type t)862 int log_line_index_by_type(log_line *ll, log_item_type t) {
863 uint32 count = ll->count;
864
865 /*
866 As later items overwrite earlier ones, return the rightmost match!
867 */
868 while (count > 0) {
869 if (ll->item[--count].type == t) return count;
870 }
871
872 return -1;
873 }
874
875 /**
876 Find the (index of the) last key/value pair of the given type
877 in the log line. This variant accepts a reference item and looks
878 for an item that is of the same type (for wellknown types), or
879 one that is of a generic type, and with the same key name (for
880 generic types). For example, a reference item containing a
881 generic string with key "foo" will a generic string, integer, or
882 float with the key "foo".
883
884 @param ll log line
885 @param ref a reference item of the log item type to look for
886
887 @retval <0: none found
888 @retval >=0: index of the key/value pair in the log line
889 */
log_line_index_by_item(log_line * ll,log_item * ref)890 int log_line_index_by_item(log_line *ll, log_item *ref) {
891 uint32 count = ll->count;
892
893 if (log_item_generic_type(ref->type)) {
894 while (count > 0) {
895 count--;
896
897 if (log_item_generic_type(ll->item[count].type) &&
898 (native_strcasecmp(ref->key, ll->item[count].key) == 0))
899 return count;
900 }
901 } else {
902 while (count > 0) {
903 if (ll->item[--count].type == ref->type) return count;
904 }
905 }
906
907 return -1;
908 }
909
910 /**
911 Initializes a log entry for use. This simply puts it in a defined
912 state; if you wish to reset an existing item, see log_item_free().
913
914 @param li the log-item to initialize
915 */
log_item_init(log_item * li)916 void log_item_init(log_item *li) { memset(li, 0, sizeof(log_item)); }
917
918 /**
919 Initializes an entry in a log line for use. This simply puts it in
920 a defined state; if you wish to reset an existing item, see
921 log_item_free().
922 This resets the element beyond the last. The element count is not
923 adjusted; this is for the caller to do once it sets up a valid
924 element to suit its needs in the cleared slot. Finally, it is up
925 to the caller to make sure that an element can be allocated.
926
927 @param ll the log-line to initialize a log_item in
928
929 @retval the address of the cleared log_item
930 */
log_line_item_init(log_line * ll)931 log_item *log_line_item_init(log_line *ll) {
932 log_item_init(&ll->item[ll->count]);
933 return &ll->item[ll->count];
934 }
935
936 /**
937 Create new log item with key name "key", and allocation flags of
938 "alloc" (see enum_log_item_free).
939 Will return a pointer to the item's log_item_data struct for
940 convenience.
941 This is mostly interesting for filters and other services that create
942 items that are not part of a log_line; sources etc. that intend to
943 create an item for a log_line (the more common case) should usually
944 use the below line_item_set_with_key() which creates an item (like
945 this function does), but also correctly inserts it into a log_line.
946
947 @param li the log_item to work on
948 @param t the item-type
949 @param key the key to set on the item.
950 ignored for non-generic types (may pass nullptr for those)
951 see alloc
952 @param alloc LOG_ITEM_FREE_KEY if key was allocated by caller
953 LOG_ITEM_FREE_NONE if key was not allocated
954 Allocated keys will automatically free()d when the
955 log_item is.
956 The log_item's alloc flags will be set to the
957 submitted value; specifically, any pre-existing
958 value will be clobbered. It is therefore WRONG
959 a) to use this on a log_item that already has a key;
960 it should only be used on freshly init'd log_items;
961 b) to use this on a log_item that already has a
962 value (specifically, an allocated one); the correct
963 order is to init a log_item, then set up type and
964 key, and finally to set the value. If said value is
965 an allocated string, the log_item's alloc should be
966 bitwise or'd with LOG_ITEM_FREE_VALUE.
967
968 @retval a pointer to the log_item's log_data, for easy chaining:
969 log_item_set_with_key(...)->data_integer= 1;
970 */
log_item_set_with_key(log_item * li,log_item_type t,const char * key,uint32 alloc)971 log_item_data *log_item_set_with_key(log_item *li, log_item_type t,
972 const char *key, uint32 alloc) {
973 int c = log_item_wellknown_by_type(t);
974
975 li->alloc = alloc;
976 if (log_item_generic_type(t)) {
977 li->key = key;
978 } else {
979 li->key = log_item_wellknown_keys[c].name;
980 DBUG_ASSERT((alloc & LOG_ITEM_FREE_KEY) == 0);
981 }
982
983 // If we accept a C-string as input, it'll become a Lex string internally
984 if ((li->item_class = log_item_wellknown_keys[c].item_class) == LOG_CSTRING)
985 li->item_class = LOG_LEX_STRING;
986
987 li->type = t;
988
989 DBUG_ASSERT(((alloc & LOG_ITEM_FREE_VALUE) == 0) ||
990 (li->item_class == LOG_CSTRING) ||
991 (li->item_class == LOG_LEX_STRING));
992
993 return &li->data;
994 }
995
996 /**
997 Create new log item in log line "ll", with key name "key", and
998 allocation flags of "alloc" (see enum_log_item_free).
999 On success, the number of registered items on the log line is increased,
1000 the item's type is added to the log_line's "seen" property,
1001 and a pointer to the item's log_item_data struct is returned for
1002 convenience.
1003
1004 @param ll the log_line to work on
1005 @param t the item-type
1006 @param key the key to set on the item.
1007 ignored for non-generic types (may pass nullptr for those)
1008 see alloc
1009 @param alloc LOG_ITEM_FREE_KEY if key was allocated by caller
1010 LOG_ITEM_FREE_NONE if key was not allocated
1011 Allocated keys will automatically free()d when the
1012 log_item is.
1013 The log_item's alloc flags will be set to the
1014 submitted value; specifically, any pre-existing
1015 value will be clobbered. It is therefore WRONG
1016 a) to use this on a log_item that already has a key;
1017 it should only be used on freshly init'd log_items;
1018 b) to use this on a log_item that already has a
1019 value (specifically, an allocated one); the correct
1020 order is to init a log_item, then set up type and
1021 key, and finally to set the value. If said value is
1022 an allocated string, the log_item's alloc should be
1023 bitwise or'd with LOG_ITEM_FREE_VALUE.
1024
1025 @retval !nullptr a pointer to the log_item's log_data, for easy chaining:
1026 log_line_item_set_with_key(...)->data_integer= 1;
1027 @retval nullptr could not create a log_item in given log_line
1028 */
log_line_item_set_with_key(log_line * ll,log_item_type t,const char * key,uint32 alloc)1029 log_item_data *log_line_item_set_with_key(log_line *ll, log_item_type t,
1030 const char *key, uint32 alloc) {
1031 log_item *li;
1032
1033 if (log_line_full(ll)) return nullptr;
1034
1035 li = &(ll->item[ll->count]);
1036
1037 log_item_set_with_key(li, t, key, alloc);
1038 ll->seen |= t;
1039 ll->count++;
1040
1041 return &li->data;
1042 }
1043
1044 /**
1045 As log_item_set_with_key(), except that the key is automatically
1046 derived from the wellknown log_item_type t.
1047
1048 Create new log item with type "t".
1049 Will return a pointer to the item's log_item_data struct for
1050 convenience.
1051 This is mostly interesting for filters and other services that create
1052 items that are not part of a log_line; sources etc. that intend to
1053 create an item for a log_line (the more common case) should usually
1054 use the below line_item_set_with_key() which creates an item (like
1055 this function does), but also correctly inserts it into a log_line.
1056
1057 The allocation of this item will be LOG_ITEM_FREE_NONE;
1058 specifically, any pre-existing value will be clobbered.
1059 It is therefore WRONG
1060 a) to use this on a log_item that already has a key;
1061 it should only be used on freshly init'd log_items;
1062 b) to use this on a log_item that already has a
1063 value (specifically, an allocated one); the correct
1064 order is to init a log_item, then set up type and
1065 key, and finally to set the value. If said value is
1066 an allocated string, the log_item's alloc should be
1067 bitwise or'd with LOG_ITEM_FREE_VALUE.
1068
1069 @param li the log_item to work on
1070 @param t the item-type
1071
1072 @retval a pointer to the log_item's log_data, for easy chaining:
1073 log_item_set_with_key(...)->data_integer= 1;
1074 */
log_item_set(log_item * li,log_item_type t)1075 log_item_data *log_item_set(log_item *li, log_item_type t) {
1076 return log_item_set_with_key(li, t, nullptr, LOG_ITEM_FREE_NONE);
1077 }
1078
1079 /**
1080 Create a new log item of well-known type "t" in log line "ll".
1081 On success, the number of registered items on the log line is increased,
1082 the item's type is added to the log_line's "seen" property,
1083 and a pointer to the item's log_item_data struct is returned for
1084 convenience.
1085
1086 The allocation of this item will be LOG_ITEM_FREE_NONE;
1087 specifically, any pre-existing value will be clobbered.
1088 It is therefore WRONG
1089 a) to use this on a log_item that already has a key;
1090 it should only be used on freshly init'd log_items;
1091 b) to use this on a log_item that already has a
1092 value (specifically, an allocated one); the correct
1093 order is to init a log_item, then set up type and
1094 key, and finally to set the value. If said value is
1095 an allocated string, the log_item's alloc should be
1096 bitwise or'd with LOG_ITEM_FREE_VALUE.
1097
1098 @param ll the log_line to work on
1099 @param t the item-type
1100
1101 @retval !nullptr a pointer to the log_item's log_data, for easy chaining:
1102 log_line_item_set(...)->data_integer= 1;
1103 @retval nullptr could not create a log_item in given log_line
1104 */
log_line_item_set(log_line * ll,log_item_type t)1105 log_item_data *log_line_item_set(log_line *ll, log_item_type t) {
1106 return log_line_item_set_with_key(ll, t, nullptr, LOG_ITEM_FREE_NONE);
1107 }
1108
1109 /**
1110 Set an integer value on a log_item.
1111 Fails gracefully if no log_item_data is supplied, so it can safely
1112 wrap log_line_item_set[_with_key]().
1113
1114 @param lid log_item_data struct to set the value on
1115 @param i integer to set
1116
1117 @retval true lid was nullptr (possibly: OOM, could not set up log_item)
1118 @retval false all's well
1119 */
log_item_set_int(log_item_data * lid,longlong i)1120 bool log_item_set_int(log_item_data *lid, longlong i) {
1121 if (lid != nullptr) {
1122 lid->data_integer = i;
1123 return false;
1124 }
1125 return true;
1126 }
1127
1128 /**
1129 Set a floating point value on a log_item.
1130 Fails gracefully if no log_item_data is supplied, so it can safely
1131 wrap log_line_item_set[_with_key]().
1132
1133 @param lid log_item_data struct to set the value on
1134 @param f float to set
1135
1136 @retval true lid was nullptr (possibly: OOM, could not set up log_item)
1137 @retval false all's well
1138 */
log_item_set_float(log_item_data * lid,double f)1139 bool log_item_set_float(log_item_data *lid, double f) {
1140 if (lid != nullptr) {
1141 lid->data_float = f;
1142 return false;
1143 }
1144 return true;
1145 }
1146
1147 /**
1148 Set a string value on a log_item.
1149 Fails gracefully if no log_item_data is supplied, so it can safely
1150 wrap log_line_item_set[_with_key]().
1151
1152 @param lid log_item_data struct to set the value on
1153 @param s pointer to string
1154 @param s_len length of string
1155
1156 @retval true lid was nullptr (possibly: OOM, could not set up log_item)
1157 @retval false all's well
1158 */
log_item_set_lexstring(log_item_data * lid,const char * s,size_t s_len)1159 bool log_item_set_lexstring(log_item_data *lid, const char *s, size_t s_len) {
1160 if (lid != nullptr) {
1161 lid->data_string.str = (s == nullptr) ? "" : s;
1162 lid->data_string.length = s_len;
1163 return false;
1164 }
1165 return true;
1166 }
1167
1168 /**
1169 Set a string value on a log_item.
1170 Fails gracefully if no log_item_data is supplied, so it can safely
1171 wrap log_line_item_set[_with_key]().
1172
1173 @param lid log_item_data struct to set the value on
1174 @param s pointer to NTBS
1175
1176 @retval true lid was nullptr (possibly: OOM, could not set up log_item)
1177 @retval false all's well
1178 */
log_item_set_cstring(log_item_data * lid,const char * s)1179 bool log_item_set_cstring(log_item_data *lid, const char *s) {
1180 if (lid != nullptr) {
1181 lid->data_string.str = (s == nullptr) ? "" : s;
1182 lid->data_string.length = strlen(lid->data_string.str);
1183 return false;
1184 }
1185 return true;
1186 }
1187
1188 /**
1189 Convenience function: Derive a log label ("error", "warning",
1190 "information") from a severity.
1191
1192 @param prio the severity/prio in question
1193
1194 @return a label corresponding to that priority.
1195 @retval "System" for prio of SYSTEM_LEVEL
1196 @retval "Error" for prio of ERROR_LEVEL
1197 @retval "Warning" for prio of WARNING_LEVEL
1198 @retval "Note" for prio of INFORMATION_LEVEL
1199 */
log_label_from_prio(int prio)1200 const char *log_label_from_prio(int prio) {
1201 switch (prio) {
1202 case SYSTEM_LEVEL:
1203 return "System";
1204 case ERROR_LEVEL:
1205 return "Error";
1206 case WARNING_LEVEL:
1207 return "Warning";
1208 case INFORMATION_LEVEL:
1209 return "Note";
1210 default:
1211 DBUG_ASSERT(false);
1212 return "";
1213 }
1214 }
1215
1216 /**
1217 services: log sinks: basic logging ("classic error-log")
1218 Will write timestamp, label, thread-ID, and message to stderr/file.
1219 If you should not be able to specify a label, one will be generated
1220 for you from the line's priority field.
1221
1222 @param instance instance handle
1223 @param ll the log line to write
1224
1225 @retval int number of added fields, if any
1226 */
log_sink_trad(void * instance MY_ATTRIBUTE ((unused)),log_line * ll)1227 static int log_sink_trad(void *instance MY_ATTRIBUTE((unused)), log_line *ll) {
1228 const char *label = "", *msg = "";
1229 int c, out_fields = 0;
1230 size_t msg_len = 0, iso_len = 0, label_len = 0, subsys_len = 0;
1231 enum loglevel prio = ERROR_LEVEL;
1232 unsigned int errcode = 0;
1233 log_item_type item_type = LOG_ITEM_END;
1234 log_item_type_mask out_types = 0;
1235 const char *iso_timestamp = "", *subsys = "";
1236 my_thread_id thread_id = 0;
1237 ulonglong microtime = 0;
1238
1239 if (ll->count > 0) {
1240 for (c = 0; c < ll->count; c++) {
1241 item_type = ll->item[c].type;
1242
1243 if (log_item_inconsistent(&ll->item[c])) continue;
1244
1245 out_fields++;
1246
1247 switch (item_type) {
1248 case LOG_ITEM_LOG_BUFFERED:
1249 microtime = (ulonglong)ll->item[c].data.data_integer;
1250 break;
1251 case LOG_ITEM_SQL_ERRCODE:
1252 errcode = (unsigned int)ll->item[c].data.data_integer;
1253 break;
1254 case LOG_ITEM_LOG_PRIO:
1255 prio = (enum loglevel)ll->item[c].data.data_integer;
1256 break;
1257 case LOG_ITEM_LOG_MESSAGE:
1258 msg = ll->item[c].data.data_string.str;
1259 msg_len = ll->item[c].data.data_string.length;
1260 break;
1261 case LOG_ITEM_LOG_LABEL:
1262 label = ll->item[c].data.data_string.str;
1263 label_len = ll->item[c].data.data_string.length;
1264 break;
1265 case LOG_ITEM_SRV_SUBSYS:
1266 subsys = ll->item[c].data.data_string.str;
1267 if ((subsys_len = ll->item[c].data.data_string.length) > 12)
1268 subsys_len = 12;
1269 break;
1270 case LOG_ITEM_LOG_TIMESTAMP:
1271 iso_timestamp = ll->item[c].data.data_string.str;
1272 iso_len = ll->item[c].data.data_string.length;
1273 break;
1274 case LOG_ITEM_SRV_THREAD:
1275 thread_id = (my_thread_id)ll->item[c].data.data_integer;
1276 break;
1277 default:
1278 out_fields--;
1279 }
1280 out_types |= item_type;
1281 }
1282
1283 /*
1284 Prevent duplicates in incremental flush of buffered log events.
1285 If start-up is very slow, we'll let error-log event buffering
1286 "time out": we'll print the events so far to the traditional
1287 error log (and only to that log, as external log-writers cannot
1288 be loaded until later, when InnoDB, upon which the component
1289 framework depends, has been initialized). Then, we'll go back
1290 to buffering. Eventually, we'll either time-out and flush again,
1291 or start-up completes, and we can do a proper flush of the
1292 buffered error-events.
1293 At that point, any loaded log-writers will be called for the
1294 first time, whereas this here traditional writer may have been
1295 called upon multiple times to give incremental updates.
1296 Therefore, we must prevent duplicates in this, and only in this
1297 writer. Ready? (Who lives in a time-out under the C? Buffered
1298 events, in trad sink!)
1299 */
1300 if (out_types & LOG_ITEM_LOG_BUFFERED) {
1301 // abort if we've already printed this (to trad log only!)
1302 if (microtime <= log_sink_trad_last) return 0;
1303 // remember micro-time of last buffered event we've printed
1304 log_sink_trad_last = microtime;
1305 }
1306
1307 if (!(out_types & LOG_ITEM_LOG_MESSAGE)) {
1308 msg =
1309 "No error message, or error message of non-string type. "
1310 "This is almost certainly a bug!";
1311 msg_len = strlen(msg);
1312
1313 prio = ERROR_LEVEL; // force severity
1314 out_types &= ~(LOG_ITEM_LOG_LABEL); // regenerate label
1315 out_types |= LOG_ITEM_LOG_MESSAGE; // we added a message
1316 }
1317
1318 {
1319 char buff_line[LOG_BUFF_MAX];
1320 size_t len;
1321
1322 if (!(out_types & LOG_ITEM_LOG_LABEL)) {
1323 label = (prio == ERROR_LEVEL) ? "ERROR" : log_label_from_prio(prio);
1324 label_len = strlen(label);
1325 }
1326
1327 if (!(out_types & LOG_ITEM_LOG_TIMESTAMP)) {
1328 char buff_local_time[iso8601_size];
1329
1330 make_iso8601_timestamp(buff_local_time, my_micro_time(),
1331 opt_log_timestamps);
1332 iso_timestamp = buff_local_time;
1333 iso_len = strlen(buff_local_time);
1334 }
1335
1336 /*
1337 WL#11009 adds "error identifier" as a field in square brackets
1338 that directly precedes the error message. As a result, new
1339 tools can check for the presence of this field by testing
1340 whether the first character of the presumed message field is '['.
1341 Older tools will just consider this identifier part of the
1342 message; this should therefore not affect log aggregation.
1343 Tools reacting to the contents of the message may wish to
1344 use the new field instead as it's simpler to parse.
1345 The rules are like so:
1346
1347 '[' [ <namespace> ':' ] <identifier> ']'
1348
1349 That is, an error identifier may be namespaced by a
1350 subsystem/component name and a ':'; the identifier
1351 itself should be considered opaque; in particular, it
1352 may be non-numerical: [ <alpha> | <digit> | '_' | '.' | '-' ]
1353 */
1354 len = snprintf(buff_line, sizeof(buff_line),
1355 "%.*s %u [%.*s] [MY-%06u] [%.*s] %.*s", (int)iso_len,
1356 iso_timestamp, thread_id, (int)label_len, label, errcode,
1357 (int)subsys_len, subsys, (int)msg_len, msg);
1358
1359 log_write_errstream(buff_line, len);
1360
1361 return out_fields; // returning number of processed items
1362 }
1363 }
1364 return 0;
1365 }
1366
1367 /**
1368 services: log sinks: buffered logging
1369
1370 During start-up, we buffer log-info until a) we have basic info for
1371 the built-in logger (what file to log to, verbosity, and so on), and
1372 b) advanced info (any logging components to load, any configuration
1373 for them, etc.).
1374
1375 As a failsafe, if start-up takes very, very long, and a time-out is
1376 reached before reaching b) and we actually have something worth
1377 reporting (e.g. errors, as opposed to info), we try to keep the user
1378 informed by using the basic logger configured in a), while going on
1379 buffering all info and flushing it to any advanced loggers when b)
1380 is reached.
1381
1382 1) This function checks and, if needed, updates the time-out, and calls
1383 the flush functions as needed. It is internal to the logger and should
1384 not be called from elsewhere.
1385
1386 2) Function will save log-event (if given) for later filtering and output.
1387
1388 3) Function acquires/releases THR_LOCK_log_buffered if initialized.
1389
1390 @param instance instance handle
1391 Not currently used in this writer;
1392 if this changes later, keep in mind
1393 that nullptr will be passed if this
1394 is called before the structured
1395 logger's locks are initialized, so
1396 that must remain a valid argument!
1397 @param ll The log line to write,
1398 or nullptr to not add a new logline,
1399 but to just check whether the time-out
1400 has been reached and if so, flush
1401 as needed.
1402
1403 @retval -1 can not add event to buffer (OOM?)
1404 @retval >0 number of added fields
1405 */
log_sink_buffer(void * instance MY_ATTRIBUTE ((unused)),log_line * ll)1406 static int log_sink_buffer(void *instance MY_ATTRIBUTE((unused)),
1407 log_line *ll) {
1408 log_line_buffer *llb = nullptr; ///< log-line buffer
1409 ulonglong now = 0;
1410 int count = 0;
1411
1412 /*
1413 If we were actually given an event, add it to the buffer.
1414 */
1415 if (ll != nullptr) {
1416 if ((llb = (log_line_buffer *)my_malloc(key_memory_log_error_stack,
1417 sizeof(log_line_buffer), MYF(0))) ==
1418 nullptr)
1419 return -1; /* purecov: inspected */
1420
1421 llb->next = nullptr;
1422
1423 /*
1424 Don't let the submitter free the keys/values; we'll do it later when
1425 the buffer is flushed and then de-allocated!
1426 (No lock needed for copy as the target-event is still private to this
1427 function, and the source-event is alloc'd in the caller so will be
1428 there at least until we return.)
1429 */
1430 log_line_duplicate(&llb->ll, ll);
1431
1432 /*
1433 Remember it when an ERROR or SYSTEM prio event was buffered.
1434 If buffered logging times out and the buffer contains such an event,
1435 we force a premature flush so the user will know what's going on.
1436 */
1437 {
1438 int index_prio = log_line_index_by_type(&llb->ll, LOG_ITEM_LOG_PRIO);
1439
1440 if ((index_prio >= 0) &&
1441 (llb->ll.item[index_prio].data.data_integer <= ERROR_LEVEL))
1442 log_buffering_flushworthy = true;
1443 }
1444 }
1445
1446 /*
1447 Insert the new last event into the buffer
1448 (a singly linked list of events).
1449 */
1450 if (log_builtins_inited) mysql_mutex_lock(&THR_LOCK_log_buffered);
1451
1452 now = my_micro_time();
1453
1454 if (ll != nullptr) {
1455 /*
1456 Prevent two events from receiving the exact same timestamp on
1457 systems with low resolution clocks.
1458 */
1459 if (now > log_sink_buffer_last)
1460 log_sink_buffer_last = now;
1461 else
1462 log_sink_buffer_last++;
1463
1464 /*
1465 Save the current time so we can regenerate the textual timestamp
1466 later when we have the command-line options telling us what format
1467 it should be in (e.g. UTC or system time).
1468 */
1469 if (!log_line_full(&llb->ll)) {
1470 log_line_item_set(&llb->ll, LOG_ITEM_LOG_BUFFERED)->data_integer =
1471 log_sink_buffer_last;
1472 }
1473
1474 *log_line_buffer_tail = llb;
1475 log_line_buffer_tail = &(llb->next);
1476
1477 /*
1478 Save the element-count now as the time-out flush below may release
1479 the underlying log line buffer, llb, making that info inaccessible.
1480 */
1481 count = llb->ll.count;
1482 }
1483
1484 /*
1485 Handle buffering time-out!
1486 */
1487
1488 // Buffering very first event; set up initial time-out ...
1489 if (log_buffering_timeout == 0)
1490 log_buffering_timeout =
1491 now + (LOG_BUFFERING_TIMEOUT_AFTER * LOG_BUFFERING_TIME_SCALE);
1492
1493 if (log_error_stage_get() == LOG_ERROR_STAGE_BUFFERING_UNIPLEX) {
1494 /*
1495 When not multiplexing several log-writers into the same stream,
1496 we need not delay.
1497 */
1498 log_sink_buffer_flush(LOG_BUFFER_REPORT_AND_KEEP);
1499 }
1500
1501 // Need to flush? Check on time-out, or when explicitly asked to.
1502 else if ((now > log_buffering_timeout) || (ll == nullptr)) {
1503 // We timed out. Flush, and set up new, possibly shorter subsequent timeout.
1504
1505 /*
1506 Timing out is somewhat awkward. Ideally, it shouldn't happen at all;
1507 but as long as it does, it is extremely unlikely to happen during early
1508 set-up -- instead, it might happen during engine set-up ("cannot lock
1509 DB file, another server instance already has it", "applying large
1510 binlog", etc.).
1511
1512 The good news is that this means we've already parsed the command line
1513 options; we have the timestamp format, the error log file, the verbosity,
1514 and the suppression list (if any).
1515
1516 The bad news is, if the engine we're waiting for is InnoDB, which the
1517 component framework persists its component list in, the log-writers
1518 selected by the DBA won't have been loaded yet. This of course means
1519 that the time-out flushes will only be available in the built-in,
1520 "traditional" format, but at least this way, the user gets updates
1521 during long waits. Additionally if the server exits during start-up
1522 (i.e. before full logging is available), we'll have log info of what
1523 happened (albeit not in the preferred format).
1524
1525 If start-up completes and external log-services become available,
1526 we will flush all buffered messages to any external log-writers
1527 requested by the user (using their preferred log-filtering set-up
1528 as well).
1529 */
1530
1531 // If anything of sufficient priority is in the buffer ...
1532 if (log_buffering_flushworthy) {
1533 /*
1534 ... log it now to the traditional log, but keep the buffered
1535 events around in case we need to write them to loadable log-sinks
1536 later.
1537 */
1538 log_sink_buffer_flush(LOG_BUFFER_REPORT_AND_KEEP);
1539 // Remember that all high-prio events so far have now been shown.
1540 log_buffering_flushworthy = false;
1541 }
1542
1543 // Whether we've needed to flush or not, start a new window:
1544 log_buffering_timeout =
1545 now + (LOG_BUFFERING_TIMEOUT_EVERY * LOG_BUFFERING_TIME_SCALE);
1546 }
1547
1548 if (log_builtins_inited) mysql_mutex_unlock(&THR_LOCK_log_buffered);
1549
1550 return count;
1551 }
1552
1553 /**
1554 Convenience function. Same as log_sink_buffer(), except we only
1555 check whether the time-out for buffered logging has been reached
1556 during start-up, and act accordingly; no new logging information
1557 is added (i.e., we only provide functionality 1 described in the
1558 preamble for log_sink_buffer() above).
1559 */
log_sink_buffer_check_timeout(void)1560 void log_sink_buffer_check_timeout(void) { log_sink_buffer(nullptr, nullptr); }
1561
1562 /**
1563 Process all buffered log-events.
1564
1565 LOG_BUFFER_DISCARD_ONLY simply releases all buffered log-events
1566 (used when called from discard_error_log_messages()).
1567
1568 LOG_BUFFER_PROCESS_AND_DISCARD sends the events through the
1569 configured error log stack first before discarding them.
1570 (used when called from flush_error_log_messages()).
1571 Must remain safe to call repeatedly (with subsequent calls only
1572 outputting any events added after the previous flush).
1573
1574 LOG_BUFFER_REPORT_AND_KEEP is used to show incremental status
1575 updates when the start-up takes unusually long. When that happens,
1576 we "report" the intermediate state using the built-in sink
1577 (as that's the only one we available to us at the time), and
1578 "keep" the events around. That way if log sinks other that the
1579 built-in one are configured, we can flush all of the buffered
1580 events there once initialization completes.
1581 (used when log_sink_buffer() detects a time-out, see also:
1582 LOG_BUFFERING_TIMEOUT_AFTER, LOG_BUFFERING_TIMEOUT_EVERY)
1583
1584 @param mode LOG_BUFFER_DISCARD_ONLY (to just
1585 throw away the buffered events), or
1586 LOG_BUFFER_PROCESS_AND_DISCARD to
1587 filter/print them first, or
1588 LOG_BUFFER_REPORT_AND_KEEP to print
1589 an intermediate report on time-out.
1590
1591 To use LOG_BUFFER_REPORT_AND_KEEP in a
1592 multi-threaded environment, the caller
1593 needs to hold THR_LOCK_log_buffered
1594 while calling this; for the other modes,
1595 this function will acquire and release the
1596 lock as needed; in this second scenario,
1597 the lock will not be held while calling
1598 log_services (via log_line_submit()).
1599 */
log_sink_buffer_flush(enum log_sink_buffer_flush_mode mode)1600 void log_sink_buffer_flush(enum log_sink_buffer_flush_mode mode) {
1601 log_line_buffer *llp, *local_head, *local_tail = nullptr;
1602
1603 /*
1604 "steal" public list of buffered log events
1605
1606 The general mechanism is that we move the buffered events from
1607 the global list to one local to this function, iterate over
1608 it, and then put it back. If anything was added to the global
1609 list we emptied while were working, we append the new items
1610 from the global list to the end of the "stolen" list, and then
1611 make that union the new global list. The grand idea here is
1612 that this way, we only have to acquire a lock very briefly
1613 (while updating the global list's head and tail, once for
1614 "stealing" it, once for giving it back), rather than holding
1615 a lock the entire time, or locking each event individually,
1616 while still remaining safe if one caller starts a
1617 flush-with-print, and then another runs a flush-to-delete
1618 that might catch up and cause trouble if we neither held
1619 a lock nor stole the list.
1620 */
1621
1622 /*
1623 If the lock hasn't been init'd yet, don't get it.
1624
1625 Likewise don't get it in LOG_BUFFER_REPORT_AND_KEEP mode as
1626 then the caller already has it. We generally only grab the lock
1627 here when coming from log.cc's discard_error_log_messages() or
1628 flush_error_log_messages().
1629 */
1630 if (log_builtins_inited && (mode != LOG_BUFFER_REPORT_AND_KEEP))
1631 mysql_mutex_lock(&THR_LOCK_log_buffered);
1632
1633 local_head = log_line_buffer_start; // save list head
1634 log_line_buffer_start = nullptr; // empty public list
1635 log_line_buffer_tail = &log_line_buffer_start; // adjust tail of public list
1636
1637 if (log_builtins_inited && (mode != LOG_BUFFER_REPORT_AND_KEEP))
1638 mysql_mutex_unlock(&THR_LOCK_log_buffered);
1639
1640 // get head element from list of buffered log events
1641 llp = local_head;
1642
1643 while (llp != nullptr) {
1644 /*
1645 Forward the buffered lines to log-writers
1646 (other than the buffered writer), unless
1647 we're in "discard" mode, in which case,
1648 we'll just throw the information away.
1649 */
1650 if (mode != LOG_BUFFER_DISCARD_ONLY) {
1651 log_service_instance *lsi = log_service_instances;
1652
1653 // regenerate timestamp with the correct options
1654 char local_time_buff[iso8601_size];
1655 int index_buff = log_line_index_by_type(&llp->ll, LOG_ITEM_LOG_BUFFERED);
1656 int index_time = log_line_index_by_type(&llp->ll, LOG_ITEM_LOG_TIMESTAMP);
1657 ulonglong now;
1658
1659 if (index_buff >= 0)
1660 now = llp->ll.item[index_buff].data.data_integer;
1661 else // we failed to set a timestamp earlier (OOM?), use current time!
1662 now = my_micro_time(); /* purecov: inspected */
1663
1664 DBUG_EXECUTE_IF("log_error_normalize", { now = 0; });
1665
1666 make_iso8601_timestamp(local_time_buff, now, opt_log_timestamps);
1667 char *date = my_strndup(key_memory_log_error_stack, local_time_buff,
1668 strlen(local_time_buff) + 1, MYF(0));
1669
1670 if (date != nullptr) {
1671 if (index_time >= 0) {
1672 // release old timestamp value
1673 if (llp->ll.item[index_time].alloc & LOG_ITEM_FREE_VALUE) {
1674 my_free(const_cast<char *>(
1675 llp->ll.item[index_time].data.data_string.str));
1676 }
1677 // set new timestamp value
1678 llp->ll.item[index_time].data.data_string.str = date;
1679 llp->ll.item[index_time].data.data_string.length = strlen(date);
1680 llp->ll.item[index_time].alloc |= LOG_ITEM_FREE_VALUE;
1681 } else if (!log_line_full(&llp->ll)) { /* purecov: begin inspected */
1682 // set all new timestamp key/value pair; we didn't previously have one
1683 // This shouldn't happen unless we run out of space during submit()!
1684 log_item_data *d =
1685 log_line_item_set(&llp->ll, LOG_ITEM_LOG_TIMESTAMP);
1686 if (d != nullptr) {
1687 d->data_string.str = local_time_buff;
1688 d->data_string.length = strlen(d->data_string.str);
1689 llp->ll.item[llp->ll.count].alloc |= LOG_ITEM_FREE_VALUE;
1690 llp->ll.seen |= LOG_ITEM_LOG_TIMESTAMP;
1691 } else
1692 my_free(date); // couldn't create key/value pair for timestamp
1693 } else
1694 my_free(date); // log_line is full
1695 } /* purecov: end */
1696
1697 /*
1698 If no log-service is configured (because log_builtins_init()
1699 hasn't run and initialized the log-pipeline), or if the
1700 log-service is the buffered writer (which is only available
1701 internally and used during start-up), it's still early in the
1702 start-up sequence, so we may not have all configured external
1703 log-components yet. Therefore, and since this is a fallback
1704 only, we ignore the configuration and use the built-in services
1705 that we know are available, on the grounds that when something
1706 goes wrong, information with undesired formatting is still
1707 better than not knowing about the issue at all.
1708 */
1709 if ((lsi == nullptr) || (lsi->sce->chistics & LOG_SERVICE_BUFFER)) {
1710 /*
1711 This is a fallback used when start-up takes very long.
1712
1713 In fallback modes, we run with default settings and
1714 services.
1715
1716 In "keep" mode (that is, "start-up's taking a long time,
1717 so show the user the info so far in the basic format, but
1718 keep the log-events so we can log them properly later"),
1719 we expect to be able to log the events with the correct
1720 settings and services later (if start-up gets that far).
1721 As the services we use in the meantime may change or even
1722 remove log events, we'll let the services work on a
1723 temporary copy of the events here. The final logging will
1724 then use the original event. This way, no information is
1725 lost.
1726 */
1727 if (mode == LOG_BUFFER_REPORT_AND_KEEP) {
1728 log_line temp_line; // copy the line so the filter may mangle it
1729
1730 log_line_duplicate(&temp_line, &llp->ll);
1731
1732 // Only run the built-in filter if any rules are defined.
1733 if (log_filter_builtin_rules != nullptr)
1734 log_builtins_filter_run(log_filter_builtin_rules, &temp_line);
1735
1736 // Emit to the built-in writer. Empty lines will be ignored.
1737 log_sink_trad(nullptr, &temp_line);
1738
1739 log_line_item_free_all(&temp_line); // release our temporary copy
1740
1741 local_tail = llp;
1742 llp = llp->next; // go to next element, keep head pointer the same
1743 continue; // skip the free()-ing
1744
1745 } else { // LOG_BUFFER_PROCESS_AND_DISCARD
1746 /*
1747 This is a fallback used primarily when start-up is aborted.
1748
1749 If we get here, flush_error_log_messages() was called
1750 before logging came out of buffered mode. (If it was
1751 called after buffered modes completes, we should land
1752 in the following branch instead.)
1753
1754 We're asked to print all log-info so far using basic
1755 logging, and to then throw it away (rather than keep
1756 it around for proper logging). This usually implies
1757 that we're shutting down because some unrecoverable
1758 situation has arisen during start-up, so a) the user
1759 needs to know about it even if full logging (as
1760 configured) is not available yet, and b) we'll shut
1761 down before we'll ever get full logging, so keeping
1762 the info around is pointless.
1763 */
1764
1765 if (log_filter_builtin_rules != nullptr)
1766 log_builtins_filter_run(log_filter_builtin_rules, &llp->ll);
1767
1768 log_sink_trad(nullptr, &llp->ll);
1769 }
1770 } else { // !LOG_SERVICE_BUFFER
1771 /*
1772 If we get here, logging has left the buffered phase, and
1773 we can write out the log-events using the configuration
1774 requested by the user, as it should be!
1775 */
1776 log_line_submit(&llp->ll); // frees keys + values (but not llp itself)
1777 goto kv_freed;
1778 }
1779 }
1780
1781 log_line_item_free_all(&local_head->ll); // free key/value pairs
1782 kv_freed:
1783 local_head = local_head->next; // delist event
1784 my_free(llp); // free buffered event
1785 llp = local_head;
1786 }
1787
1788 if (log_builtins_inited && (mode != LOG_BUFFER_REPORT_AND_KEEP))
1789 mysql_mutex_lock(&THR_LOCK_log_buffered);
1790
1791 /*
1792 At this point, if (local_head == nullptr), we either didn't have
1793 a list to begin with, or we just emptied the local version.
1794 Since we also emptied the global version at the top, whatever's
1795 in there now (still empty, or new events attached while we were
1796 processing) is now authoritive, and no change is needed here.
1797
1798 If local_head is non-NULL, we started with a non-empty local list
1799 and mode was KEEP. In that case, we merge the local list back into
1800 the global one:
1801 */
1802
1803 if (local_head != nullptr) {
1804 DBUG_ASSERT(local_tail != nullptr);
1805
1806 /*
1807 Append global list to end of local list. If global list is still
1808 empty, the global tail pointer points at the global anchor, so
1809 we set the global tail pointer to the next-pointer in last stolen
1810 item. If global list was non-empty, the tail pointer already
1811 points at the correct element's next-pointer (the correct element
1812 being the last one in the global list, as we'll append the
1813 global list to the end of the local list).
1814 */
1815 if ((local_tail->next = log_line_buffer_start) == nullptr)
1816 log_line_buffer_tail = &local_tail->next;
1817
1818 /*
1819 Return the stolen list, with any log-events that happened while
1820 we were processing appended to its end.
1821 */
1822 log_line_buffer_start = local_head;
1823 }
1824
1825 if (log_builtins_inited && (mode != LOG_BUFFER_REPORT_AND_KEEP))
1826 mysql_mutex_unlock(&THR_LOCK_log_buffered);
1827 }
1828
1829 /**
1830 Complete, filter, and write submitted log items.
1831
1832 This expects a log_line collection of log-related key/value pairs,
1833 e.g. from log_message().
1834
1835 Where missing, timestamp, priority, thread-ID (if any) and so forth
1836 are added.
1837
1838 Log item source services, log item filters, and log item sinks are
1839 then called.
1840
1841 @param ll key/value pairs describing info to log
1842
1843 @retval int number of fields in created log line
1844 */
log_line_submit(log_line * ll)1845 int log_line_submit(log_line *ll) {
1846 log_item_iter iter_save;
1847
1848 DBUG_TRACE;
1849
1850 /*
1851 The log-services we'll call below are likely to change the default
1852 iter. Since log-services are allowed to call the logger, we'll save
1853 the iter on entry and restore it on exit to be properly re-entrant in
1854 that regard.
1855 */
1856 iter_save = ll->iter;
1857 ll->iter.ll = nullptr;
1858
1859 /*
1860 If anything of what was submitted survived, proceed ...
1861 */
1862 if (ll->count > 0) {
1863 THD *thd = nullptr;
1864
1865 // avoid some allocs/frees.
1866 char local_time_buff[iso8601_size];
1867 char strerr_buf[MYSYS_STRERROR_SIZE];
1868
1869 /* auto-add a prio item */
1870 if (!(ll->seen & LOG_ITEM_LOG_PRIO) && !log_line_full(ll)) {
1871 log_line_item_set(ll, LOG_ITEM_LOG_PRIO)->data_integer = ERROR_LEVEL;
1872 }
1873
1874 /* auto-add a timestamp item if needed */
1875 if (!(ll->seen & LOG_ITEM_LOG_TIMESTAMP) && !log_line_full(ll)) {
1876 log_item_data *d;
1877 ulonglong now = my_micro_time();
1878
1879 DBUG_EXECUTE_IF("log_error_normalize", { now = 0; });
1880
1881 make_iso8601_timestamp(local_time_buff, now, opt_log_timestamps);
1882
1883 d = log_line_item_set(ll, LOG_ITEM_LOG_TIMESTAMP);
1884 d->data_string.str = local_time_buff;
1885 d->data_string.length = strlen(d->data_string.str);
1886 }
1887
1888 /* auto-add a ts item if needed */
1889 if (!(ll->seen & LOG_ITEM_LOG_TS) && !log_line_full(ll)) {
1890 log_item_data *d;
1891 ulonglong now = my_milli_time();
1892
1893 DBUG_EXECUTE_IF("log_error_normalize", { now = 0; });
1894
1895 d = log_line_item_set(ll, LOG_ITEM_LOG_TS);
1896 d->data_integer = now;
1897 }
1898
1899 /* auto-add a strerror item if relevant and available */
1900 if (!(ll->seen & LOG_ITEM_SYS_STRERROR) && !log_line_full(ll) &&
1901 (ll->seen & LOG_ITEM_SYS_ERRNO)) {
1902 int en; // operating system errno
1903 int n = log_line_index_by_type(ll, LOG_ITEM_SYS_ERRNO);
1904 log_item_data *d = log_line_item_set(ll, LOG_ITEM_SYS_STRERROR);
1905
1906 DBUG_ASSERT(n >= 0);
1907
1908 en = (int)ll->item[n].data.data_integer;
1909 my_strerror(strerr_buf, sizeof(strerr_buf), en);
1910 d->data_string.str = strerr_buf;
1911 d->data_string.length = strlen(d->data_string.str);
1912 }
1913
1914 /* add thread-related info, if available */
1915 if ((thd = current_thd) != nullptr) {
1916 /* auto-add a thread item if needed */
1917 if (!(ll->seen & LOG_ITEM_SRV_THREAD) && !log_line_full(ll)) {
1918 my_thread_id tid = log_get_thread_id(thd);
1919
1920 DBUG_EXECUTE_IF("log_error_normalize", { tid = 0; });
1921
1922 log_line_item_set(ll, LOG_ITEM_SRV_THREAD)->data_integer = tid;
1923 }
1924 }
1925
1926 /* auto-add a symbolic MySQL error code item item if needed */
1927 if (!(ll->seen & LOG_ITEM_SQL_ERRSYMBOL) && !log_line_full(ll) &&
1928 (ll->seen & LOG_ITEM_SQL_ERRCODE)) {
1929 int ec; // MySQL error code
1930 int n = log_line_index_by_type(ll, LOG_ITEM_SQL_ERRCODE);
1931 const char *es;
1932
1933 DBUG_ASSERT(n >= 0);
1934
1935 ec = (int)ll->item[n].data.data_integer;
1936 if ((ec != 0) && ((es = mysql_errno_to_symbol(ec)) != nullptr)) {
1937 log_item_data *d = log_line_item_set(ll, LOG_ITEM_SQL_ERRSYMBOL);
1938 d->data_string.str = es;
1939 d->data_string.length = strlen(d->data_string.str);
1940 }
1941 }
1942 /* auto-add a numeric MySQL error code item item if needed */
1943 else if (!(ll->seen & LOG_ITEM_SQL_ERRCODE) && !log_line_full(ll) &&
1944 (ll->seen & LOG_ITEM_SQL_ERRSYMBOL)) {
1945 const char *es; // MySQL error symbol
1946 int n = log_line_index_by_type(ll, LOG_ITEM_SQL_ERRSYMBOL);
1947 int ec;
1948
1949 DBUG_ASSERT(n >= 0);
1950
1951 es = ll->item[n].data.data_string.str;
1952
1953 DBUG_ASSERT(es != nullptr);
1954
1955 if ((ec = mysql_symbol_to_errno(es)) > 0) {
1956 log_item_data *d = log_line_item_set(ll, LOG_ITEM_SQL_ERRCODE);
1957 d->data_integer = ec;
1958 }
1959 }
1960
1961 /* auto-add a SQL state item item if needed */
1962 if (!(ll->seen & LOG_ITEM_SQL_STATE) && !log_line_full(ll) &&
1963 (ll->seen & LOG_ITEM_SQL_ERRCODE)) {
1964 int ec; // MySQL error code
1965 int n = log_line_index_by_type(ll, LOG_ITEM_SQL_ERRCODE);
1966 const char *es;
1967
1968 if (n < 0) {
1969 n = log_line_index_by_type(ll, LOG_ITEM_SQL_ERRSYMBOL);
1970 DBUG_ASSERT(n >= 0);
1971
1972 es = ll->item[n].data.data_string.str;
1973 DBUG_ASSERT(es != nullptr);
1974
1975 ec = mysql_symbol_to_errno(es);
1976 } else
1977 ec = (int)ll->item[n].data.data_integer;
1978
1979 if ((ec > 0) && ((es = mysql_errno_to_sqlstate((uint)ec)) != nullptr)) {
1980 log_item_data *d = log_line_item_set(ll, LOG_ITEM_SQL_STATE);
1981 d->data_string.str = es;
1982 d->data_string.length = strlen(d->data_string.str);
1983 }
1984 }
1985
1986 /* add the default sub-system if none is set */
1987 if (!(ll->seen & LOG_ITEM_SRV_SUBSYS) && !log_line_full(ll)) {
1988 log_item_data *d = log_line_item_set(ll, LOG_ITEM_SRV_SUBSYS);
1989 d->data_string.str = LOG_SUBSYSTEM_TAG;
1990 d->data_string.length = strlen(d->data_string.str);
1991 }
1992
1993 /* normalize source line if needed */
1994 DBUG_EXECUTE_IF("log_error_normalize", {
1995 if (ll->seen & LOG_ITEM_SRC_LINE) {
1996 int n = log_line_index_by_type(ll, LOG_ITEM_SRC_LINE);
1997
1998 if (n >= 0) {
1999 ll->item[n] = ll->item[ll->count - 1];
2000 ll->count--;
2001 ll->seen &= ~LOG_ITEM_SRC_LINE;
2002 }
2003 }
2004 });
2005
2006 /*
2007 We were called before even the buffered sink (and our locks)
2008 were set up. This usually means that something went
2009 catastrophically wrong, so we'll make sure the information
2010 (e.g. cause of failure) isn't lost.
2011 */
2012 if (!log_builtins_inited)
2013 log_sink_buffer(nullptr, ll);
2014
2015 else {
2016 mysql_rwlock_rdlock(&THR_LOCK_log_stack);
2017
2018 /*
2019 sources:
2020 Add info from other log item sources,
2021 e.g. that supplied by the client on connect using mysql_options4();
2022
2023 filters:
2024 Remove or modify entries
2025
2026 sinks:
2027 Write logs
2028 */
2029
2030 log_service_cache_entry *sce;
2031 log_service_instance *lsi = log_service_instances;
2032
2033 while ((lsi != nullptr) && ((sce = lsi->sce) != nullptr)) {
2034 if (sce->chistics & LOG_SERVICE_BUFFER)
2035 log_sink_buffer(lsi->instance, ll);
2036
2037 else if (!(sce->chistics & LOG_SERVICE_BUILTIN)) {
2038 SERVICE_TYPE(log_service) * ls;
2039
2040 ls = reinterpret_cast<SERVICE_TYPE(log_service) *>(sce->service);
2041
2042 if (ls != nullptr) ls->run(lsi->instance, ll);
2043
2044 } else if (log_service_has_characteristics(
2045 sce, (LOG_SERVICE_BUILTIN | LOG_SERVICE_FILTER)))
2046 log_builtins_filter_run(log_filter_builtin_rules, ll);
2047
2048 else if (log_service_has_characteristics(
2049 sce, (LOG_SERVICE_BUILTIN | LOG_SERVICE_SINK)))
2050 log_sink_trad(lsi->instance, ll);
2051
2052 lsi = lsi->next;
2053 }
2054
2055 mysql_rwlock_unlock(&THR_LOCK_log_stack);
2056 }
2057
2058 #if !defined(DBUG_OFF)
2059 /*
2060 Assert that we're not given anything but server error-log codes
2061 or global error codes (shared between MySQL server and clients).
2062 If your code bombs out here, check whether you're trying to log
2063 using an error-code in the range intended for messages that are
2064 sent to the client, not the error-log, (< ER_SERVER_RANGE_START).
2065 */
2066 if (ll->seen & LOG_ITEM_SQL_ERRCODE) {
2067 int n = log_line_index_by_type(ll, LOG_ITEM_SQL_ERRCODE);
2068 if (n >= 0) {
2069 int ec = (int)ll->item[n].data.data_integer;
2070 DBUG_ASSERT((ec < 1) || (ec >= EE_ERROR_FIRST && ec <= EE_ERROR_LAST) ||
2071 (ec >= ER_SERVER_RANGE_START));
2072 }
2073 }
2074 #endif
2075
2076 // release any memory that might need it
2077 log_line_item_free_all(ll);
2078 }
2079
2080 ll->iter = iter_save;
2081
2082 return ll->count;
2083 }
2084
2085 /**
2086 Make and return an ISO 8601 / RFC 3339 compliant timestamp.
2087 Heeds log_timestamps.
2088
2089 @param buf A buffer of at least 26 bytes to store the timestamp in
2090 (19 + tzinfo tail + \0)
2091 @param utime Microseconds since the epoch
2092 @param mode if 0, use UTC; if 1, use local time
2093
2094 @retval length of timestamp (excluding \0)
2095 */
make_iso8601_timestamp(char * buf,ulonglong utime,int mode)2096 int make_iso8601_timestamp(char *buf, ulonglong utime, int mode) {
2097 struct tm my_tm;
2098 char tzinfo[8] = "Z"; // max 6 chars plus \0
2099 size_t len;
2100 time_t seconds;
2101
2102 seconds = utime / 1000000;
2103 utime = utime % 1000000;
2104
2105 if (mode == 0)
2106 gmtime_r(&seconds, &my_tm);
2107 else if (mode == 1) {
2108 localtime_r(&seconds, &my_tm);
2109
2110 #ifdef HAVE_TM_GMTOFF
2111 /*
2112 The field tm_gmtoff is the offset (in seconds) of the time represented
2113 from UTC, with positive values indicating east of the Prime Meridian.
2114 Originally a BSDism, this is also supported in glibc, so this should
2115 cover the majority of our platforms.
2116 */
2117 long tim = -my_tm.tm_gmtoff;
2118 #else
2119 /*
2120 Work this out "manually".
2121 */
2122 struct tm my_gm;
2123 long tim, gm;
2124 gmtime_r(&seconds, &my_gm);
2125 gm = (my_gm.tm_sec + 60 * (my_gm.tm_min + 60 * my_gm.tm_hour));
2126 tim = (my_tm.tm_sec + 60 * (my_tm.tm_min + 60 * my_tm.tm_hour));
2127 tim = gm - tim;
2128 #endif
2129 char dir = '-';
2130
2131 if (tim < 0) {
2132 dir = '+';
2133 tim = -tim;
2134 }
2135 snprintf(tzinfo, sizeof(tzinfo), "%c%02u:%02u", dir,
2136 (unsigned int)((tim / (60 * 60)) % 100),
2137 (unsigned int)((tim / 60) % 60));
2138 } else {
2139 DBUG_ASSERT(false);
2140 }
2141
2142 len = snprintf(buf, iso8601_size, "%04d-%02d-%02dT%02d:%02d:%02d.%06lu%s",
2143 my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday,
2144 my_tm.tm_hour, my_tm.tm_min, my_tm.tm_sec,
2145 (unsigned long)utime, tzinfo);
2146
2147 return std::min<int>((int)len, iso8601_size - 1);
2148 }
2149
2150 /**
2151 Helper: get token from error stack configuration string
2152
2153 @param[in,out] s start of the token (may be positioned on whitespace
2154 on call; this will be adjusted to the first non-white
2155 character)
2156 @param[out] e end of the token
2157 @param[in,out] d delimiter (in: last used, \0 if none; out: detected here)
2158
2159 @retval <0 an error occur
2160 @retval the length in bytes of the token
2161 */
log_builtins_stack_get_service_from_var(const char ** s,const char ** e,char * d)2162 static size_t log_builtins_stack_get_service_from_var(const char **s,
2163 const char **e, char *d) {
2164 DBUG_ASSERT(s != nullptr);
2165 DBUG_ASSERT(e != nullptr);
2166
2167 // proceed to next service (skip whitespace, and the delimiter once defined)
2168 while (isspace(**s) || ((*d != '\0') && (**s == *d))) (*s)++;
2169
2170 *e = *s;
2171
2172 // find end of service
2173 while ((**e != '\0') && !isspace(**e)) {
2174 if ((**e == ';') || (**e == ',')) {
2175 if (*d == '\0') // no delimiter determined yet
2176 {
2177 if (*e == *s) // token may not start with a delimiter
2178 return -1;
2179 *d = **e; // save the delimiter we found
2180 } else if (**e != *d) // different delimiter than last time: error
2181 return -2;
2182 }
2183 if (**e == *d) // found a valid delimiter; end scan
2184 goto done;
2185 (*e)++; // valid part of token found, go on!
2186 }
2187
2188 done:
2189 return (size_t)(*e - *s);
2190 }
2191
2192 /**
2193 Look up a log service by name (in the service registry).
2194
2195 @param name name of the component
2196 @param len length of that name
2197
2198 @retval a handle to that service.
2199 */
log_service_get_by_name(const char * name,size_t len)2200 static my_h_service log_service_get_by_name(const char *name, size_t len) {
2201 char buf[128];
2202 my_h_service service = nullptr;
2203 size_t needed;
2204
2205 needed =
2206 snprintf(buf, sizeof(buf), LOG_SERVICES_PREFIX ".%.*s", (int)len, name);
2207
2208 if (needed > sizeof(buf)) return service;
2209
2210 if ((!srv_registry->acquire(buf, &service)) && (service != nullptr)) {
2211 SERVICE_TYPE(log_service) * ls;
2212
2213 if ((ls = reinterpret_cast<SERVICE_TYPE(log_service) *>(service)) ==
2214 nullptr) {
2215 srv_registry->release(service);
2216 service = nullptr;
2217 }
2218 } else
2219 service = nullptr;
2220
2221 return service;
2222 }
2223
operator ()(log_service_cache_entry * sce) const2224 void log_service_cache_entry_free::operator()(
2225 log_service_cache_entry *sce) const {
2226 if (sce == nullptr) return;
2227
2228 if (sce->name != nullptr) my_free(sce->name);
2229
2230 DBUG_ASSERT(sce->opened == 0);
2231
2232 if (sce->service != nullptr) srv_registry->release(sce->service);
2233
2234 memset(sce, 0, sizeof(log_service_cache_entry));
2235
2236 my_free(sce);
2237 }
2238
2239 /**
2240 Create a new entry in the cache of log services.
2241
2242 @param name Name of component that provides the service
2243 @param name_len Length of that name
2244 @param srv The handle of the log_service
2245
2246 @retval A new log_service_cache_entry on success
2247 @retval nullptr on failure
2248 */
log_service_cache_entry_new(const char * name,size_t name_len,my_h_service srv)2249 static log_service_cache_entry *log_service_cache_entry_new(const char *name,
2250 size_t name_len,
2251 my_h_service srv) {
2252 char *n = my_strndup(key_memory_log_error_stack, name, name_len, MYF(0));
2253 log_service_cache_entry *sce = nullptr;
2254
2255 if (n != nullptr) {
2256 // make new service cache entry
2257 if ((sce = (log_service_cache_entry *)my_malloc(
2258 key_memory_log_error_stack, sizeof(log_service_cache_entry),
2259 MYF(0))) == nullptr)
2260 my_free(n);
2261 else {
2262 memset(sce, 0, sizeof(log_service_cache_entry));
2263 sce->name = n;
2264 sce->name_len = name_len;
2265 sce->service = srv;
2266 sce->chistics = LOG_SERVICE_UNSPECIFIED;
2267 sce->requested = 0;
2268 sce->opened = 0;
2269 }
2270 }
2271
2272 return sce;
2273 }
2274
2275 /**
2276 Find out characteristics of a service (e.g. whether it is a singleton)
2277 by asking it.
2278
2279 (See log_service_chistics for a list of possible characteristics!)
2280
2281 @param service what service to examine
2282
2283 @retval a set of log_service_chistics flags
2284 */
log_service_get_characteristics(my_h_service service)2285 static int log_service_get_characteristics(my_h_service service) {
2286 SERVICE_TYPE(log_service) * ls;
2287
2288 DBUG_ASSERT(service != nullptr);
2289
2290 ls = reinterpret_cast<SERVICE_TYPE(log_service) *>(service);
2291
2292 // no information available, default to restrictive
2293 if (ls->characteristics == nullptr)
2294 return LOG_SERVICE_UNSPECIFIED |
2295 LOG_SERVICE_SINGLETON; /* purecov: inspected */
2296
2297 return ls->characteristics();
2298 }
2299
2300 /**
2301 Allocate and open a new instance of a given service.
2302
2303 @param sce the cache-entry for the service
2304 @param ll a log_line containing optional parameters, or nullptr
2305
2306 @return a pointer to an instance record or success, nullptr otherwise
2307 */
log_service_instance_new(log_service_cache_entry * sce,log_line * ll)2308 log_service_instance *log_service_instance_new(log_service_cache_entry *sce,
2309 log_line *ll) {
2310 log_service_instance *lsi;
2311
2312 // make new service instance entry
2313 if ((lsi = (log_service_instance *)my_malloc(
2314 key_memory_log_error_stack, sizeof(log_service_instance), MYF(0))) !=
2315 nullptr) {
2316 memset(lsi, 0, sizeof(log_service_instance));
2317 lsi->sce = sce;
2318
2319 DBUG_ASSERT(sce != nullptr);
2320
2321 if (lsi->sce->service != nullptr) {
2322 SERVICE_TYPE(log_service) *ls = nullptr;
2323
2324 ls = reinterpret_cast<SERVICE_TYPE(log_service) *>(lsi->sce->service);
2325
2326 if ((ls == nullptr) ||
2327 ((ls->open != nullptr) && (ls->open(ll, &lsi->instance) < 0)))
2328 goto fail;
2329 }
2330
2331 lsi->sce->opened++;
2332 }
2333
2334 return lsi;
2335
2336 fail:
2337 my_free(lsi);
2338 return nullptr;
2339 }
2340
2341 /**
2342 Close and release all instances of all log services.
2343 */
log_service_instance_release_all()2344 static void log_service_instance_release_all() {
2345 log_service_instance *lsi, *lsi_next;
2346
2347 lsi = log_service_instances;
2348 log_service_instances = nullptr;
2349
2350 // release all instances!
2351 while (lsi != nullptr) {
2352 SERVICE_TYPE(log_service) * ls;
2353
2354 ls = reinterpret_cast<SERVICE_TYPE(log_service) *>(lsi->sce->service);
2355
2356 if (ls != nullptr) {
2357 if (ls->close != nullptr) ls->close(&lsi->instance);
2358 }
2359
2360 lsi->sce->opened--;
2361 lsi_next = lsi->next;
2362 my_free(lsi);
2363 lsi = lsi_next;
2364 }
2365 }
2366
2367 /**
2368 Call flush() on all log_services.
2369 flush() function must not try to log anything, as we hold an
2370 exclusive lock on the stack.
2371
2372 @retval 0 no problems
2373 @retval -1 error
2374 */
log_builtins_error_stack_flush()2375 int log_builtins_error_stack_flush() {
2376 int rr = 0;
2377 log_service_cache_entry *sce;
2378 log_service_instance *lsi;
2379
2380 if (!log_builtins_inited) return 0;
2381
2382 mysql_rwlock_wrlock(&THR_LOCK_log_stack);
2383
2384 lsi = log_service_instances;
2385
2386 while ((lsi != nullptr) && ((sce = lsi->sce) != nullptr)) {
2387 if (!(sce->chistics & LOG_SERVICE_BUILTIN)) {
2388 SERVICE_TYPE(log_service) *ls = nullptr;
2389 ls = reinterpret_cast<SERVICE_TYPE(log_service) *>(sce->service);
2390
2391 if (ls != nullptr) {
2392 if ((ls->flush != nullptr) && (ls->flush(&lsi->instance) < 0)) rr--;
2393 } else
2394 rr--;
2395 }
2396 lsi = lsi->next;
2397 }
2398
2399 mysql_rwlock_unlock(&THR_LOCK_log_stack);
2400
2401 return rr;
2402 }
2403
2404 /**
2405 Set up custom error logging stack.
2406
2407 @param conf The configuration string
2408 @param check_only If true, report on whether configuration is valid
2409 (i.e. whether all requested services are available),
2410 but do not apply the new configuration.
2411 if false, set the configuration (acquire the
2412 necessary services, update the hash by
2413 adding/deleting entries as necessary)
2414 @param[out] pos If an error occurs and this pointer is non-null,
2415 the position in the configuration string where
2416 the error occurred will be written to the
2417 pointed-to size_t.
2418
2419 @retval 0 success
2420 @retval -1 expected delimiter not found
2421 @retval -2 one or more services not found
2422 @retval -3 failed to create service cache entry
2423 @retval -4 tried to open multiple instances of a singleton
2424 @retval -5 failed to create service instance entry
2425 @retval -6 last element in pipeline should be a sink
2426 @retval -7 service only available during start-up;
2427 may not be set by the user
2428 @retval -101 service name may not start with a delimiter
2429 @retval -102 delimiters ',' and ';' may not be mixed
2430 */
log_builtins_error_stack(const char * conf,bool check_only,size_t * pos)2431 int log_builtins_error_stack(const char *conf, bool check_only, size_t *pos) {
2432 const char *start = conf, *end;
2433 char delim = '\0';
2434 long len;
2435 my_h_service service;
2436 int rr = 0;
2437 int count = 0;
2438 log_service_cache_entry *sce = nullptr;
2439 log_service_instance *lsi;
2440 int chistics = LOG_SERVICE_UNSPECIFIED;
2441
2442 mysql_rwlock_wrlock(&THR_LOCK_log_stack);
2443
2444 // if we're actually setting this configuration, release the previous one!
2445 if (!check_only) log_service_instance_release_all();
2446
2447 // clear keep flag on all service cache entries
2448 for (auto &key_and_value : *log_service_cache) {
2449 sce = key_and_value.second.get();
2450 sce->requested = 0;
2451
2452 DBUG_ASSERT(check_only || (sce->opened == 0));
2453 }
2454
2455 sce = nullptr;
2456
2457 lsi = nullptr;
2458 while ((len = log_builtins_stack_get_service_from_var(&start, &end, &delim)) >
2459 0) {
2460 chistics = LOG_SERVICE_UNSPECIFIED;
2461
2462 // more than one services listed, but no delimiter used (only space)
2463 if ((++count > 1) && (delim == '\0')) {
2464 rr = -1; // at least one service not found => fail
2465 goto done;
2466 }
2467
2468 // find current service name in service-cache
2469 auto it = log_service_cache->find(string(start, len));
2470
2471 // not found in cache; is it a built-in default?
2472 if (it == log_service_cache->end()) {
2473 chistics = log_service_check_if_builtin(start, len);
2474
2475 /*
2476 Buffered logging is only used during start-up.
2477 As we set it up internally from a constant, we don't
2478 check the value first, but go straight to the set phase.
2479 If we do see the special sink during the check phase,
2480 it was requested by the user. That is not supported
2481 (as it isn't useful), so we throw an error here.
2482 */
2483 if (check_only && (chistics & LOG_SERVICE_BUFFER)) {
2484 rr = -7;
2485 goto done;
2486 }
2487
2488 // not a built-in; ask component framework
2489 if (!(chistics & LOG_SERVICE_BUILTIN)) {
2490 service = log_service_get_by_name(start, len);
2491
2492 // not found in framework, signal failure
2493 if (service == nullptr) {
2494 rr = -2; // at least one service not found => fail
2495 goto done;
2496 }
2497 } else
2498 service = nullptr;
2499
2500 // make a cache-entry for this service
2501 if ((sce = log_service_cache_entry_new(start, len, service)) == nullptr) {
2502 // failed to make cache-entry. if we hold a service handle, release it!
2503 /* purecov: begin inspected */
2504 if (service != nullptr) srv_registry->release(service);
2505 rr = -3;
2506 goto done; /* purecov: end */
2507 }
2508
2509 // service is not built-in, so we know nothing about it. Ask it!
2510 if ((sce->chistics = chistics) == LOG_SERVICE_UNSPECIFIED)
2511 sce->chistics =
2512 log_service_get_characteristics(service) &
2513 ~LOG_SERVICE_BUILTIN; // loaded service can not be built-in
2514
2515 log_service_cache->emplace(string(sce->name, sce->name_len),
2516 cache_entry_with_deleter(sce));
2517 } else
2518 sce = it->second.get();
2519
2520 // at this point, it's in cache, one way or another
2521 sce->requested++;
2522
2523 if (check_only) {
2524 // tried to multi-open a service that doesn't support it => fail
2525 if ((sce->requested > 1) && (sce->chistics & LOG_SERVICE_SINGLETON)) {
2526 rr = -4;
2527 goto done;
2528 }
2529 } else if ((sce->requested == 1) ||
2530 !(sce->chistics & LOG_SERVICE_SINGLETON)) {
2531 log_service_instance *lsi_new = nullptr;
2532
2533 // actually setting this config, so open this instance!
2534 lsi_new = log_service_instance_new(sce, nullptr);
2535
2536 if (lsi_new != nullptr) // add to chain of instances
2537 {
2538 if (log_service_instances == nullptr)
2539 log_service_instances = lsi_new;
2540 else {
2541 DBUG_ASSERT(lsi != nullptr);
2542 lsi->next = lsi_new;
2543 }
2544
2545 lsi = lsi_new;
2546 } else // could not make new instance entry; fail
2547 {
2548 rr = -5; /* purecov: inspected */
2549 goto done; /* purecov: inspected */
2550 }
2551 }
2552
2553 /*
2554 If neither branch was true, we're in set mode, but the set-up
2555 is invalid (i.e. we're trying to multi-open a singleton). As
2556 this should have been caught in the check phase, we don't
2557 specfically handle it here; the invalid element is skipped and
2558 not added to the instance list; that way, we'll get as close
2559 to a working configuration as possible in our attempt to fail
2560 somewhat gracefully.
2561 */
2562
2563 start = end;
2564 }
2565
2566 if (len < 0)
2567 rr = len - 100;
2568 else if ((sce != nullptr) && !(sce->chistics & LOG_SERVICE_SINK))
2569 rr = -6;
2570 else
2571 rr = 0;
2572
2573 done:
2574 // remove stale entries from cache
2575 for (auto it = log_service_cache->begin(); it != log_service_cache->end();) {
2576 sce = it->second.get();
2577
2578 if (sce->opened <= 0)
2579 it = log_service_cache->erase(it);
2580 else
2581 ++it;
2582 }
2583
2584 mysql_rwlock_unlock(&THR_LOCK_log_stack);
2585
2586 if (pos != nullptr) *pos = (size_t)(start - conf);
2587
2588 return rr;
2589 }
2590
2591 /**
2592 De-initialize the structured logging subsystem.
2593
2594 @retval 0 no errors
2595 @retval -1 not stopping, never started
2596 */
log_builtins_exit()2597 int log_builtins_exit() {
2598 if (!log_builtins_inited) return -1;
2599
2600 mysql_rwlock_wrlock(&THR_LOCK_log_stack);
2601 mysql_mutex_lock(&THR_LOCK_log_buffered);
2602 mysql_mutex_lock(&THR_LOCK_log_syseventlog);
2603
2604 log_builtins_filter_exit();
2605 log_service_instance_release_all();
2606 delete log_service_cache;
2607
2608 log_builtins_inited = false;
2609 log_error_stage_set(LOG_ERROR_STAGE_BUFFERING_EARLY);
2610
2611 mysql_rwlock_unlock(&THR_LOCK_log_stack);
2612 mysql_rwlock_destroy(&THR_LOCK_log_stack);
2613
2614 mysql_mutex_unlock(&THR_LOCK_log_syseventlog);
2615 mysql_mutex_destroy(&THR_LOCK_log_syseventlog);
2616
2617 mysql_mutex_unlock(&THR_LOCK_log_buffered);
2618 mysql_mutex_destroy(&THR_LOCK_log_buffered);
2619
2620 return 0;
2621 }
2622
2623 /**
2624 Initialize the structured logging subsystem.
2625
2626 Since we're initializing various locks here, we must call this late enough
2627 so this is clean, but early enough so it still happens while we're running
2628 single-threaded -- this specifically also means we must call it before we
2629 start plug-ins / storage engines / external components!
2630
2631 @retval 0 no errors
2632 @retval -1 couldn't initialize stack lock
2633 @retval -2 couldn't initialize built-in default filter
2634 @retval -3 couldn't set up service hash
2635 @retval -4 couldn't initialize syseventlog lock
2636 @retval -5 couldn't set service pipeline
2637 @retval -6 couldn't initialize buffered logging lock
2638 */
log_builtins_init()2639 int log_builtins_init() {
2640 int rr = 0;
2641
2642 DBUG_ASSERT(!log_builtins_inited);
2643
2644 // Reset flag. This is *also* set on definition, this is intentional.
2645 log_buffering_flushworthy = false;
2646
2647 if (mysql_rwlock_init(0, &THR_LOCK_log_stack)) return -1;
2648
2649 if (mysql_mutex_init(0, &THR_LOCK_log_syseventlog, MY_MUTEX_INIT_FAST)) {
2650 mysql_rwlock_destroy(&THR_LOCK_log_stack);
2651 return -4;
2652 }
2653
2654 if (mysql_mutex_init(0, &THR_LOCK_log_buffered, MY_MUTEX_INIT_FAST)) {
2655 rr = -6;
2656 goto fail;
2657 }
2658
2659 mysql_rwlock_wrlock(&THR_LOCK_log_stack);
2660
2661 if (log_builtins_filter_init())
2662 rr = -2;
2663 else {
2664 log_service_cache =
2665 new collation_unordered_map<string, cache_entry_with_deleter>(
2666 system_charset_info, 0);
2667 if (log_service_cache == nullptr) rr = -3;
2668 }
2669
2670 log_service_instances = nullptr;
2671
2672 mysql_rwlock_unlock(&THR_LOCK_log_stack);
2673
2674 if (rr >= 0) {
2675 if (log_builtins_error_stack(LOG_BUILTINS_BUFFER, false, nullptr) >= 0) {
2676 log_error_stage_set(LOG_ERROR_STAGE_BUFFERING_EARLY);
2677 log_builtins_inited = true;
2678 return 0;
2679 } else {
2680 rr = -5; /* purecov: inspected */
2681 DBUG_ASSERT(false); /* purecov: inspected */
2682 }
2683 }
2684
2685 fail:
2686 mysql_rwlock_destroy(&THR_LOCK_log_stack); /* purecov: begin inspected */
2687 mysql_mutex_destroy(&THR_LOCK_log_syseventlog);
2688
2689 return rr; /* purecov: end */
2690 }
2691
2692 /*
2693 Service: helpers for logging. Mostly accessors for log events.
2694 See include/mysql/components/services/log_builtins.h for more information.
2695 */
2696
2697 /**
2698 See whether a type is wellknown.
2699
2700 @param t log item type to examine
2701
2702 @retval LOG_ITEM_TYPE_NOT_FOUND: key not found
2703 @retval >0: index in array of wellknowns
2704 */
2705 DEFINE_METHOD(int, log_builtins_imp::wellknown_by_type, (log_item_type t)) {
2706 return log_item_wellknown_by_type(t);
2707 }
2708
2709 /**
2710 See whether a string is a wellknown field name.
2711
2712 @param key potential key starts here
2713 @param length length of the string to examine
2714
2715 @retval LOG_ITEM_TYPE_RESERVED: reserved, but not "wellknown" key
2716 @retval LOG_ITEM_TYPE_NOT_FOUND: key not found
2717 @retval >0: index in array of wellknowns
2718 */
2719 DEFINE_METHOD(int, log_builtins_imp::wellknown_by_name,
2720 (const char *key, size_t length)) {
2721 return log_item_wellknown_by_name(key, length);
2722 }
2723
2724 /**
2725 Accessor: from a record describing a wellknown key, get its type
2726
2727 @param i index in array of wellknowns, see log_item_wellknown_by_...()
2728
2729 @retval the log item type for the wellknown key
2730 */
2731 DEFINE_METHOD(log_item_type, log_builtins_imp::wellknown_get_type, (uint i)) {
2732 return log_item_wellknown_get_type(i);
2733 }
2734
2735 /**
2736 Accessor: from a record describing a wellknown key, get its name
2737
2738 @param i index in array of wellknowns, see log_item_wellknown_by_...()
2739
2740 @retval name (NTBS)
2741 */
2742 DEFINE_METHOD(const char *, log_builtins_imp::wellknown_get_name, (uint i)) {
2743 return log_item_wellknown_get_name(i);
2744 }
2745
2746 /**
2747 Sanity check an item.
2748 Certain log sinks have very low requirements with regard to the data
2749 they receive; they write keys as strings, and then data according to
2750 the item's class (string, integer, or float), formatted to the sink's
2751 standards (e.g. JSON, XML, ...).
2752 Code that has higher requirements can use this check to see whether
2753 the given item is of a known type (whether generic or wellknown),
2754 whether the given type and class agree, and whether in case of a
2755 well-known type, the given key is correct for that type.
2756 If your code generates items that don't pass this check, you should
2757 probably go meditate on it.
2758
2759 @param li the log_item to check
2760
2761 @retval 0 no problems
2762 @retval -2 unknown item type
2763 @retval -3 item_class derived from type isn't what's set on the item
2764 @retval -4 class not generic, so key should match wellknown
2765 */
2766 DEFINE_METHOD(int, log_builtins_imp::item_inconsistent, (log_item * li)) {
2767 return log_item_inconsistent(li);
2768 }
2769
2770 /**
2771 Predicate used to determine whether a type is generic
2772 (generic string, generic float, generic integer) rather
2773 than a well-known type.
2774
2775 @param t log item type to examine
2776
2777 @retval true if generic type
2778 @retval false if wellknown type
2779 */
2780 DEFINE_METHOD(bool, log_builtins_imp::item_generic_type, (log_item_type t)) {
2781 return log_item_generic_type(t);
2782 }
2783
2784 /**
2785 Predicate used to determine whether a class is a string
2786 class (C-string or Lex-string).
2787
2788 @param c log item class to examine
2789
2790 @retval true if of a string class
2791 @retval false if not of a string class
2792 */
2793 DEFINE_METHOD(bool, log_builtins_imp::item_string_class, (log_item_class c)) {
2794 return log_item_string_class(c);
2795 }
2796
2797 /**
2798 Predicate used to determine whether a class is a numeric
2799 class (integer or float).
2800
2801 @param c log item class to examine
2802
2803 @retval true if of a numeric class
2804 @retval false if not of a numeric class
2805 */
2806 DEFINE_METHOD(bool, log_builtins_imp::item_numeric_class, (log_item_class c)) {
2807 return log_item_numeric_class(c);
2808 }
2809
2810 /**
2811 Set an integer value on a log_item.
2812 Fails gracefully if no log_item_data is supplied, so it can safely
2813 wrap log_line_item_set[_with_key]().
2814
2815 @param lid log_item_data struct to set the value on
2816 @param i integer to set
2817
2818 @retval true lid was nullptr (possibly: OOM, could not set up log_item)
2819 @retval false all's well
2820 */
2821 DEFINE_METHOD(bool, log_builtins_imp::item_set_int,
2822 (log_item_data * lid, longlong i)) {
2823 return log_item_set_int(lid, i);
2824 }
2825
2826 /**
2827 Set a floating point value on a log_item.
2828 Fails gracefully if no log_item_data is supplied, so it can safely
2829 wrap log_line_item_set[_with_key]().
2830
2831 @param lid log_item_data struct to set the value on
2832 @param f float to set
2833
2834 @retval true lid was nullptr (possibly: OOM, could not set up log_item)
2835 @retval false all's well
2836 */
2837 DEFINE_METHOD(bool, log_builtins_imp::item_set_float,
2838 (log_item_data * lid, double f)) {
2839 return log_item_set_float(lid, f);
2840 }
2841
2842 /**
2843 Set a string value on a log_item.
2844 Fails gracefully if no log_item_data is supplied, so it can safely
2845 wrap log_line_item_set[_with_key]().
2846
2847 @param lid log_item_data struct to set the value on
2848 @param s pointer to string
2849 @param s_len length of string
2850
2851 @retval true lid was nullptr (possibly: OOM, could not set up log_item)
2852 @retval false all's well
2853 */
2854 DEFINE_METHOD(bool, log_builtins_imp::item_set_lexstring,
2855 (log_item_data * lid, const char *s, size_t s_len)) {
2856 return log_item_set_lexstring(lid, s, s_len);
2857 }
2858
2859 /**
2860 Set a string value on a log_item.
2861 Fails gracefully if no log_item_data is supplied, so it can safely
2862 wrap log_line_item_set[_with_key]().
2863
2864 @param lid log_item_data struct to set the value on
2865 @param s pointer to NTBS
2866
2867 @retval true lid was nullptr (possibly: OOM, could not set up log_item)
2868 @retval false all's well
2869 */
2870 DEFINE_METHOD(bool, log_builtins_imp::item_set_cstring,
2871 (log_item_data * lid, const char *s)) {
2872 return log_item_set_cstring(lid, s);
2873 }
2874
2875 /**
2876 Create new log item with key name "key", and allocation flags of
2877 "alloc" (see enum_log_item_free).
2878 Will return a pointer to the item's log_item_data struct for
2879 convenience.
2880 This is mostly interesting for filters and other services that create
2881 items that are not part of a log_line; sources etc. that intend to
2882 create an item for a log_line (the more common case) should usually
2883 use the below line_item_set_with_key() which creates an item (like
2884 this function does), but also correctly inserts it into a log_line.
2885
2886 @param li the log_item to work on
2887 @param t the item-type
2888 @param key the key to set on the item.
2889 ignored for non-generic types (may pass nullptr for those)
2890 see alloc
2891 @param alloc LOG_ITEM_FREE_KEY if key was allocated by caller
2892 LOG_ITEM_FREE_NONE if key was not allocated
2893 Allocated keys will automatically free()d when the
2894 log_item is.
2895 The log_item's alloc flags will be set to the
2896 submitted value; specifically, any pre-existing
2897 value will be clobbered. It is therefore WRONG
2898 a) to use this on a log_item that already has a key;
2899 it should only be used on freshly init'd log_items;
2900 b) to use this on a log_item that already has a
2901 value (specifically, an allocated one); the correct
2902 order is to init a log_item, then set up type and
2903 key, and finally to set the value. If said value is
2904 an allocated string, the log_item's alloc should be
2905 bitwise or'd with LOG_ITEM_FREE_VALUE.
2906
2907 @retval a pointer to the log_item's log_data, for easy chaining:
2908 log_item_set_with_key(...)->data_integer= 1;
2909 */
2910 DEFINE_METHOD(log_item_data *, log_builtins_imp::item_set_with_key,
2911 (log_item * li, log_item_type t, const char *key, uint32 alloc)) {
2912 return log_item_set_with_key(li, t, key, alloc);
2913 }
2914
2915 /**
2916 As log_item_set_with_key(), except that the key is automatically
2917 derived from the wellknown log_item_type t.
2918
2919 Create new log item with type "t".
2920 Will return a pointer to the item's log_item_data struct for
2921 convenience.
2922 This is mostly interesting for filters and other services that create
2923 items that are not part of a log_line; sources etc. that intend to
2924 create an item for a log_line (the more common case) should usually
2925 use the below line_item_set_with_key() which creates an item (like
2926 this function does), but also correctly inserts it into a log_line.
2927
2928 The allocation of this item will be LOG_ITEM_FREE_NONE;
2929 specifically, any pre-existing value will be clobbered.
2930 It is therefore WRONG
2931 a) to use this on a log_item that already has a key;
2932 it should only be used on freshly init'd log_items;
2933 b) to use this on a log_item that already has a
2934 value (specifically, an allocated one); the correct
2935 order is to init a log_item, then set up type and
2936 key, and finally to set the value. If said value is
2937 an allocated string, the log_item's alloc should be
2938 bitwise or'd with LOG_ITEM_FREE_VALUE.
2939
2940 @param li the log_item to work on
2941 @param t the item-type
2942
2943 @retval a pointer to the log_item's log_data, for easy chaining:
2944 log_item_set_with_key(...)->data_integer= 1;
2945 */
2946 DEFINE_METHOD(log_item_data *, log_builtins_imp::item_set,
2947 (log_item * li, log_item_type t)) {
2948 return log_item_set(li, t);
2949 }
2950
2951 /**
2952 Create new log item in log line "ll", with key name "key", and
2953 allocation flags of "alloc" (see enum_log_item_free).
2954 On success, the number of registered items on the log line is increased,
2955 the item's type is added to the log_line's "seen" property,
2956 and a pointer to the item's log_item_data struct is returned for
2957 convenience.
2958
2959 @param ll the log_line to work on
2960 @param t the item-type
2961 @param key the key to set on the item.
2962 ignored for non-generic types (may pass nullptr for those)
2963 see alloc
2964 @param alloc LOG_ITEM_FREE_KEY if key was allocated by caller
2965 LOG_ITEM_FREE_NONE if key was not allocated
2966 Allocated keys will automatically free()d when the
2967 log_item is.
2968 The log_item's alloc flags will be set to the
2969 submitted value; specifically, any pre-existing
2970 value will be clobbered. It is therefore WRONG
2971 a) to use this on a log_item that already has a key;
2972 it should only be used on freshly init'd log_items;
2973 b) to use this on a log_item that already has a
2974 value (specifically, an allocated one); the correct
2975 order is to init a log_item, then set up type and
2976 key, and finally to set the value. If said value is
2977 an allocated string, the log_item's alloc should be
2978 bitwise or'd with LOG_ITEM_FREE_VALUE.
2979
2980
2981 @retval !nullptr a pointer to the log_item's log_data, for easy chaining:
2982 line_item_set_with_key(...)->data_integer= 1;
2983 @retval nullptr could not create a log_item in given log_line
2984 */
2985 DEFINE_METHOD(log_item_data *, log_builtins_imp::line_item_set_with_key,
2986 (log_line * ll, log_item_type t, const char *key, uint32 alloc)) {
2987 return log_line_item_set_with_key(ll, t, key, alloc);
2988 }
2989
2990 /**
2991 Create a new log item of well-known type "t" in log line "ll".
2992 On success, the number of registered items on the log line is increased,
2993 the item's type is added to the log_line's "seen" property,
2994 and a pointer to the item's log_item_data struct is returned for
2995 convenience.
2996
2997 The allocation of this item will be LOG_ITEM_FREE_NONE;
2998 specifically, any pre-existing value will be clobbered.
2999 It is therefore WRONG
3000 a) to use this on a log_item that already has a key;
3001 it should only be used on freshly init'd log_items;
3002 b) to use this on a log_item that already has a
3003 value (specifically, an allocated one); the correct
3004 order is to init a log_item, then set up type and
3005 key, and finally to set the value. If said value is
3006 an allocated string, the log_item's alloc should be
3007 bitwise or'd with LOG_ITEM_FREE_VALUE.
3008
3009 @param ll the log_line to work on
3010 @param t the item-type
3011
3012 @retval !nullptr a pointer to the log_item's log_data, for easy chaining:
3013 line_item_set(...)->data_integer= 1;
3014 @retval nullptr could not create a log_item in given log_line
3015 */
3016 DEFINE_METHOD(log_item_data *, log_builtins_imp::line_item_set,
3017 (log_line * ll, log_item_type t)) {
3018 return log_line_item_set_with_key(ll, t, nullptr, LOG_ITEM_FREE_NONE);
3019 }
3020
3021 /**
3022 Dynamically allocate and initialize a log_line.
3023
3024 @retval nullptr could not set up buffer (too small?)
3025 @retval other address of the newly initialized log_line
3026 */
3027 DEFINE_METHOD(log_line *, log_builtins_imp::line_init, ()) {
3028 return log_line_init();
3029 }
3030
3031 /**
3032 Release a log_line allocated with line_init()
3033
3034 @param ll a log_line previously allocated with line_init()
3035 */
3036 DEFINE_METHOD(void, log_builtins_imp::line_exit, (log_line * ll)) {
3037 log_line_exit(ll);
3038 }
3039
3040 /**
3041 How many items are currently set on the given log_line?
3042
3043 @param ll the log-line to examine
3044
3045 @retval the number of items set
3046 */
3047 DEFINE_METHOD(int, log_builtins_imp::line_item_count, (log_line * ll)) {
3048 return log_line_item_count(ll);
3049 }
3050
3051 /**
3052 Test whether a given type is presumed present on the log line.
3053
3054 @param ll the log_line to examine
3055 @param m the log_type to test for
3056
3057 @retval 0 not present
3058 @retval !=0 present
3059 */
3060 DEFINE_METHOD(log_item_type_mask, log_builtins_imp::line_item_types_seen,
3061 (log_line * ll, log_item_type_mask m)) {
3062 return log_line_item_types_seen(ll, m);
3063 }
3064
3065 /**
3066 Get an iterator for the items in a log_line.
3067 For now, only one iterator may exist per log_line.
3068
3069 @param ll the log_line to examine
3070
3071 @retval a log_iterm_iter, or nullptr on failure
3072 */
3073 DEFINE_METHOD(log_item_iter *, log_builtins_imp::line_item_iter_acquire,
3074 (log_line * ll)) {
3075 if (ll == nullptr) return nullptr;
3076
3077 // If the default iter has already been claimed, refuse to overwrite it.
3078 if (ll->iter.ll != nullptr) return nullptr;
3079
3080 ll->iter.ll = ll;
3081 ll->iter.index = -1;
3082
3083 return &ll->iter;
3084 }
3085
3086 /**
3087 Release an iterator for the items in a log_line.
3088
3089 @param it the iterator to release
3090 */
3091 DEFINE_METHOD(void, log_builtins_imp::line_item_iter_release,
3092 (log_item_iter * it)) {
3093 DBUG_ASSERT(it != nullptr);
3094 DBUG_ASSERT(it->ll != nullptr);
3095
3096 it->ll = nullptr;
3097 }
3098
3099 /**
3100 Use the log_line iterator to get the first item from the set.
3101
3102 @param it the iterator to use
3103
3104 @retval pointer to the first log_item in the collection, or nullptr
3105 */
3106 DEFINE_METHOD(log_item *, log_builtins_imp::line_item_iter_first,
3107 (log_item_iter * it)) {
3108 DBUG_ASSERT(it != nullptr);
3109 DBUG_ASSERT(it->ll != nullptr);
3110
3111 if (it->ll->count < 1) return nullptr;
3112
3113 it->index = 0;
3114 return &it->ll->item[it->index];
3115 }
3116
3117 /**
3118 Use the log_line iterator to get the next item from the set.
3119
3120 @param it the iterator to use
3121
3122 @retval pointer to the next log_item in the collection, or nullptr
3123 */
3124 // Call to function through pointer to incorrect function type
3125 DEFINE_METHOD(log_item *, log_builtins_imp::line_item_iter_next,
3126 (log_item_iter * it)) {
3127 DBUG_ASSERT(it != nullptr);
3128 DBUG_ASSERT(it->ll != nullptr);
3129 DBUG_ASSERT(it->index >= 0);
3130
3131 it->index++;
3132
3133 if (it->index >= it->ll->count) return nullptr;
3134
3135 return &it->ll->item[it->index];
3136 }
3137
3138 /**
3139 Use the log_line iterator to get the current item from the set.
3140
3141 @param it the iterator to use
3142
3143 @retval pointer to the current log_item in the collection, or nullptr
3144 */
3145 DEFINE_METHOD(log_item *, log_builtins_imp::line_item_iter_current,
3146 (log_item_iter * it)) {
3147 DBUG_ASSERT(it != nullptr);
3148 DBUG_ASSERT(it->ll != nullptr);
3149 DBUG_ASSERT(it->index >= 0);
3150
3151 if (it->index >= it->ll->count) return nullptr;
3152
3153 return &it->ll->item[it->index];
3154 }
3155
3156 /**
3157 Complete, filter, and write submitted log items.
3158
3159 This expects a log_line collection of log-related key/value pairs,
3160 e.g. from log_message().
3161
3162 Where missing, timestamp, priority, thread-ID (if any) and so forth
3163 are added.
3164
3165 Log item source services, log item filters, and log item sinks are
3166 then called; then all applicable resources are freed.
3167
3168 This interface is intended to facilitate the building of submission
3169 interfaces other than the variadic message() one below. See the
3170 example fluent C++ LogEvent() wrapper for an example of how to leverage
3171 it.
3172
3173 @param ll key/value pairs describing info to log
3174
3175 @retval int number of fields in created log line
3176 */
3177 DEFINE_METHOD(int, log_builtins_imp::line_submit, (log_line * ll)) {
3178 return log_line_submit(ll);
3179 }
3180
3181 /**
3182 Submit a log-message for log "log_type".
3183 Variadic convenience function for logging.
3184
3185 This fills in the array that is used by the filter and log-writer
3186 services. Where missing, timestamp, priority, and thread-ID (if any)
3187 are added. Log item source services, log item filters, and log item
3188 writers are called.
3189
3190 The variadic list accepts a list of "assignments" of the form
3191 - log_item_type, value, for well-known types, and
3192 - log_item_type, key, value, for ad-hoc types (LOG_ITEM_GEN_*)
3193
3194 As its last item, the list should have
3195 - an element of type LOG_ITEM_LOG_MESSAGE, containing a printf-style
3196 format string, followed by all variables necessary to satisfy the
3197 substitutions in that string
3198
3199 OR
3200
3201 - an element of type LOG_ITEM_LOG_LOOKUP, containing a MySQL error code,
3202 which will be looked up in the list or regular error messages, followed
3203 by all variables necessary to satisfy the substitutions in that string
3204
3205 OR
3206
3207 - an element of type LOG_ITEM_LOG_VERBATIM, containing a string that will
3208 be used directly, with no % substitutions
3209
3210 see log_vmessage() for more information.
3211
3212 @param log_type what log should this go to?
3213 @param ... fields: LOG_ITEM_* tag, [[key], value]
3214 @retval int return value of log_vmessage()
3215 */
3216 DEFINE_METHOD(int, log_builtins_imp::message, (int log_type, ...)) {
3217 va_list lili;
3218 int ret;
3219
3220 va_start(lili, log_type);
3221 ret = log_vmessage(log_type, lili);
3222 va_end(lili);
3223
3224 return ret;
3225 }
3226
3227 /*
3228 Escape \0 bytes, add \0 terminator. For log-sinks that terminate in
3229 an API using C-strings.
3230
3231 @param li list_item to process
3232
3233 @retval -1 out of memory
3234 @retval 0 success
3235 */
3236 DEFINE_METHOD(int, log_builtins_imp::sanitize, (log_item * li)) {
3237 size_t in_len = li->data.data_string.length, out_len, len;
3238 const char *in_start = li->data.data_string.str, *in_read;
3239 char *out_start = nullptr, *out_write;
3240 int nuls_found = 0;
3241
3242 DBUG_ASSERT((li != nullptr) && (li->item_class == LOG_LEX_STRING));
3243
3244 // find out how many \0 to escape
3245 for (in_read = in_start, len = in_len;
3246 ((in_read = (const char *)memchr(in_read, '\0', len)) != nullptr);) {
3247 nuls_found++;
3248 in_read++; // skip over \0
3249 len = in_len - (in_read - in_start);
3250 }
3251
3252 /*
3253 Current length + 3 extra for each NUL so we can escape it + terminating NUL
3254 */
3255 out_len = in_len + (nuls_found * 3) + 1;
3256
3257 if ((out_start = (char *)my_malloc(key_memory_log_error_loaded_services,
3258 out_len, MYF(0))) == nullptr)
3259 return -1;
3260
3261 /*
3262 copy over
3263 */
3264 in_read = in_start;
3265 out_write = out_start;
3266
3267 while (nuls_found--) {
3268 // copy part before NUL
3269 len = strlen(in_read);
3270 strcpy(out_write, in_read);
3271 out_write += len;
3272
3273 // add escaped NUL
3274 strcpy(out_write, "\\000");
3275 out_write += 4;
3276 in_read += (len + 1);
3277 }
3278
3279 // calculate tail (with no further NUL bytes) length
3280 len = (in_read > in_start) ? (in_read - in_start) : in_len;
3281
3282 // copy tail
3283 strncpy(out_write, in_read, len);
3284
3285 /*
3286 NUL terminate. (the formula above always gives a minimum out-size of 1.)
3287 */
3288 out_start[out_len - 1] = '\0';
3289
3290 if (li->alloc & LOG_ITEM_FREE_VALUE) {
3291 my_free(const_cast<char *>(in_start));
3292 }
3293
3294 li->data.data_string.str = out_start;
3295 li->alloc |= LOG_ITEM_FREE_VALUE;
3296
3297 return 0;
3298 }
3299
3300 /**
3301 Return MySQL error message for a given error code.
3302
3303 @param mysql_errcode the error code the message for which to look up
3304
3305 @retval the message (a printf-style format string)
3306 */
3307 DEFINE_METHOD(const char *, log_builtins_imp::errmsg_by_errcode,
3308 (int mysql_errcode)) {
3309 return error_message_for_error_log(mysql_errcode);
3310 }
3311
3312 /**
3313 Return MySQL error code for a given error symbol.
3314
3315 @param sym the symbol to look up
3316
3317 @retval -1 failure
3318 @retval >=0 the MySQL error code
3319 */
3320 DEFINE_METHOD(longlong, log_builtins_imp::errcode_by_errsymbol,
3321 (const char *sym)) {
3322 return mysql_symbol_to_errno(sym);
3323 }
3324
3325 /**
3326 Convenience function: Derive a log label ("error", "warning",
3327 "information") from a severity.
3328
3329 @param prio the severity/prio in question
3330
3331 @return a label corresponding to that priority.
3332 @retval "Error" for prio of ERROR_LEVEL or higher
3333 @retval "Warning" for prio of WARNING_LEVEL
3334 @retval "Note" otherwise
3335 */
3336 DEFINE_METHOD(const char *, log_builtins_imp::label_from_prio, (int prio)) {
3337 return log_label_from_prio(prio);
3338 }
3339
3340 /**
3341 open an error log file
3342
3343 @param file if beginning with '.':
3344 @@global.log_error, except with this extension
3345 otherwise:
3346 use this as file name in the same location as
3347 @@global.log_error
3348
3349 Value may not contain folder separators!
3350
3351 @param[out] my_errstream an error log handle, or nullptr on failure
3352
3353 @retval 0 success
3354 @retval -1 EINVAL: my_errstream is NULL
3355 @retval -2 EINVAL: invalid file name / extension
3356 @retval -3 OOM: could not allocate file handle
3357 @retval -4 couldn't lock lock
3358 @retval -5 couldn't write to given location
3359 */
3360 DEFINE_METHOD(int, log_builtins_imp::open_errstream,
3361 (const char *file, void **my_errstream)) {
3362 log_errstream *les;
3363 int rr;
3364
3365 if (my_errstream == nullptr) return -1;
3366
3367 *my_errstream = nullptr;
3368
3369 les = (log_errstream *)my_malloc(key_memory_log_error_loaded_services,
3370 sizeof(log_errstream), MYF(0));
3371
3372 if (les == nullptr) return -3;
3373
3374 new (les) log_errstream();
3375
3376 if (mysql_mutex_init(0, &les->LOCK_errstream, MY_MUTEX_INIT_FAST)) {
3377 my_free(les);
3378 return -4;
3379 }
3380
3381 if ((file == nullptr) || (file[0] == '\0') ||
3382 (strchr(file, FN_LIBCHAR) != nullptr)) {
3383 rr = -2;
3384 goto fail_with_free;
3385 } else if ((log_error_dest == nullptr) ||
3386 (!strcmp(log_error_dest, "stderr"))) {
3387 // using default stream, no file struct needed
3388 les->file = nullptr;
3389 } else {
3390 char errorlog_filename_buff[FN_REFLEN];
3391 char path[FN_REFLEN];
3392 size_t path_length;
3393 MY_STAT f_stat;
3394
3395 dirname_part(path, log_error_dest, &path_length);
3396
3397 rr = -5;
3398
3399 if (file[0] == '.') {
3400 fn_format(errorlog_filename_buff, log_error_dest, path, file,
3401 MY_APPEND_EXT | MY_REPLACE_DIR);
3402
3403 } else {
3404 fn_format(errorlog_filename_buff, file, path, ".les", MY_REPLACE_DIR);
3405 }
3406
3407 if (my_stat(errorlog_filename_buff, &f_stat, MYF(0)) != nullptr) {
3408 if (!(f_stat.st_mode & MY_S_IWRITE)) goto fail_with_free;
3409 } else {
3410 if (path_length && my_access(path, (F_OK | W_OK))) goto fail_with_free;
3411 }
3412
3413 les->file = my_fopen(errorlog_filename_buff,
3414 O_APPEND | O_WRONLY | MY_FOPEN_BINARY, MYF(0));
3415
3416 if (les->file == nullptr) goto fail_with_free;
3417 }
3418
3419 *my_errstream = les;
3420
3421 return 0;
3422
3423 fail_with_free:
3424 my_free(les);
3425
3426 return rr;
3427 }
3428
3429 /**
3430 write to an error log file previously opened with open_errstream()
3431
3432 @param my_errstream a handle describing the log file
3433 @param buffer pointer to the string to write
3434 @param length length of the string to write
3435
3436 @retval 0 success
3437 @retval !0 failure
3438 */
3439 DEFINE_METHOD(int, log_builtins_imp::write_errstream,
3440 (void *my_errstream, const char *buffer, size_t length)) {
3441 log_errstream *les = (log_errstream *)my_errstream;
3442
3443 if ((les == nullptr) || (les->file == nullptr))
3444 log_write_errstream(buffer, length);
3445 else {
3446 mysql_mutex_lock(&les->LOCK_errstream);
3447 fprintf(les->file, "%.*s\n", (int)length, buffer);
3448 fflush(les->file);
3449 mysql_mutex_unlock(&les->LOCK_errstream);
3450 }
3451
3452 return 0;
3453 }
3454
3455 /**
3456 are we writing to a dedicated errstream, or are we sharing it?
3457
3458 @param my_errstream a handle describing the log file
3459
3460 @retval <0 error
3461 @retval 0 not dedicated (multiplexed, stderr, ...)
3462 @retval 1 dedicated
3463 */
3464 DEFINE_METHOD(int, log_builtins_imp::dedicated_errstream,
3465 (void *my_errstream)) {
3466 log_errstream *les = (log_errstream *)my_errstream;
3467
3468 if (les == nullptr) return -1;
3469
3470 return (les->file != nullptr) ? 1 : 0;
3471 }
3472
3473 /**
3474 close an error log file previously opened with open_errstream()
3475
3476 @param my_errstream a handle describing the log file
3477
3478 @retval 0 success
3479 @retval !0 failure
3480 */
3481 DEFINE_METHOD(int, log_builtins_imp::close_errstream, (void **my_errstream)) {
3482 int rr;
3483
3484 if (my_errstream == nullptr) return -1;
3485
3486 log_errstream *les = (log_errstream *)(*my_errstream);
3487
3488 if (les == nullptr) return -2;
3489
3490 *my_errstream = nullptr;
3491
3492 if (les->file != nullptr) {
3493 my_fclose(les->file, MYF(0));
3494 // Continue to log after closing, you'll log to stderr. That'll learn ya.
3495 les->file = nullptr;
3496 }
3497
3498 rr = mysql_mutex_destroy(&les->LOCK_errstream);
3499
3500 my_free(les);
3501
3502 return rr;
3503 }
3504
3505 /*
3506 Service: some stand-ins for string functions we need until they are
3507 implemented in a more comprehensive service.
3508 3rd party services should not rely on these being here forever.
3509 */
3510
3511 /**
3512 Wrapper for my_malloc()
3513
3514 Alloc (len+1) bytes.
3515
3516 @param len length of string to copy
3517 */
3518 DEFINE_METHOD(void *, log_builtins_string_imp::malloc, (size_t len)) {
3519 return my_malloc(key_memory_log_error_loaded_services, len, MYF(0));
3520 }
3521
3522 /**
3523 Wrapper for my_strndup()
3524
3525 Alloc (len+1) bytes, then copy len bytes from fm, and \0 terminate.
3526 Like my_strndup(), and unlike strndup(), \0 in input won't end copying.
3527
3528 @param fm string to copy
3529 @param len length of string to copy
3530 */
3531 DEFINE_METHOD(char *, log_builtins_string_imp::strndup,
3532 (const char *fm, size_t len)) {
3533 return my_strndup(key_memory_log_error_loaded_services, fm, len, MYF(0));
3534 }
3535
3536 /**
3537 Wrapper for my_free() - free allocated memory
3538 */
3539 DEFINE_METHOD(void, log_builtins_string_imp::free, (void *ptr)) {
3540 return my_free(ptr);
3541 }
3542
3543 /**
3544 Wrapper for strlen() - length of a nul-terminated byte string
3545 */
3546 DEFINE_METHOD(size_t, log_builtins_string_imp::length, (const char *s)) {
3547 return strlen(s);
3548 }
3549
3550 /**
3551 Wrapper for strchr() - find character in string, from the left
3552 */
3553 DEFINE_METHOD(char *, log_builtins_string_imp::find_first,
3554 (const char *s, int c)) {
3555 return strchr(const_cast<char *>(s), c);
3556 }
3557
3558 /**
3559 Wrapper for strrchr() - find character in string, from the right
3560 */
3561 DEFINE_METHOD(char *, log_builtins_string_imp::find_last,
3562 (const char *s, int c)) {
3563 return strrchr(const_cast<char *>(s), c);
3564 }
3565
3566 /**
3567 Compare two NUL-terminated byte strings
3568
3569 Note that when comparing without length limit, the long string
3570 is greater if they're equal up to the length of the shorter
3571 string, but the shorter string will be considered greater if
3572 its "value" up to that point is greater:
3573
3574 compare 'abc','abcd': -100 (longer wins if otherwise same)
3575 compare 'abca','abcd': -3 (higher value wins)
3576 compare 'abcaaaaa','abcd': -3 (higher value wins)
3577
3578 @param a the first string
3579 @param b the second string
3580 @param len compare at most this many characters --
3581 0 for no limit
3582 @param case_insensitive ignore upper/lower case in comparison
3583
3584 @retval -1 a < b
3585 @retval 0 a == b
3586 @retval 1 a > b
3587 */
3588 DEFINE_METHOD(int, log_builtins_string_imp::compare,
3589 (const char *a, const char *b, size_t len,
3590 bool case_insensitive)) {
3591 return log_string_compare(a, b, len, case_insensitive);
3592 }
3593
3594 /**
3595 Wrapper for vsnprintf()
3596 Replace all % in format string with variables from list
3597
3598 @param to buffer to write the result to
3599 @param n size of that buffer
3600 @param fmt format string
3601 @param ap va_list with valuables for all substitutions in format string
3602
3603 @retval return value of vsnprintf
3604 */
3605 DEFINE_METHOD(size_t, log_builtins_string_imp::substitutev,
3606 (char *to, size_t n, const char *fmt, va_list ap)) {
3607 return vsnprintf(to, n, fmt, ap);
3608 }
3609
3610 /**
3611 Wrapper for vsnprintf()
3612 Replace all % in format string with variables from list
3613 */
3614 DEFINE_METHOD(size_t, log_builtins_string_imp::substitute,
3615 (char *to, size_t n, const char *fmt, ...)) {
3616 size_t ret;
3617 va_list ap;
3618
3619 va_start(ap, fmt);
3620 ret = vsnprintf(to, n, fmt, ap);
3621 va_end(ap);
3622 return ret;
3623 }
3624
3625 /*
3626 Service: some stand-ins we need until certain other WLs are implemented.
3627 3rd party services should not rely on these being here for long.
3628 */
3629
3630 DEFINE_METHOD(size_t, log_builtins_tmp_imp::notify_client,
3631 (void *thd, uint severity, uint code, char *to, size_t n,
3632 const char *format, ...)) {
3633 size_t ret = 0;
3634
3635 if ((to != nullptr) && (n > 0)) {
3636 va_list ap;
3637
3638 va_start(ap, format);
3639 ret = vsnprintf(to, n, format, ap);
3640 va_end(ap);
3641
3642 push_warning((THD *)thd, (Sql_condition::enum_severity_level)severity, code,
3643 to);
3644 }
3645
3646 return ret;
3647 }
3648
3649 /*
3650 Service: expose syslog/eventlog to other components.
3651 3rd party services should not rely on these being here for long,
3652 as this may be merged into a possibly mysys API later.
3653 */
3654
3655 /**
3656 Wrapper for mysys' my_openlog.
3657 Opens/Registers a new handle for system logging.
3658 Note: It's a thread-unsafe function. It should either
3659 be invoked from the main thread or some extra thread
3660 safety measures need to be taken.
3661
3662 @param name Name of the event source / syslog ident.
3663 @param option MY_SYSLOG_PIDS to log PID with each message.
3664 @param facility Type of program. Passed to openlog().
3665
3666 @retval 0 Success
3667 @retval -1 Error, log not opened
3668 @retval -2 Error, not updated, using previous values
3669 */
3670 DEFINE_METHOD(int, log_builtins_syseventlog_imp::open,
3671 (const char *name, int option, int facility)) {
3672 int ret;
3673
3674 mysql_mutex_lock(&THR_LOCK_log_syseventlog);
3675 ret = my_openlog(name, option, facility);
3676 mysql_mutex_unlock(&THR_LOCK_log_syseventlog);
3677
3678 return ret;
3679 }
3680
3681 /**
3682 Wrapper for mysys' my_syslog.
3683 Sends message to the system logger. On Windows, the specified message is
3684 internally converted to UCS-2 encoding, while on other platforms, no
3685 conversion takes place and the string is passed to the syslog API as it is.
3686
3687 @param level Log level
3688 @param msg Message to be logged
3689
3690 @retval 0 Success
3691 @retval <>0 Error
3692 */
3693 DEFINE_METHOD(int, log_builtins_syseventlog_imp::write,
3694 (enum loglevel level, const char *msg)) {
3695 int ret;
3696
3697 mysql_mutex_lock(&THR_LOCK_log_syseventlog);
3698 ret = my_syslog(&my_charset_utf8_bin, level, msg);
3699 mysql_mutex_unlock(&THR_LOCK_log_syseventlog);
3700
3701 return ret;
3702 }
3703
3704 /**
3705 Wrapper for mysys' my_closelog.
3706 Closes/de-registers the system logging handle.
3707
3708 @retval 0 Success
3709 @retval <>0 Error
3710 */
3711 DEFINE_METHOD(int, log_builtins_syseventlog_imp::close, (void)) {
3712 int ret = 0;
3713
3714 mysql_mutex_lock(&THR_LOCK_log_syseventlog);
3715 ret = my_closelog();
3716 mysql_mutex_unlock(&THR_LOCK_log_syseventlog);
3717
3718 return ret;
3719 }
3720