mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-13 17:40:04 +00:00
doc: new logging subsystem
Signed-off-by: David Lamparter <equinox@diac24.net>
This commit is contained in:
parent
149e0bafd6
commit
0eb5f93863
@ -1,7 +1,7 @@
|
|||||||
.. _logging:
|
.. _logging:
|
||||||
|
|
||||||
Developer's Guide to Logging
|
Logging
|
||||||
============================
|
=======
|
||||||
|
|
||||||
One of the most frequent decisions to make while writing code for FRR is what
|
One of the most frequent decisions to make while writing code for FRR is what
|
||||||
to log, what level to log it at, and when to log it. Here is a list of
|
to log, what level to log it at, and when to log it. Here is a list of
|
||||||
@ -116,8 +116,11 @@ AS-Safety
|
|||||||
while AS-Safe)
|
while AS-Safe)
|
||||||
* extensions are only AS-Safe if their printer is AS-Safe
|
* extensions are only AS-Safe if their printer is AS-Safe
|
||||||
|
|
||||||
|
Log levels
|
||||||
|
----------
|
||||||
|
|
||||||
Errors and warnings
|
Errors and warnings
|
||||||
-------------------
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
If it is something that the user will want to look at and maybe do
|
If it is something that the user will want to look at and maybe do
|
||||||
something, it is either an **error** or a **warning**.
|
something, it is either an **error** or a **warning**.
|
||||||
@ -163,7 +166,7 @@ Examples for errors:
|
|||||||
|
|
||||||
|
|
||||||
Informational messages
|
Informational messages
|
||||||
----------------------
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Anything that provides introspection to the user during normal operation
|
Anything that provides introspection to the user during normal operation
|
||||||
is an **info** message.
|
is an **info** message.
|
||||||
@ -202,7 +205,7 @@ Examples:
|
|||||||
|
|
||||||
|
|
||||||
Debug messages and asserts
|
Debug messages and asserts
|
||||||
--------------------------
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Everything that is only interesting on-demand, or only while developing,
|
Everything that is only interesting on-demand, or only while developing,
|
||||||
is a **debug** message. It might be interesting to the user for a
|
is a **debug** message. It might be interesting to the user for a
|
||||||
@ -239,3 +242,176 @@ Examples:
|
|||||||
* some field that is absolutely needed is :code:`NULL`
|
* some field that is absolutely needed is :code:`NULL`
|
||||||
* any other kind of data structure corruption that will cause the daemon
|
* any other kind of data structure corruption that will cause the daemon
|
||||||
to crash sooner or later, one way or another
|
to crash sooner or later, one way or another
|
||||||
|
|
||||||
|
Thread-local buffering
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The core logging code in :file:`lib/zlog.c` allows setting up per-thread log
|
||||||
|
message buffers in order to improve logging performance. The following rules
|
||||||
|
apply for this buffering:
|
||||||
|
|
||||||
|
* Only messages of priority *DEBUG* or *INFO* are buffered.
|
||||||
|
* Any higher-priority message causes the entire buffer to be flushed, thus
|
||||||
|
message ordering is preserved properly.
|
||||||
|
* The buffers are only ever accessed by the thread they are created by. This
|
||||||
|
means no locking is necessary.
|
||||||
|
|
||||||
|
Both the main/default thread and additional threads created by
|
||||||
|
:c:func:`frr_pthread_new()` with the default :c:func:`frr_run()` handler will
|
||||||
|
initialize thread-local buffering and call :c:func:`zlog_tls_buffer_flush()`
|
||||||
|
when idle.
|
||||||
|
|
||||||
|
If some piece of code runs for an extended period, it may be useful to insert
|
||||||
|
calls to :c:func:`zlog_tls_buffer_flush()` in appropriate places:
|
||||||
|
|
||||||
|
.. c:function:: void zlog_tls_buffer_flush(void)
|
||||||
|
|
||||||
|
Write out any pending log messages that the calling thread may have in its
|
||||||
|
buffer. This function is safe to call regardless of the per-thread log
|
||||||
|
buffer being set up / in use or not.
|
||||||
|
|
||||||
|
When working with threads that do not use the :c:type:`struct thread_master`
|
||||||
|
event loop, per-thread buffers can be managed with:
|
||||||
|
|
||||||
|
.. c:function:: void zlog_tls_buffer_init(void)
|
||||||
|
|
||||||
|
Set up thread-local buffering for log messages. This function may be
|
||||||
|
called repeatedly without adverse effects, but remember to call
|
||||||
|
:c:func:`zlog_tls_buffer_fini()` at thread exit.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
If this function is called, but :c:func:`zlog_tls_buffer_flush()` is
|
||||||
|
not used, log message output will lag behind since messages will only be
|
||||||
|
written out when the buffer is full.
|
||||||
|
|
||||||
|
Exiting the thread without calling :c:func:`zlog_tls_buffer_fini()`
|
||||||
|
will cause buffered log messages to be lost.
|
||||||
|
|
||||||
|
.. c:function:: void zlog_tls_buffer_fini(void)
|
||||||
|
|
||||||
|
Flush pending messages and tear down thread-local log message buffering.
|
||||||
|
This function may be called repeatedly regardless of whether
|
||||||
|
:c:func:`zlog_tls_buffer_init()` was ever called.
|
||||||
|
|
||||||
|
Log targets
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The actual logging subsystem (in :file:`lib/zlog.c`) is heavily separated
|
||||||
|
from the actual log writers. It uses an atomic linked-list (`zlog_targets`)
|
||||||
|
with RCU to maintain the log targets to be called. This list is intended to
|
||||||
|
function as "backend" only, it **is not used for configuration**.
|
||||||
|
|
||||||
|
Logging targets provide their configuration layer on top of this and maintain
|
||||||
|
their own capability to enumerate and store their configuration. Some targets
|
||||||
|
(e.g. syslog) are inherently single instance and just stuff their config in
|
||||||
|
global variables. Others (e.g. file/fd output) are multi-instance capable.
|
||||||
|
There is another layer boundary here between these and the VTY configuration
|
||||||
|
that they use.
|
||||||
|
|
||||||
|
Basic internals
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. c:type:: struct zlog_target
|
||||||
|
|
||||||
|
This struct needs to be filled in by any log target and then passed to
|
||||||
|
:c:func:`zlog_target_replace()`. After it has been registered,
|
||||||
|
**RCU semantics apply**. Most changes to associated data should make a
|
||||||
|
copy, change that, and then replace the entire struct.
|
||||||
|
|
||||||
|
Additional per-target data should be "appended" by embedding this struct
|
||||||
|
into a larger one, for use with `containerof()`, and
|
||||||
|
:c:func:`zlog_target_clone()` and :c:func:`zlog_target_free()` should be
|
||||||
|
used to allocate/free the entire container struct.
|
||||||
|
|
||||||
|
Do not use this structure to maintain configuration. It should only
|
||||||
|
contain (a copy of) the data needed to perform the actual logging. For
|
||||||
|
example, the syslog target uses this:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
struct zlt_syslog {
|
||||||
|
struct zlog_target zt;
|
||||||
|
int syslog_facility;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void zlog_syslog(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs)
|
||||||
|
{
|
||||||
|
struct zlt_syslog *zte = container_of(zt, struct zlt_syslog, zt);
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < nmsgs; i++)
|
||||||
|
if (zlog_msg_prio(msgs[i]) <= zt->prio_min)
|
||||||
|
syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%s",
|
||||||
|
zlog_msg_text(msgs[i], NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: struct zlog_target *zlog_target_clone(struct memtype *mt, struct zlog_target *oldzt, size_t size)
|
||||||
|
|
||||||
|
Allocates a logging target struct. Note that the ``oldzt`` argument may be
|
||||||
|
``NULL`` to allocate a "from scratch". If ``oldzt`` is not ``NULL``, the
|
||||||
|
generic bits in :c:type:`struct zlog_target` are copied. **Target specific
|
||||||
|
bits are not copied.**
|
||||||
|
|
||||||
|
.. c:function:: struct zlog_target *zlog_target_replace(struct zlog_target *oldzt, struct zlog_target *newzt)
|
||||||
|
|
||||||
|
Adds, replaces or deletes a logging target (either ``oldzt`` or ``newzt`` may be ``NULL``.)
|
||||||
|
|
||||||
|
Returns ``oldzt`` for freeing. The target remains possibly in use by
|
||||||
|
other threads until the RCU cycle ends. This implies you cannot release
|
||||||
|
resources (e.g. memory, file descriptors) immediately.
|
||||||
|
|
||||||
|
The replace operation is not atomic; for a brief period it is possible that
|
||||||
|
messages are delivered on both ``oldzt`` and ``newzt``.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
``oldzt`` must remain **functional** until the RCU cycle ends.
|
||||||
|
|
||||||
|
.. c:function:: void zlog_target_free(struct memtype *mt, struct zlog_target *zt)
|
||||||
|
|
||||||
|
Counterpart to :c:func:`zlog_target_clone()`, frees a target (using RCU.)
|
||||||
|
|
||||||
|
.. c:member:: void (*zlog_target.logfn)(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsg)
|
||||||
|
|
||||||
|
Called on a target to deliver "normal" logging messages. ``msgs`` is an
|
||||||
|
array of opaque structs containing the actual message. Use ``zlog_msg_*``
|
||||||
|
functions to access message data (this is done to allow some optimizations,
|
||||||
|
e.g. lazy formatting the message text and timestamp as needed.)
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
``logfn()`` must check each individual message's priority value against
|
||||||
|
the configured ``prio_min``. While the ``prio_min`` field is common to
|
||||||
|
all targets and used by the core logging code to early-drop unneeded log
|
||||||
|
messages, the array is **not** filtered for each ``logfn()`` call.
|
||||||
|
|
||||||
|
.. c:member:: void (*zlog_target.logfn_sigsafe)(struct zlog_target *zt, const char *text, size_t len)
|
||||||
|
|
||||||
|
Called to deliver "exception" logging messages (i.e. SEGV messages.)
|
||||||
|
Must be Async-Signal-Safe (may not allocate memory or call "complicated"
|
||||||
|
libc functions.) May be ``NULL`` if the log target cannot handle this.
|
||||||
|
|
||||||
|
Standard targets
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
:file:`lib/zlog_targets.c` provides the standard file / fd / syslog targets.
|
||||||
|
The syslog target is single-instance while file / fd targets can be
|
||||||
|
instantiated as needed. There are 3 built-in targets that are fully
|
||||||
|
autonomous without any config:
|
||||||
|
|
||||||
|
- startup logging to `stderr`, until either :c:func:`zlog_startup_end()` or
|
||||||
|
:c:func:`zlog_aux_init()` is called.
|
||||||
|
- stdout logging for non-daemon programs using :c:func:`zlog_aux_init()`
|
||||||
|
- crashlogs written to :file:`/var/tmp/frr.daemon.crashlog`
|
||||||
|
|
||||||
|
The regular CLI/command-line logging setup is handled by :file:`lib/log_vty.c`
|
||||||
|
which makes the appropriate instantiations of syslog / file / fd targets.
|
||||||
|
|
||||||
|
.. todo::
|
||||||
|
|
||||||
|
:c:func:`zlog_startup_end()` should do an explicit switchover from
|
||||||
|
startup stderr logging to configured logging. Currently, configured logging
|
||||||
|
starts in parallel as soon as the respective setup is executed. This results
|
||||||
|
in some duplicate logging.
|
||||||
|
@ -86,6 +86,15 @@ Basic Config Commands
|
|||||||
debugging. Note that the existing code logs its most important messages with
|
debugging. Note that the existing code logs its most important messages with
|
||||||
severity ``errors``.
|
severity ``errors``.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
FRRouting uses the ``writev()`` system call to write log messages. This
|
||||||
|
call is supposed to be atomic, but in reality this does not hold for
|
||||||
|
pipes or terminals, only regular files. This means that in rare cases,
|
||||||
|
concurrent log messages from distinct threads may get jumbled in
|
||||||
|
terminal output. Use a log file and ``tail -f`` if this rare chance is
|
||||||
|
inacceptable to your setup.
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: no log file [FILENAME [LEVEL]]
|
single: no log file [FILENAME [LEVEL]]
|
||||||
single: log file FILENAME [LEVEL]
|
single: log file FILENAME [LEVEL]
|
||||||
@ -104,14 +113,6 @@ Basic Config Commands
|
|||||||
deprecated ``log trap`` command) will be used. The ``no`` form of the command
|
deprecated ``log trap`` command) will be used. The ``no`` form of the command
|
||||||
disables logging to a file.
|
disables logging to a file.
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
If you do not configure any file logging, and a daemon crashes due to a
|
|
||||||
signal or an assertion failure, it will attempt to save the crash
|
|
||||||
information in a file named :file:`/var/tmp/frr.<daemon name>.crashlog`.
|
|
||||||
For security reasons, this will not happen if the file exists already, so
|
|
||||||
it is important to delete the file after reporting the crash information.
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: no log syslog [LEVEL]
|
single: no log syslog [LEVEL]
|
||||||
single: log syslog [LEVEL]
|
single: log syslog [LEVEL]
|
||||||
|
@ -6,6 +6,22 @@ Basic Setup
|
|||||||
After installing FRR, some basic configuration must be completed before it is
|
After installing FRR, some basic configuration must be completed before it is
|
||||||
ready to use.
|
ready to use.
|
||||||
|
|
||||||
|
Crash logs
|
||||||
|
----------
|
||||||
|
|
||||||
|
If any daemon should crash for some reason (segmentation fault, assertion
|
||||||
|
failure, etc.), it will attempt to write a backtrace to a file located in
|
||||||
|
:file:`/var/tmp/frr/<daemon>[-<instance>].<pid>/crashlog`. This feature is
|
||||||
|
not affected by any configuration options.
|
||||||
|
|
||||||
|
The crashlog file's directory also contains files corresponding to per-thread
|
||||||
|
message buffers in files named
|
||||||
|
:file:`/var/tmp/frr/<daemon>[-<instance>].<pid>/logbuf.<tid>`. In case of a
|
||||||
|
crash, these may contain unwritten buffered log messages. To show the contents
|
||||||
|
of these buffers, pipe their contents through ``tr '\0' '\n'``. A blank line
|
||||||
|
marks the end of valid unwritten data (it will generally be followed by
|
||||||
|
garbled, older log messages since the buffer is not cleared.)
|
||||||
|
|
||||||
Daemons Configuration File
|
Daemons Configuration File
|
||||||
--------------------------
|
--------------------------
|
||||||
After a fresh install, starting FRR will do nothing. This is because daemons
|
After a fresh install, starting FRR will do nothing. This is because daemons
|
||||||
|
Loading…
Reference in New Issue
Block a user