include\mysql\components\services\log_builtins.h:
#define LogErr(severity, ecode, ...) \
LogEvent() \
.prio(severity) \
.errcode(ecode) \
.subsys(LOG_SUBSYSTEM_TAG) \
.source_line(__LINE__) \
.source_file(MY_BASENAME) \
.function(__FUNCTION__) \
.lookup(ecode, ##__VA_ARGS__)
#endif
类定义:
class LogEvent {
private:
log_line *ll;
char *msg;
const char *msg_tag;
/**
Set MySQL error-code if none has been set yet.
@param errcode the error code (not operating system errno!)
@retval true an error occurred, value not set (OOM?)
@retval false value was set without incident, or did not need to be set
*/
bool set_errcode(longlong errcode) {
if (ll == nullptr) return true;
if (!log_line_item_types_seen(ll, LOG_ITEM_SQL_ERRCODE) &&
!log_line_item_types_seen(ll, LOG_ITEM_SQL_ERRSYMBOL)) {
return log_set_int(log_line_item_set(ll, LOG_ITEM_SQL_ERRCODE), errcode);
}
return false; // already set, that's OK then
}
/**
Set the error message.
@param fmt format string. % substitution will be performed.
@param ap va_list of the arguments for % substitution.
*/
void set_message(const char *fmt, va_list ap)
MY_ATTRIBUTE((format(printf, 2, 0)));
/**
Set the error message (by MySQL error code).
The actual message will be looked up using this errcode.
As the message is a printf-style format string, % substitution
will be performed.
@param errcode MySQL error code to fetch the message string for
@param ap va_list of the arguments for % substitution.
*/
void set_message_by_errcode(longlong errcode, va_list ap);
public:
/**
Destructor automatically sends the event on.
It is auto-free()d after processing.
*/
~LogEvent() {
if (ll != nullptr) {
log_line_submit(this->ll);
log_line_exit(ll);
log_free(msg);
}
}
/**
"Full customization" constructor. Use one of the LogErr() macro
where possible; it's there to help you remember the minimum set
of particles and their data-types. Be prepared for stern looks
from your reviewers if you use this constructor except for external
(loadable) services that have no error messages registered with the
server, and therefore need to submit them free-form.
*/
LogEvent() {
if ((ll = log_line_init()) != nullptr) {
if ((msg = (char *)log_malloc(LOG_BUFF_MAX)) == nullptr) {
log_line_exit(ll);
ll = nullptr;
}
} else
msg = nullptr;
msg_tag = nullptr;
}
/**
Set log type.
@param val the log type (LOG_TYPE_ERROR)
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &type(enum_log_type val) {
log_set_int(log_line_item_set(this->ll, LOG_ITEM_LOG_TYPE), val);
return *this;
}
/**
Append a numeric error code
@param val the MySQL error code (not operating system errno!).
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &errcode(longlong val) {
log_set_int(log_line_item_set(this->ll, LOG_ITEM_SQL_ERRCODE), val);
return *this;
}
/**
Append a (string) error symbol
@param val error symbol. NTBS.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &errsymbol(const char *val) {
log_set_cstring(log_line_item_set(this->ll, LOG_ITEM_SQL_ERRSYMBOL), val);
return *this;
}
/**
Append a (string) SQL state
@param val the SQLstate. NTBS.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &sqlstate(const char *val) {
log_set_cstring(log_line_item_set(this->ll, LOG_ITEM_SQL_STATE), val);
return *this;
}
/**
Append a numeric (operating system, as opposed to MySQL) error number.
@param val the operating system errno.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &os_errno(longlong val) {
log_set_int(log_line_item_set(this->ll, LOG_ITEM_SYS_ERRNO), val);
return *this;
}
/**
Append a textual (operating system, as opposed to MySQL) error message,
vulgo, strerror()
@param val the error message returned by the operating system. NTBS.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &os_errmsg(const char *val) {
log_set_cstring(log_line_item_set(this->ll, LOG_ITEM_SYS_STRERROR), val);
return *this;
}
/**
Which source file was the problem detected in?
@param val the source file's name. NTBS.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &source_file(const char *val) {
log_set_cstring(log_line_item_set(this->ll, LOG_ITEM_SRC_FILE), val);
return *this;
}
/**
Which line in the source file was the problem detected on?
@param val the line number.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &source_line(longlong val) {
log_set_int(log_line_item_set(this->ll, LOG_ITEM_SRC_LINE), val);
return *this;
}
/**
Which function in the source was the problem detected in?
@param val the function's name. NTBS.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &function(const char *val) {
log_set_cstring(log_line_item_set(this->ll, LOG_ITEM_SRC_FUNC), val);
return *this;
}
/**
Which subsystem in the source was the problem detected in?
("Repl"/"InnoDB"/"Server")
@param val the subsystem. NTBS.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &subsys(const char *val) {
if (val != nullptr)
log_set_cstring(log_line_item_set(this->ll, LOG_ITEM_SRV_SUBSYS), val);
return *this;
}
/**
Which component in the source was the problem detected in?
This should be the same string that is given to the
component/service framework.
@param val the component. NTBS.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &component(const char *val) {
if (val != nullptr)
log_set_cstring(log_line_item_set(this->ll, LOG_ITEM_SRV_COMPONENT), val);
return *this;
}
/**
What user were we working for at the time of the issue?
@param val the user part (of "user@host"). LEX_CSTRING.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &user(LEX_CSTRING val) {
log_set_lexstring(log_line_item_set(this->ll, LOG_ITEM_MSC_USER), val.str,
val.length);
return *this;
}
/**
What user were we working for at the time of the issue?
@param val the user part (of "user@host"). NTBS.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &user(const char *val) {
log_set_cstring(log_line_item_set(this->ll, LOG_ITEM_MSC_USER), val);
return *this;
}
/**
Whose session did the issue appear in?
@param val the host part (of "user@host"). LEX_CSTRING.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &host(LEX_CSTRING val) {
log_set_lexstring(log_line_item_set(this->ll, LOG_ITEM_MSC_HOST), val.str,
val.length);
return *this;
}
/**
Whose session did the issue appear in?
@param val the host part (of "user@host"). NTBS.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &host(const char *val) {
log_set_cstring(log_line_item_set(this->ll, LOG_ITEM_MSC_HOST), val);
return *this;
}
/**
What thread / "connection ID" was the issue detected in?
@param val the thread_ID of the session the issue appeared in
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &thread_id(longlong val) {
log_set_int(log_line_item_set(this->ll, LOG_ITEM_SRV_THREAD), val);
return *this;
}
/**
What query apparently caused the issue?
@param val the query_ID of the offending query
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &query_id(longlong val) {
log_set_int(log_line_item_set(this->ll, LOG_ITEM_SQL_QUERY_ID), val);
return *this;
}
/**
What table were we working on?
@param val the table's name/alias. NTBS.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &table_name(const char *val) {
log_set_cstring(log_line_item_set(this->ll, LOG_ITEM_SQL_TABLE_NAME), val);
return *this;
}
/**
Set error message priority.
Assign one of ERROR_LEVEL, WARNING_LEVEL, INFORMATION_LEVEL.
log-writers and other sinks should use this value (rather
than that of LOG_ITEM_LOG_EPRIO):
- file writers should use the value to determine
what label to write (perhaps by submitting it to label_from_prio())
- sinks that submit the event data to a sub-system outside of
the MySQL server (such as syslog, EventLog, systemd journal, etc.)
should translate this value into a priority/log level understood
by that target subsystem.
@param val The priority for this LogEvent.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &prio(longlong val) {
log_set_int(log_line_item_set(this->ll, LOG_ITEM_LOG_PRIO), val);
return *this;
}
/**
Set a label (usually "warning"/"error"/"information").
Will be derived from prio if not set explicitly.
Some log services may ignore custom labels.
@param val the (custom) label to set
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &label(const char *val) {
log_set_cstring(log_line_item_set(this->ll, LOG_ITEM_LOG_LABEL), val);
return *this;
}
/**
Add a message to the event, verbatim (i.e. with no % substitutions).
This is an analog of message("%s", message); it can be used when
message may contain user input or a message from another subsystem
that could contain % that must not be interpreted as an invitation
to do % substitutions.
If you use this in a context other than an external service that
has no messages registered with the server, your reviewers will
say unkind things about you. Use registered messages and their
error codes wherever possible!
@param msg_arg the message. % substitution will not happen.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &verbatim(const char *msg_arg) {
log_set_cstring(log_line_item_set(this->ll, LOG_ITEM_LOG_MESSAGE), msg_arg);
return *this;
}
/**
Fill in a format string by substituting the % with the given
arguments, then add the result as the event's message.
This should be used very sparingly; use registered messages
and their error codes wherever possible!
@param fmt message (treated as a printf-style format-string,
so % substitution will happen)
@param ap valist to satisfy any % in the message
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &messagev(const char *fmt, va_list ap)
MY_ATTRIBUTE((format(printf, 2, 0))) {
set_message(fmt, ap);
return *this;
}
/**
Fill in a format string by substituting the % with the given
arguments, then add the result as the event's message.
If you use this in a context other than an external service that
has no messages registered with the server, your reviewers will
say unkind things about you. Use registered messages and their
error codes wherever possible!
@param fmt message (treated as a printf-style format-string,
so % substitution will happen)
@param ... varargs to satisfy any % in the message
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &message(const char *fmt, ...) MY_ATTRIBUTE((format(printf, 2, 3)));
/**
Fill in a format string by substituting the % with the given
arguments and tag, then add the result as the event's message.
@param tag Tag to prefix to message.
@param fmt message (treated as a printf-style format-string,
so % substitution will happen)
@param ... varargs to satisfy any % in the message
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &message_quoted(const char *tag, const char *fmt, ...)
MY_ATTRIBUTE((format(printf, 3, 4))) {
msg_tag = tag;
va_list args;
va_start(args, fmt);
set_message(fmt, args);
va_end(args);
return *this;
}
/**
Find an error message by its MySQL error code.
Substitute the % in that message with the given
arguments, then add the result as the event's message.
@param errcode MySQL error code for the message in question,
e.g. ER_STARTUP
@param ... varargs to satisfy any % in the message
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &lookup(longlong errcode, ...) {
va_list args;
va_start(args, errcode);
set_message_by_errcode(errcode, args);
va_end(args);
return *this;
}
/**
Find an error message by its MySQL error code. Substitute the % in that
message with the given arguments list, then add the result as the event's
message.
@param errcode MySQL error code for the message in question,
e.g. ER_STARTUP
@param args varargs to satisfy any % in the message
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &lookupv(longlong errcode, va_list args) {
set_message_by_errcode(errcode, args);
return *this;
}
LogEvent &lookup_quoted(longlong errcode, const char *tag, ...) {
msg_tag = tag;
va_list args;
va_start(args, tag);
set_message_by_errcode(errcode, args);
va_end(args);
return *this;
}
LogEvent &lookup_quotedv(longlong errcode, const char *tag, va_list vl) {
msg_tag = tag;
set_message_by_errcode(errcode, vl);
return *this;
}
/**
Add a ad hoc integer value with the given key.
@param key user-defined key (i.e. not wellknown). NTBS.
@param val value.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &int_value(const char *key, longlong val) {
log_set_int(log_line_item_set_with_key(this->ll, LOG_ITEM_GEN_INTEGER, key,
LOG_ITEM_FREE_NONE),
val);
return *this;
}
/**
Add a ad hoc (not "well-known") float value with the given key.
@param key user-defined key (i.e. not wellknown). NTBS.
@param val value.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &float_value(const char *key, double val) {
log_set_float(log_line_item_set_with_key(this->ll, LOG_ITEM_GEN_FLOAT, key,
LOG_ITEM_FREE_NONE),
val);
return *this;
}
/**
Add a ad hoc string value with the given key.
@param key user-defined key (i.e. not wellknown). NTBS.
@param val value.
@param len length in bytes of the value.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &string_value(const char *key, const char *val, size_t len) {
log_set_lexstring(
log_line_item_set_with_key(this->ll, LOG_ITEM_GEN_LEX_STRING, key,
LOG_ITEM_FREE_NONE),
val, len);
return *this;
}
/**
Add a ad hoc string value with the given key.
@param key user-defined key (i.e. not wellknown). NTBS.
@param val value. NTBS.
@retval the LogEvent, for easy fluent-style chaining.
*/
LogEvent &string_value(const char *key, const char *val) {
log_set_cstring(
log_line_item_set_with_key(this->ll, LOG_ITEM_GEN_LEX_STRING, key,
LOG_ITEM_FREE_NONE),
val);
return *this;
}
};
用宏对LogEvent类中各个函数进行封装,其中log_bi变量为SERVICE_TYPE(log_builtins)类型,其定义为:
BEGIN_SERVICE_DEFINITION(log_builtins)
/**
See whether a type is wellknown.
@param t log item type to examine
@retval LOG_ITEM_TYPE_NOT_FOUND: key not found
@retval >0: index in array of wellknowns
*/
DECLARE_METHOD(int, wellknown_by_type, (log_item_type t));
/**
See whether a string is a wellknown field name.
@param key potential key starts here
@param length length of the string to examine
@retval LOG_ITEM_TYPE_RESERVED: reserved, but not "wellknown" key
@retval LOG_ITEM_TYPE_NOT_FOUND: key not found
@retval >0: index in array of wellknowns
*/
DECLARE_METHOD(int, wellknown_by_name, (const char *key, size_t length));
/**
Accessor: from a record describing a wellknown key, get its type
@param idx index in array of wellknowns, see log_item_wellknown_by_...()
@retval the log item type for the wellknown key
*/
DECLARE_METHOD(log_item_type, wellknown_get_type, (uint idx));
/**
Accessor: from a record describing a wellknown key, get its name
@param idx index in array of wellknowns, see log_item_wellknown_by_...()
@retval name (NTBS)
*/
DECLARE_METHOD(const char *, wellknown_get_name, (uint idx));
/**
Sanity check an item.
Certain log sinks have very low requirements with regard to the data
they receive; they write keys as strings, and then data according to
the item's class (string, integer, or float), formatted to the sink's
standards (e.g. JSON, XML, ...).
Code that has higher requirements can use this check to see whether
the given item is of a known type (whether generic or wellknown),
whether the given type and class agree, and whether in case of a
well-known type, the given key is correct for that type.
If your code generates items that don't pass this check, you should
probably go meditate on it.
@param li the log_item to check
@retval LOG_ITEM_OK no problems
@retval LOG_ITEM_TYPE_NOT_FOUND unknown item type
@retval LOG_ITEM_CLASS_MISMATCH item_class derived from type isn't
what's set on the item
@retval LOG_ITEM_KEY_MISMATCH class not generic, so key should
match wellknown
@retval LOG_ITEM_STRING_NULL class is string, pointer is nullptr
@retval LOG_ITEM_KEY_NULL no key set (this is legal e.g. on aux
items of filter rules, but should not
occur in a log_line, i.e., log_sinks are
within their rights to discard such items)
*/
DECLARE_METHOD(int, item_inconsistent, (log_item * li));
// helpers: predicates to find out about types and classes
/**
Predicate used to determine whether a type is generic
(generic string, generic float, generic integer) rather
than a well-known type.
@param t log item type to examine
@retval true if generic type
@retval false if wellknown type
*/
DECLARE_METHOD(bool, item_generic_type, (log_item_type t));
/**
Predicate used to determine whether a class is a string
class (C-string or Lex-string).
@param c log item class to examine
@retval true if of a string class
@retval false if not of a string class
*/
DECLARE_METHOD(bool, item_string_class, (log_item_class c));
/**
Predicate used to determine whether a class is a numeric
class (integer or float).
@param c log item class to examine
@retval true if of a numeric class
@retval false if not of a numeric class
*/
DECLARE_METHOD(bool, item_numeric_class, (log_item_class c));
/**
Set an integer value on a log_item.
Fails gracefully if no log_item_data is supplied, so it can safely
wrap log_line_item_set[_with_key]().
@param lid log_item_data struct to set the value on
@param i integer to set
@retval true lid was nullptr (possibly: OOM, could not set up log_item)
@retval false all's well
*/
DECLARE_METHOD(bool, item_set_int, (log_item_data * lid, longlong i));
/**
Set a floating point value on a log_item.
Fails gracefully if no log_item_data is supplied, so it can safely
wrap log_line_item_set[_with_key]().
@param lid log_item_data struct to set the value on
@param f float to set
@retval true lid was nullptr (possibly: OOM, could not set up log_item)
@retval false all's well
*/
DECLARE_METHOD(bool, item_set_float, (log_item_data * lid, double f));
/**
Set a string value on a log_item.
Fails gracefully if no log_item_data is supplied, so it can safely
wrap log_line_item_set[_with_key]().
@param lid log_item_data struct to set the value on
@param s pointer to string
@param s_len length of string
@retval true lid was nullptr (possibly: OOM, could not set up log_item)
@retval false all's well
*/
DECLARE_METHOD(bool, item_set_lexstring,
(log_item_data * lid, const char *s, size_t s_len));
/**
Set a string value on a log_item.
Fails gracefully if no log_item_data is supplied, so it can safely
wrap log_line_item_set[_with_key]().
@param lid log_item_data struct to set the value on
@param s pointer to NTBS
@retval true lid was nullptr (possibly: OOM, could not set up log_item)
@retval false all's well
*/
DECLARE_METHOD(bool, item_set_cstring, (log_item_data * lid, const char *s));
/**
Create new log item with key name "key", and allocation flags of
"alloc" (see enum_log_item_free).
Will return a pointer to the item's log_item_data struct for
convenience.
This is mostly interesting for filters and other services that create
items that are not part of a log_line; sources etc. that intend to
create an item for a log_line (the more common case) should usually
use the below line_item_set_with_key() which creates an item (like
this function does), but also correctly inserts it into a log_line.
@param li the log_item to work on
@param t the item-type
@param key the key to set on the item.
ignored for non-generic types (may pass nullptr for those)
see alloc
@param alloc LOG_ITEM_FREE_KEY if key was allocated by caller
LOG_ITEM_FREE_NONE if key was not allocated
Allocated keys will automatically free()d when the
log_item is.
The log_item's alloc flags will be set to the
submitted value; specifically, any pre-existing
value will be clobbered. It is therefore WRONG
a) to use this on a log_item that already has a key;
it should only be used on freshly init'd log_items;
b) to use this on a log_item that already has a
value (specifically, an allocated one); the correct
order is to init a log_item, then set up type and
key, and finally to set the value. If said value is
an allocated string, the log_item's alloc should be
bitwise or'd with LOG_ITEM_FREE_VALUE.
@retval a pointer to the log_item's log_data, for easy chaining:
log_item_set_with_key(...)->data_integer= 1;
*/
DECLARE_METHOD(log_item_data *, item_set_with_key,
(log_item * li, log_item_type t, const char *key, uint32 alloc));
/**
As log_item_set_with_key(), except that the key is automatically
derived from the wellknown log_item_type t.
Create new log item with type "t".
Will return a pointer to the item's log_item_data struct for
convenience.
This is mostly interesting for filters and other services that create
items that are not part of a log_line; sources etc. that intend to
create an item for a log_line (the more common case) should usually
use the below line_item_set_with_key() which creates an item (like
this function does), but also correctly inserts it into a log_line.
The allocation of this item will be LOG_ITEM_FREE_NONE;
specifically, any pre-existing value will be clobbered.
It is therefore WRONG
a) to use this on a log_item that already has a key;
it should only be used on freshly init'd log_items;
b) to use this on a log_item that already has a
value (specifically, an allocated one); the correct
order is to init a log_item, then set up type and
key, and finally to set the value. If said value is
an allocated string, the log_item's alloc should be
bitwise or'd with LOG_ITEM_FREE_VALUE.
@param li the log_item to work on
@param t the item-type
@retval a pointer to the log_item's log_data, for easy chaining:
log_item_set_with_key(...)->data_integer= 1;
*/
DECLARE_METHOD(log_item_data *, item_set, (log_item * li, log_item_type t));
/**
Create new log item in log line "ll", with key name "key", and
allocation flags of "alloc" (see enum_log_item_free).
On success, the number of registered items on the log line is increased,
the item's type is added to the log_line's "seen" property,
and a pointer to the item's log_item_data struct is returned for
convenience.
@param ll the log_line to work on
@param t the item-type
@param key the key to set on the item.
ignored for non-generic types (may pass nullptr for those)
see alloc
@param alloc LOG_ITEM_FREE_KEY if key was allocated by caller
LOG_ITEM_FREE_NONE if key was not allocated
Allocated keys will automatically free()d when the
log_item is.
The log_item's alloc flags will be set to the
submitted value; specifically, any pre-existing
value will be clobbered. It is therefore WRONG
a) to use this on a log_item that already has a key;
it should only be used on freshly init'd log_items;
b) to use this on a log_item that already has a
value (specifically, an allocated one); the correct
order is to init a log_item, then set up type and
key, and finally to set the value. If said value is
an allocated string, the log_item's alloc should be
bitwise or'd with LOG_ITEM_FREE_VALUE.
@retval a pointer to the log_item's log_data, for easy chaining:
log_line_item_set_with_key(...)->data_integer= 1;
*/
DECLARE_METHOD(log_item_data *, line_item_set_with_key,
(log_line * ll, log_item_type t, const char *key, uint32 alloc));
/**
Create a new log item of well-known type "t" in log line "ll".
On success, the number of registered items on the log line is increased,
the item's type is added to the log_line's "seen" property,
and a pointer to the item's log_item_data struct is returned for
convenience.
The allocation of this item will be LOG_ITEM_FREE_NONE;
specifically, any pre-existing value will be clobbered.
It is therefore WRONG
a) to use this on a log_item that already has a key;
it should only be used on freshly init'd log_items;
b) to use this on a log_item that already has a
value (specifically, an allocated one); the correct
order is to init a log_item, then set up type and
key, and finally to set the value. If said value is
an allocated string, the log_item's alloc should be
bitwise or'd with LOG_ITEM_FREE_VALUE.
@param ll the log_line to work on
@param t the item-type
@retval a pointer to the log_item's log_data, for easy chaining:
log_line_item_set_with_key(...)->data_integer= 1;
*/
DECLARE_METHOD(log_item_data *, line_item_set,
(log_line * ll, log_item_type t));
/**
Dynamically allocate and initialize a log_line.
@retval nullptr could not set up buffer (too small?)
@retval other address of the newly initialized log_line
*/
DECLARE_METHOD(log_line *, line_init, ());
/**
Release a log_line allocated with line_init()
@param ll a log_line previously allocated with line_init()
*/
DECLARE_METHOD(void, line_exit, (log_line * ll));
/**
How many items are currently set on the given log_line?
@param ll the log-line to examine
@retval the number of items set
*/
DECLARE_METHOD(int, line_item_count, (log_line * ll));
/**
Test whether a given type is presumed present on the log line.
@param ll the log_line to examine
@param m the log_type to test for
@retval 0 not present
@retval !=0 present
*/
DECLARE_METHOD(log_item_type_mask, line_item_types_seen,
(log_line * ll, log_item_type_mask m));
/**
Get log-line's output buffer.
If the logger core provides this buffer, the log-service may use it
to assemble its output therein and implicitly return it to the core.
Participation is required for services that support populating
performance_schema.error_log, and optional for all others.
@param ll the log_line to examine
@retval nullptr success, an output buffer is available
@retval otherwise failure, no output buffer is available
*/
DECLARE_METHOD(log_item *, line_get_output_buffer, (log_line * ll));
/**
Get an iterator for the items in a log_line.
For now, only one iterator may exist per log_line.
@param ll the log_line to examine
@retval a log_iter_iter, or nullptr on failure
*/
DECLARE_METHOD(log_item_iter *, line_item_iter_acquire, (log_line * ll));
/**
Release an iterator for the items in a log_line.
@param it the iterator to release
*/
DECLARE_METHOD(void, line_item_iter_release, (log_item_iter * it));
/**
Use the log_line iterator to get the first item from the set.
@param it the iterator to use
@retval pointer to the first log_item in the collection, or nullptr
*/
DECLARE_METHOD(log_item *, line_item_iter_first, (log_item_iter * it));
/**
Use the log_line iterator to get the next item from the set.
@param it the iterator to use
@retval pointer to the next log_item in the collection, or nullptr
*/
DECLARE_METHOD(log_item *, line_item_iter_next, (log_item_iter * it));
/**
Use the log_line iterator to get the current item from the set.
@param it the iterator to use
@retval pointer to the current log_item in the collection, or nullptr
*/
DECLARE_METHOD(log_item *, line_item_iter_current, (log_item_iter * it));
/**
Complete, filter, and write submitted log items.
This expects a log_line collection of log-related key/value pairs,
e.g. from log_message().
Where missing, timestamp, priority, thread-ID (if any) and so forth
are added.
Log item source services, log item filters, and log item sinks are
then called; then all applicable resources are freed.
This interface is intended to facilitate the building of submission
interfaces other than the variadic message() one below. See the
example fluent C++ LogEvent() wrapper for an example of how to leverage
it.
@param ll key/value pairs describing info to log
@retval int number of fields in created log line
*/
DECLARE_METHOD(int, line_submit, (log_line * ll));
/**
Submit a log-message for log "log_type".
Variadic convenience function for logging.
This fills in the array that is used by the filter and log-writer
services. Where missing, timestamp, priority, and thread-ID (if any)
are added. Log item source services, log item filters, and log item
writers are called.
The variadic list accepts a list of "assignments" of the form
- log_item_type, value, for well-known types, and
- log_item_type, key, value, for ad-hoc types (LOG_ITEM_GEN_*)
As its last item, the list should have
- an element of type LOG_ITEM_LOG_MESSAGE, containing a printf-style
format string, followed by all variables necessary to satisfy the
substitutions in that string
OR
- an element of type LOG_ITEM_LOG_LOOKUP, containing a MySQL error code,
which will be looked up in the list or regular error messages, followed
by all variables necessary to satisfy the substitutions in that string
OR
- an element of type LOG_ITEM_LOG_VERBATIM, containing a string that will
be used directly, with no % substitutions
see log_vmessage() for more information.
*/
DECLARE_METHOD(int, message, (int log_type, ...));
/**
Escape \0 bytes, add \0 terminator. For log-writers and other sinks
that terminate in an API using C-strings.
@param li list_item to process
@retval -1 out of memory
@retval 0 success
*/
DECLARE_METHOD(int, sanitize, (log_item * li));
/**
Return MySQL error message for a given error code.
@param mysql_errcode the error code the message for which to look up
@retval the message (a printf-style format string)
*/
DECLARE_METHOD(const char *, errmsg_by_errcode, (int mysql_errcode));
/**
Return MySQL error code for a given error symbol.
@param sym the symbol to look up
@retval -1 failure
@retval >=0 the MySQL error code
*/
DECLARE_METHOD(longlong, errcode_by_errsymbol, (const char *sym));
/**
Convenience function: Derive a log label ("error", "warning",
"information") from a severity.
@param prio the severity/prio in question
@return a label corresponding to that priority.
@retval "System" for prio of SYSTEM_LEVEL
@retval "Error" for prio of ERROR_LEVEL
@retval "Warning" for prio of WARNING_LEVEL
@retval "Note" for prio of INFORMATION_LEVEL
*/
DECLARE_METHOD(const char *, label_from_prio, (int prio));
/**
Parse a ISO8601 timestamp and return the number of microseconds
since the epoch. Heeds +/- timezone info if present.
@see make_iso8601_timestamp()
@param timestamp an ASCII string containing an ISO8601 timestamp
@param len Length in bytes of the aforementioned string
@return microseconds since the epoch
*/
DECLARE_METHOD(ulonglong, parse_iso8601_timestamp,
(const char *timestamp, size_t len));
/**
open an error log file
@param name_or_ext if beginning with '.':
@@global.log_error, except with this extension
otherwise:
use this as file name in the same location as
@@global.log_error
Value not contain folder separators!
@param[out] my_errstream an error log handle, or nullptr on failure
@returns LOG_SERVICE_SUCCESS success
@returns LOG_SERVICE_INVALID_ARGUMENT no my_errstream, or bad log name
@returns LOG_SERVICE_OUT_OF_MEMORY could not allocate file handle
@returns LOG_SERVICE_LOCK_ERROR couldn't lock lock
@returns LOG_SERVICE_UNABLE_TO_WRITE couldn't write to given location
@returns LOG_SERVICE_COULD_NOT_MAKE_LOG_NAME could not make log name
*/
DECLARE_METHOD(log_service_error, open_errstream,
(const char *name_or_ext, void **my_errstream));
/**
write to an error log file previously opened with open_errstream()
@param my_errstream a handle describing the log file
@param buffer pointer to the string to write
@param length length of the string to write
@returns LOG_SERVICE_SUCCESS success
@returns otherwise failure
*/
DECLARE_METHOD(log_service_error, write_errstream,
(void *my_errstream, const char *buffer, size_t length));
/**
are we writing to a dedicated errstream, or are we sharing it?
@param my_errstream a handle describing the log file
@retval 0 not dedicated (multiplexed, stderr, ...)
@retval 1 dedicated
*/
DECLARE_METHOD(int, dedicated_errstream, (void *my_errstream));
/**
close an error log file previously opened with open_errstream()
@param my_stream a handle describing the log file
@returns LOG_SERVICE_SUCCESS on success
*/
DECLARE_METHOD(log_service_error, close_errstream, (void **my_errstream));
/**
re-open an error log file
(primarily to facilitate flush/log-rotation)
If the new file can be opened, update the my_errstream descriptor to
use it and close the old file. Otherwise, keep using the old file.
@param name_or_ext if beginning with '.':
@@global.log_error, except with this extension
otherwise:
use this as file name in the same location as
@@global.log_error
Value may not contain folder separators!
In the general case, the caller will be a
log-writer, the log-writer will just pass
its preferred file extension, and the resulting
file name and path will therefore be the same
as for the original log file.
@param[in,out] my_errstream an error log handle
@returns LOG_SERVICE_INVALID_ARGUMENT, or the result of open_errstream()
*/
DECLARE_METHOD(log_service_error, reopen_errstream,
(const char *file, void **my_errstream));
END_SERVICE_DEFINITION(log_builtins)
extern SERVICE_TYPE(log_builtins) * log_bi;
extern SERVICE_TYPE(log_builtins_string) * log_bs;
#define log_line_init log_bi->line_init
#define log_line_exit log_bi->line_exit
#define log_line_item_set_with_key log_bi->line_item_set_with_key
#define log_line_item_set log_bi->line_item_set
#define log_line_item_types_seen log_bi->line_item_types_seen
#define log_line_submit log_bi->line_submit
#define log_set_int log_bi->item_set_int
#define log_set_float log_bi->item_set_float
#define log_set_lexstring log_bi->item_set_lexstring
#define log_set_cstring log_bi->item_set_cstring
#define log_malloc log_bs->malloc
#define log_free log_bs->free
#define log_msg log_bs->substitutev
#define error_msg_by_errcode log_bi->errmsg_by_errcode
#define error_code_by_errsymbol log_bi->errcode_by_errsymbol
其中通过定义log_builtins_imp类实现LogErr中的具体方法
sql\server_component\server_component.cc:
DEFINE_METHOD(log_line *, log_builtins_imp::line_init, ()) {
return log_line_init();
}
/**
Release a log_line allocated with line_init()
@param ll a log_line previously allocated with line_init()
*/
DEFINE_METHOD(void, log_builtins_imp::line_exit, (log_line * ll)) {
log_line_exit(ll);
}
...
sql\server_component\server_component.cc:
BEGIN_SERVICE_IMPLEMENTATION(mysql_server, log_builtins) //定义 imp_mysql_server_log_builtins
log_builtins_imp::wellknown_by_type, log_builtins_imp::wellknown_by_name,
log_builtins_imp::wellknown_get_type, log_builtins_imp::wellknown_get_name,
log_builtins_imp::item_inconsistent, log_builtins_imp::item_generic_type,
log_builtins_imp::item_string_class, log_builtins_imp::item_numeric_class,
log_builtins_imp::item_set_int, log_builtins_imp::item_set_float,
log_builtins_imp::item_set_lexstring, log_builtins_imp::item_set_cstring,
log_builtins_imp::item_set_with_key, log_builtins_imp::item_set,
log_builtins_imp::line_item_set_with_key, log_builtins_imp::line_item_set,
log_builtins_imp::line_init, log_builtins_imp::line_exit,
log_builtins_imp::line_item_count,
log_builtins_imp::line_item_types_seen,
log_builtins_imp::line_get_output_buffer,
log_builtins_imp::line_item_iter_acquire,
log_builtins_imp::line_item_iter_release,
log_builtins_imp::line_item_iter_first,
log_builtins_imp::line_item_iter_next,
log_builtins_imp::line_item_iter_current,
log_builtins_imp::line_submit,
log_builtins_imp::message,
log_builtins_imp::sanitize,
log_builtins_imp::errmsg_by_errcode, log_builtins_imp::errcode_by_errsymbol,
log_builtins_imp::label_from_prio,
log_builtins_imp::parse_iso8601_timestamp,
log_builtins_imp::open_errstream, log_builtins_imp::write_errstream,
log_builtins_imp::dedicated_errstream, log_builtins_imp::close_errstream,
log_builtins_imp::reopen_errstream END_SERVICE_IMPLEMENTATION();
在__mysql_server_provides数组中调用前面定义的变量imp_mysql_server_log_builtins
__mysql_server_provides[]数组定义:
struct mysql_service_ref_t {
const char *name;
void *implementation;
};
BEGIN_COMPONENT_PROVIDES(mysql_server)
PROVIDES_SERVICE(mysql_server_path_filter, dynamic_loader_scheme_file),
PROVIDES_SERVICE(mysql_server, persistent_dynamic_loader),
PROVIDES_SERVICE(mysql_server, dynamic_privilege_register),
PROVIDES_SERVICE(mysql_server, global_grants_check),
PROVIDES_SERVICE(mysql_server, mysql_charset),
PROVIDES_SERVICE(mysql_server, mysql_string_factory),
PROVIDES_SERVICE(mysql_server, mysql_string_case),
PROVIDES_SERVICE(mysql_server, mysql_string_converter),
PROVIDES_SERVICE(mysql_server, mysql_string_charset_converter),
PROVIDES_SERVICE(mysql_server, mysql_string_character_access),
PROVIDES_SERVICE(mysql_server, mysql_string_byte_access),
PROVIDES_SERVICE(mysql_server, mysql_string_iterator),
PROVIDES_SERVICE(mysql_server, mysql_string_ctype),
PROVIDES_SERVICE(mysql_server, mysql_string_reset),
PROVIDES_SERVICE(mysql_server, mysql_string_append),
PROVIDES_SERVICE(mysql_server, mysql_string_compare),
PROVIDES_SERVICE(mysql_server, mysql_string_get_data_in_charset),
PROVIDES_SERVICE(mysql_server, log_builtins),
PROVIDES_SERVICE(mysql_server, log_builtins_filter),
PROVIDES_SERVICE(mysql_server, log_builtins_filter_debug),
PROVIDES_SERVICE(mysql_server, log_builtins_string),
PROVIDES_SERVICE(mysql_server, log_builtins_tmp),
PROVIDES_SERVICE(mysql_server, log_builtins_syseventlog),
PROVIDES_SERVICE(mysql_server, log_sink_perfschema),
PROVIDES_SERVICE(mysql_server, udf_registration),
PROVIDES_SERVICE(mysql_server, udf_registration_aggregate),
PROVIDES_SERVICE(mysql_server, mysql_udf_metadata),
PROVIDES_SERVICE(mysql_server, component_sys_variable_register),
PROVIDES_SERVICE(mysql_server, component_sys_variable_unregister),
PROVIDES_SERVICE(mysql_server, mysql_cond_v1),
...
END_COMPONENT_PROVIDES();
struct mysql_component_t {
const char *name;
struct mysql_service_ref_t *provides;
struct mysql_service_placeholder_ref_t *requires;
struct mysql_metadata_ref_t *metadata;
mysql_service_status_t (*init)();
mysql_service_status_t (*deinit)();
};
定义变量 mysql_component_mysql_server:
DECLARE_COMPONENT(mysql_server, "mysql:core")
mysql_server_init, mysql_server_deinit END_DECLARE_COMPONENT();
最后通过函数的方式放回全局变量的地址 mysql_component_mysql_server:
#ifndef WITH_MYSQL_COMPONENTS_TEST_DRIVER
/* components contained in this library.
for now assume that each library will have exactly one component. */
DECLARE_LIBRARY_COMPONENTS &COMPONENT_REF(mysql_server)
END_DECLARE_LIBRARY_COMPONENTS
宏定义如下:
#define DECLARE_LIBRARY_COMPONENTS \
mysql_component_t *library_components_list = {
/**
A macro to end the last declaration started with the
DECLARE_LIBRARY_COMPONENTS.
*/
#define END_DECLARE_LIBRARY_COMPONENTS \
} \
; \
DLL_EXPORT mysql_component_t *list_components() { \
return library_components_list; \
}
/**
Defines a reference to the specified Component data info structure.
*/
#define COMPONENT_REF(name) mysql_component_##name
/**
This is the component module entry function, used to get the component's
structure to register the required services.
*/
#define COMPONENT_ENTRY_FUNC "list_components"
需要使用时通过打开动态库的方式返回全局变量mysql_component_mysql_server的地址
DEFINE_BOOL_METHOD(mysql_dynamic_loader_scheme_file_imp::load,
(const char *urn, mysql_component_t **out_data)) {
try {
if (urn == nullptr) {
return true;
}
std::string urn_string = urn;
/* Check if library is not already loaded, by comparing URNs. */
minimal_chassis::rwlock_scoped_lock lock(
&mysql_dynamic_loader_scheme_file_imp::LOCK_dynamic_loader_scheme_file,
true, __FILE__, __LINE__);
if (object_files_list.find(urn_string) != object_files_list.end()) {
return true;
}
/* Omit scheme prefix to get filename. */
const char *file = strstr(urn, "://");
if (file == nullptr) {
return true;
}
/* Offset by "://" */
file += 3;
std::string file_name = std::string(file);
#ifdef _WIN32
file_name += ".dll";
#else
file_name += ".so";
#endif
/* Open library. */
void *handle = dlopen(file_name.c_str(), RTLD_NOW); //打开动态库
if (handle == NULL) {
const char *errmsg;
int error_number = dlopen_errno;
DLERROR_GENERATE(errmsg, error_number);
mysql_error_service_printf(ER_CANT_OPEN_LIBRARY, MYF(0),
file_name.c_str(), error_number, errmsg);
return true;
}
auto guard_library = create_scope_guard([&handle]() {
/* In case we need to rollback we close the opened library. */ //如果需要回滚,则关闭打开的库。
dlclose(handle);
});
/* Look for "list_components" function. */
list_components_func list_func = reinterpret_cast<list_components_func>(
dlsym(handle, COMPONENT_ENTRY_FUNC));
if (list_func == NULL) {
return true;
}
/* Check if library is not already loaded, by comparing "list_components"
function address. */
if (library_entry_set.insert(list_func).second == false) {
return true;
}
auto guard_library_set = create_scope_guard([&list_func]() {
/* In case we need to rollback we remove library handle from set. */
library_entry_set.erase(list_func);
});
/* Get components data from library.
It is ok for not copying the data even the mutex is released at the
function exit, since it is used under the registry
services (as they take another mutex) */
*out_data = list_func();
/* Add library and it's handle to list of loaded libraries. */
if (object_files_list.emplace(urn_string, handle).second == false) {
return true;
}
guard_library.commit();
guard_library_set.commit();
return false;
} catch (...) {
}
return true;
}