WL#6661: Error logging: Allow control of verbosity

Affects: Server-5.7   —   Status: Complete

We want to give the DBA control of how "verbose" the MySQL Server should be
(error/warning/note) when writing to the error log. This work is partly
motivated by
http://datacharmer.blogspot.jp/2012/03/mysql-56-too-verbose-when-creating-
data.html

A possible design could be three levels of verbosity:
1) Write only "Errors"
2) Write "Errors + Warnings"
3) Write "Errors + Warnings + Notes" (default)

Two other requests we have gotten related to error logging from MEM:
- Change the timestamp format printed to a more standard format (syslog)
- Convert existing fprintf(stderr, ...) in the server layer to use
  error logging API whenever possible.
These should also be handled in the scope of this worklog.

User Documentation
==================

http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-2.html
http://dev.mysql.com/doc/refman/5.7/en/server-system-
variables.html#sysvar_log_error_verbosity
http://dev.mysql.com/doc/refman/5.7/en/server-system-
variables.html#sysvar_log_timestamps
http://dev.mysql.com/doc/refman/5.7/en/server-system-
variables.html#sysvar_log_warnings
http://dev.mysql.com/doc/refman/5.7/en/error-log.html (last parts about verbosity 
and format)
SCOPE

#0a Certain warnings may be outside the immediate scope of this
    worklog, such as warnings printed in emergency states where
    the log system is considered non-functional or uninitialized.

  b Not all of InnoDB uses the mysqld-proper logging facilities
    at this point. WL#6755 "InnoDB: Use of error logging API in
    InnoDB" is designed to amend this.   Inno warnings will be
    subject to the new facilities to the extent that they use them;
    increasing such use falls into the scope of WL#6755, not WL#6661.



NOMENCLATURE

#1  In the context of this worklog, information items for the error
    logging facilities are assumed to be tagged with one of three
    levels of urgency.  The most urgent type of message we log is
    the error (message), followed by the warning, and the note,
    respectively.

    In the server, this is modeled by sql_print_(errror|warning|information),
    and (ERROR|WARNING|INFORMATION)_LEVEL for use by the server proper,
    and MY_(ERROR|WARNING|INFORMATION)_LEVEL for plugins, respectively.

    The Windows event log uses error, warning, and information levels.

    The UNIX syslog offers Notice and Information levels, with Notice
    conveying more urgency.

    This document shall use the user-visible nomenclature of error, warning,
    note however, so for symmetry with the errors, warnings, and notes we
    issue to clients is maintained.  This is done in the hope of keeping
    the user experience consistent and accessible.



HELP / --help

#2a --help shows help, as before

  b The default verbosity shall be errors only, as per the request
    that lead to the worklog in the first place, but this shouldn't
    make much of a difference as we have a short code-path anyway
    (no loading of plugins etc.).



SHOW VARIABLES / --help --verbose

#3a --help --verbose shows help and variables, as before.

  b The default verbosity shall be errors only, as per the request
    that lead to the worklog in the first place, so that failures
    to load plugins (that could result in fewer options being shown)
    will still be flagged, but the output will remain as terse as
    circumstances allow otherwise.


VERBOSITY

--log_error_verbosity

#4a A new system-variable log_error_verbosity shall govern the
    verbosity of error messages not issued to the client, but
    logged on the server side, to the error log file, syslog,
    Windows event log, or similar facilities.

  b The default setting for log_error_verbosity is
    ERRORS+WARNINGS+NOTES.

    This maintains the current default of not gagging any of
    (previously ungaggable) errors, warnings, and notes that
    may be logged to the error logging facilities.

  c log_error_verbosity requires a numeric argument.
    The values 1, 2, 3  shall request errors, +warnings, +notes,
    respectively.

  d log_error_verbosity shall be settable on the command-line at startup.

  e log_error_verbosity shall be a global variable settable at runtime by
    those with the SUPER privilege.



--verbose

#5  Use of --verbose without --help shall result in an error being
    issued to the points the user to --log_error_verbosity.



--log_warnings

#6  Information items previously governed by --log_warnings/-W
    shall now be governed by --log_error_verbosity, like so:

  a Items previously printed when log_warnings=1 or higher was
    requested will now count as warnings and be printed at
    log_error_verbosity=2 or higher.

    [NOTE: log_warnings=1 was the previous default. These
           warnings will still be printed by default. cf. #4b]

  b Items previously printed when log_warnings=2 was requested
    will now count as notes and be printed at log_error_verbosity=3.

    [NOTE: This additional information will now also be printed
           by default. cf. #4b]

  c Gagging of all log_warnings items, previously achieved by
    requesting log_warnings=0, will now be achieved by setting
    log_error_verbosity=1, "errors only."



#7  Use of --log_warnings shall result in a deprecation warning,
    pointing the user to --log_error_verbosity.



#8  Use of --log_warnings shall, until such a time as it is removed,
    attempt to emulate its previous behavior:

  a --log_warnings= shall result in an appropriate --error_log_verbosity
    to be set, i.e. an explicit

      --log_warnings=0   will set --verbosity=1, errors only
      --log_warnings=1   will set --verbosity=2, +warnings
      --log_warnings=2   will set --verbosity=3, +notes


#9  Current behavior of -W (with no numerical argument) increases
    the current value by 1 for each time it is given. This shall be
    maintained while -W exists.

    Absent of any -W, the first -W currently therefore increases
    the default of 1 by 1 for a total of 2. It follows that the only
    way to set level 0 is therefore the explicit -W0.

    Henceforth, a) the effects formerly controlled by log_warnings
    shall be controlled by log_error_verbosity (cf. #6) and
    b) log_error_verbosity shall default to maximum verbosity (cf. #4b).
    The effect formerly requested by a sole -W (maximum verbosity) is
    already the default, so -W as the lone option will now have no
    visible effect.



COMBINATIONS

#A  No combination of options will cause the final verbosity
    to be outside of the allowed range (1..3).



#B  If log_error_verbosity= or log_warnings=
    are both given, or either form appears multiple times, the
    assignment that is processed last (the rightmost assignment
    on the command-line, or the bottommost assignment in a .cnf)
    shall prevail.



BOOTSTRAP / --bootstrap

#C  Like --help, --bootstrap shall lower the default for error_log_verbosity
    to "errors only".  This is done in an attempt not to "flood"
    the user with messages at install time, and potentially have them miss
    error messages.  This directly addresses one of the suggestions that led
    to this WL item.



TIMESTAMPS

[NOTE: The following applies to log-files.  Log tables already use
       the TIMESTAMP type and can be converted on SELECT at will.]



#D  Log file lines shall be timestamped in ISO 8601 / RFC 3339
    format %04d-%02d-%02dT%02d:%02d:%02d.%06lu%s, with the tail %s
    being Z for zulu time, and of the form +|- hh:mm otherwise.

    Log tables shall be changed to likewise support microsecond
    resolution in timestamps; new column type shall be TIMESTAMP(6).



#Ea A new command-line option / system-variable --log-timestamps
    can be used to select UTC or local time timestamps, respectively.

  b Valid values shall be:    SYSTEM | UTC

  c Default shall be:         UTC

  d The value of the global variable may be changed at runtime by
    those with the SUPER privilege.

  e "SYSTEM" is an analog of current behavior (use of localtime_r(),
    plus now the timezone variable).



OTHER CHANGES
(attn docs team)

Clients:
The default function for printing error messages will now
prepend the running program's name (just the name, for
"bin/mysqladmin.exe", we'd get "mysqladmin", no path, no ".exe").
This is particularly useful for debugging mysql_upgrade, which
in turn calls various other binaries: the new behavior makes it
much easier to tell whether a warning was thrown by mysql_upgrade,
or by one of its child process (and in the latter case, which).
Severity levels (which are added by the default wrapper around above
function) also now have brackets around them, so lines that used to
start with e.g. "Warning"/"WARNING" will now start "my_name: [Warning] "
These default reporters can be overridden, but in practice, the clients
don't, while the server does (early in its initialization, from whence
it goes through our new-and-improved function that adds ISO timestamps,
filters by --log_error_verbosity, etc.). So as a general rule, "this only
applies to clients."

Fixes for logging, after discussion with Mark:

The server proper prints the MySQL thread ID (connection ID)
to its general and slow query logs.

In contrast, it used to print the server's process ID to the
error log. This has changed; for consistency, this column now
also shows a connection ID in the rare case that that's applicable,
and 0 otherwise (e.g. when the condition was raised in a thread
that has no connection).

The server will now report its process ID at start-up instead.
This message is logged early at startup, so the PID will be
available to monitoring even if the server is accidentally
double-started by the DBA (in which case we likely won't get
all the way to ER_STARTUP, and won't see both PIDs in the pid-file).

Some legacy reporting in InnoDB prints a thread ID to the error
log that is not the MySQL thread-ID / connection ID, but the system's
thread ID. To distinguish this from connection IDs, this thread ID is
printed in hexadecimal. WL#6661 now adds a proper 0x prefix to those
hex values, for clarity when the value does not contain a...f.

James Day observes:

Doesn't seem to meet key diagnostic needs yet, Notes are not of equal
importance. Warnings aren't of equal importance either.

Some examples:

1. Critical notes that must never be suppressed, whatever the setting, Always
logged to error log because they are critical diagnostic and status information
that cannot normally be recovered later by changing a setting (looking back in
time is often needed for these):

2013-03-01 20:01:38 2752 [Note] InnoDB: 1.2.10 started; log sequence number 1625987
2013-02-25 22:25:04 5732 [Note] bin\mysqld: ready for connections.
Version: '5.6.10-enterprise-commercial-advanced'  socket: ''  port: 3306  MySQL
Enterprise Server - Advanced Edition (Commercial)

Replication start and stop IO and SQL thread positions.

2013-03-01 23:08:53 2752 [Note] InnoDB: Starting shutdown...
2013-03-01 23:08:54 2752 [Note] InnoDB: Shutdown completed; log sequence number
1804146
2013-02-25 22:28:08 5732 [Note] bin\mysqld: Normal shutdown

2. High noise level warnings, best treated as low priority warnings or notes.
These are noise because of their very high frequency if they happen:

client connection errors
warning that statement may not be safe for statement replication mode

Both of those can generate huge numbers of error log entries.

3. Low priority notes:

Routine plugin start and stop are examples.

So three verbosity settings might be:

1.
Errors: all
Warnings: critical only
Notes: critical only

2.
Errors: all
Warnings: critical, normal priority
Notes: critical, normal (but no notes currently expected to be normal priority).

3.
Errors: all
Warnings: critical, normal and low priority (all)
Notes: critical, normal, low priority (all)

All existing warnings are normal priority unless mentioned here or otherwise.
All existing notes are low priority unless mentioned here or otherwise mentioned.

Support team can generate a list of warnings that should be critical, normal,
low priority, Will not be many exceptions. Code can have priority level 1,2 or 3
in function call.

Result: level 1, errors and critical notes end up being displayed, highest
priority items, Turn on level 2 and you get all but common spam warnings and
most notes. Level 3 gets you all. And we can adjust priority based on Support
and other feedback.


Tatiana says:
I appreciate that some things should always be printed.
The name for that in the current nomenclature is "error."

Now, I understand that you may feel that that's not a very
good name for something that is not a problem, just generally
important to know. In that case, we'd have to change the system
to not gag according to error, warning, note at all, but according
to critical, important, supplementary.  Which I'm not sure you'll
notice is the exact same mechanism as specified anyway.

I very much appreciate beautiful (or at least clear) language as a
concern, but at the same time, it was suggested that we should mirror
the error, warning, note classification that is sent to the client.

This gives us three possible courses of action:

- Keep the 3 level gagging, let both factions reason out what the nomenclature
for the bike shed should be.

- Keep the 3 level gagging, use critical etc., and let individual messages use
words like "error" in the descriptive text if they wish to. This might result in
a mess as it'd hard to apply consistently, especially across localizations.

- Keep 3 level gagging, pass *two* parameters, one for severity (critical etc.)
one for "name" (error, warning, note), and then print the composite (e.g.
[critical error]), but filter according to severity, not the name tag. This
would imply changing every call to s.th. like error_log_message(prio,
funky_name_tag, actual_message), and then manually adjusting the prio for the
outliers. Might give us a chance to drop the print_(error|warning|information)
trifecta, of which sql_print_information is badly named anyway (as it prints a
label of "Note") -- in which case it would probably be reasonable to change the
symbol to NOTE_LEVEL too, as well as supporting MY_NOTE_LEVEL, while
keeping-but-discouraging MY_INFORMATION_LEVEL to maintain compatibility with
existing plugins.

No matter whether we beautify the naming, the upshot will be that we'll need a
change request for each of the outliers (that don't fall into the classical
combinations "error / high prio", "warning / mid prio", "note / low prio").


PREAMBLE

The thing to keep in mind is that the server now defaults to its most verbose,
"INFORMATION_LEVEL."  This gives us the following guidelines:

a) Items that previously were printed when running with defaults
   will be printed when running with defaults now.

b) Some items that previously weren't printed when running with
   defaults (but could be requested) will be printed now (but can
   be muted).

c) Some items that previously (wrongly, because they didn't use
   the proper facilities) could not be muted can be muted now
   (but aren't by default).



CODE

On a code level, (almost) all fprintf(stderr, ...) have been converted to
sql_print_(error|warning|information).  This means they can now be filtered.

sql_print_(error|warning|information) was already used in the code in many
places, but since there was no central gagging facility, most items could not be
gagged, and some implemented their own gagging, not always on the level the
label implied (a line could appear with a "warning" label in the label, while
actually requiring the higher verbosity level of "information" to be printed, etc.):

- Many lines of the form if(verbosity>WARNING_LEVEL) sql_print_warning()
  had their now redundant if() cut. => no change in observable behavior

- We do not at this point allow gagging of error messages
  (only of warning/info).  The behavior for all sql_print_error()
  is therefore also unchanged (couldn't be bagged before, can't be now).

- Where sql_print_warning / sql_print_information did heed the
  verbosity level (log_warning, the predecessor of log_error_verbosity),
  but the label ("Warning", "Information") did not reflect the
  verbosity level required for this item to be printed
  (WARNING_LEVEL, INFORMATION_LEVEL, respectively), care was taken
  to align label and level in a way reflecting the spirit of the
  original code, usually towards the end of no or minimal change
  in user-observable behavior (i.e. a different label on the log line).
  Additionally, these occurrences are listed below for a review beyond
  the immediate (and completed) code review, should this become
  desirable, as well as for the information of groups beyond #runtime.

- Where sql_print_warning / sql_print_information were unconditional,
  they now heed the verbosity level set by the user.  That level now
  defaults to maximum verbosity, meaning that all such items as were
  printed unconditionally before shall still be printed by default.
  Additionally, the user can now elect to mute information type log
  items, or both warning type and information type log items.
  It follows that by default, the goal was once again to have no change
  in user-observable behavior (but offer options beyond the default, in
  which case behavior will change).



EXPECTED IMPLICATIONS

--bootstrap and therefore the installation process should now print
fewer non-essential items, to guard against overwhelming the new MySQL user.
This should improve the user experience.

Similar for --help --verbose.

There are some additional "information" type log items that previously
were available on request, and are now printed by default.  This may
provide support & development with additional diagnostics.

Conversely, a mechanism for gagging many diagnostic log items can be utilized by
the user to suppress items that may have been beneficial in supporting said
user.  There may be a case for suggesting to supply log snippets with the
highest verbosity level set when requesting support, if this is not already the
case.  (The "Don't set verbosity too low,
shoot yourself in the foot, or try to ski through a revolving door."
clause.)



GLOBAL CHANGES

#0 --bootstrap and --help behave differently in that they gag
   everything but errors, to de-spam installation.

#1 log_warnings, both as a system variable and on the command
   line, will throw a deprecation warning pointing the user
   at log_error_verbosity.

#2 Where appropriate, tools (mysqlupdate, mysqladmin, mysql,
   "items that live in client/") might prefix warnings with
   their name. This is mostly useful in the context of mysqlupgrade,
   which in turn run mysql, mysqlcheck, etc. With the new semantics,
   it's easier to see which of the tools threw a warning (as opposed
   to just seeing that there was one).

#3 log_error_verbosity and log_timestamps were added, as per the spec.
   See there.

#4 Timestamps now show microseconds.

#5 Timestamps in log files (not tables) can be set to reflect UTC, or
   system time.  See #3.  They're ISO compliant.

#6 We now consistently log thread-ID (rather than process-ID)
   if we have it.
   Additionally, process-ID is issued during start-up, but can
   still also be retrieved from the PID file.



INDIVIDUAL CHANGES

Below is a list of explicitly affected items.
The list including implicitly affected items can easily be
retrieved using

   find mysql-5.7/ -iname "*.[ch]*" \
     -exec egrep -HI "sql_print_(warning|information)" {} \; 

a) In debug servers, mf_keycache.c will run a sanity check,
   cache_empty(). This is now deemed INFORMATION_LEVEL.
   Non-debug servers do not print at any level now, and never did.

b) In my_largepage.c, several non-lethal failure warnings were
   formerly prefixed with "Warning: "; these are now issued at
   WARNING_LEVEL.

c) In my_open.c, EXTRA_DEBUG function my_print_open_files()
   now prints its information at INFORMATION_LEVEL.

d) In my_thr_init.c, normal operation always prints its errors
   (ERROR_LEVEL), while the specific EXTRA_DEBUG_THREADS items
   are issued at WARNING_LEVEL for the one warning, and
   INFORMATION_LEVEL otherwise.

e) thr_alarm.c prints its single warning at WARNING_LEVEL.

f) my_default.cc prints the three items previously prefixed with
   "Warning:" at WARNING_LEVEL (everything else at ERROR_LEVEL).

g) my_getopt.cc's "password on command line insecure" warning is
   issued at WARNING_LEVEL.

h) "init called twice" warnings in semisync_(master|slave).cc
   are issued at WARNING_LEVEL.

i) SSL: client_authentication.cc
   "Can't locate server public key", "Public key is not in PEM format"
   issued at WARNING_LEVEL.

j) sql_authentication.cc
   "access denied: no password" was always issued at INFORMATION_LEVEL,
   but previously wrongly had a "Warning" label on the log line. It is
   now issued with the correct label.
   Likewise for regular "access denied" message.
   Likewise for "must change password"
 ! Conversely, X509 subject and issuer mismatch messages were called
 ! info, but issued at WARNING_LEVEL. Now issued at INFORMATION_LEVEL.

k) filesort.cc
   printed some information at INFORMATION_LEVEL, but labeled it a
   warning. Now labeled information.

l) log_event.cc
   issues where reported at INFORMATION_LEVEL, but received a warning
   label. label was corrected.

m) sql_connect.cc
   "aborting connection" was printed at INFORMATION_LEVEL, but labeled
   a warning. Label corrected.

n) sql_plugin.cc
 ! "plugin disabled" always labeled info, but previously printed
   at WARNING_LEVEL, now at INFORMATION_LEVEL.

o) ut0ut.cc
   already printed thread_ID in hex, but now uses a hex prefix to
   make that more clear

p) rpl_slave.cc
   Slave reconnect was labeled information, but could be
   printed at WARNING_LEVEL. Now consistently printed at
   INFORMATION_LEVEL.

q) mi_dbug.c
   fprintf(stderr, "Warning: ...") for EXTRA_DEBUG check_table_is_closed
   now printed at WARNING_LEVEL.

r) mysqld.cc
   Buffered log messages may now include "information" type items,
   which previously ALWAYS were discarded. 

-------------------------------------8<--------------------------

NOTES TO SELF

Current Error logging API:

typedef void (*my_error_reporter)(enum loglevel level, const char *format, ...)
  ATTRIBUTE_FORMAT_FPTR(printf, 2, 3);

Function pointers (for buffered logging until logger is ready)
mysqld.cc: my_charset_error_reporter
mysqld.cc: my_getopt_error_reporter

Implemented by:
conf_to_src.c: default_reporter(...) - dummy (for tool), drops all messages
charset.c:     default_reporter(...) - dummy,            drops all messages
my_getopt.cc:  default_reporter(...) - fprintf's all levels to stderr
mysqld.cc:     charset_error_reporter(...) - error_log_print()
mysqld.cc:     buffered_option_error_reporter(...) - Buffered_log::print()
               *drops* [Note]s. Should user-setting override this?
               The rest go through sql_print_error|warning()
mysqld.cc:     option_error_reporter(...) - error_log_print(),
               special cases --bootstrap

sql_plugin.cc: reap_plugins() - Shutdown notices via sql_print_information() 
               depend on !--bootstrap

the following 3 wrap error_log_print({ERROR|WARNING|INFORMATION}_LEVEL,
                                     format, args)
log.cc:        sql_print_error(format, args...)
log.cc:        sql_print_warning(format, args...)
log.cc:        sql_print_information(format, args...)

log.cc:        my_plugin_log_message(plugin, plugin_log_level, format, args) -
               translates MY_{ERROR|WARNING|INFORMATION}_LEVEL to
               to {ERROR|WARNING|INFORMATION}_LEVEL, prefixes plugin name,
               then calls error_log_print()

log.cc:        sql_print_message_func sql_print_message_handlers[];
               ln264, until WL#6613, only used in event_scheduler.cc,
               which now uses sql_print_error|warning|information(), depending
               on SL_ERROR|WARNING|NOTE:

log.cc:        error_log_print(loglevel, format, args...) - 
               print_buffer_to_file(),
               on Win also print_buffer_to_nt_eventlog()

log.cc:        init_error_log(uint error_log_printer) - removed by WL#6613

log.cc:        log_error(loglevel, format, args...) - removed by WL#6613