Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 85 additions & 5 deletions doc/appendices/command-line/traffic_ctl.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ Display the current value of a configuration record.

- **Monitor** a reload in real-time: ``traffic_ctl config reload -t <token> -m``
- **Query** the final status: ``traffic_ctl config status -t <token>``
- **Get detailed logs**: ``traffic_ctl config status -t <token> -l``
- **Get detailed logs**: ``traffic_ctl config status -t <token>``

The timestamp of the last reconfiguration event (in seconds since epoch) is published in the
``proxy.process.proxy.reconfigure_time`` metric.
Expand Down Expand Up @@ -659,7 +659,8 @@ Display the current value of a configuration record.
**Failed reload report:**

When a reload has failed handlers, the output shows which handlers succeeded and which failed,
along with durations for each:
along with durations and per-handler log entries. Log entries carry severity tags when a
severity level was recorded:

.. code-block:: bash

Expand All @@ -673,10 +674,17 @@ Display the current value of a configuration record.

Tasks:
✔ ip_allow.yaml ·························· 18ms
✗ logging.yaml ·························· 120ms ✗ FAIL
✗ ssl_client_coordinator ················· 85ms ✗ FAIL
├─ ✔ sni.yaml ··························· 20ms
└─ ✗ ssl_multicert.config ··············· 65ms ✗ FAIL
│ [Note] SSL configs reloaded
├─ ✔ SSLConfig ·························· 10ms
│ [Note] SSLConfig loading ...
│ [Note] SSLConfig reloaded
├─ ✗ SNIConfig ·························· 12ms ✗ FAIL
│ [Note] sni.yaml loading ...
│ [Err] sni.yaml failed to load: yaml-cpp error ...
└─ ✔ SSLCertificateConfig ·············· 13ms
[Note] (ssl) ssl_multicert.yaml loading ...
[Note] (ssl) ssl_multicert.yaml finished loading
...

Supports the following options:
Expand Down Expand Up @@ -704,6 +712,78 @@ Display the current value of a configuration record.
# Show last 5 reloads
$ traffic_ctl config status -c 5

.. option:: --min-level <level>

Filter task log entries by minimum severity level. Only entries at or above the specified
level are displayed. State-transition messages carry implicit severity:
``in_progress()`` and ``complete()`` produce ``[Note]`` entries, ``fail()`` produces
``[Err]`` entries. Entries without a severity (``DL_Undefined``) — typically those logged
via the one-argument ``ctx.log(text)`` — are always shown regardless of this filter.

Valid levels (case-insensitive): ``debug``, ``note``, ``warning``, ``error``.

.. code-block:: bash

# Show only warnings and errors
$ traffic_ctl config status -t my-token --min-level warning

# Show only errors
$ traffic_ctl config status -t my-token --min-level error

**Example — all logs (no filter):**

.. code-block:: text

✗ ssl_client_coordinator ······················· 2ms ✗ FAIL
│ [Note] SSL configs reloaded
├─ ✔ SSLConfig ································· 1ms
│ [Note] SSLConfig loading ...
│ [Note] SSLConfig reloaded
├─ ✗ SNIConfig ································· 1ms ✗ FAIL
│ [Note] sni.yaml loading ...
│ [Err] sni.yaml failed to load
└─ ✔ SSLCertificateConfig ······················ 0ms
[Note] (ssl) ssl_multicert.yaml loading ...
[Warn] Cannot open SSL certificate configuration "ssl_multicert.yaml" - No such file or directory
[Note] (ssl) ssl_multicert.yaml finished loading

**Example — --min-level warning (note and debug entries filtered out):**

.. code-block:: text

✗ ssl_client_coordinator ······················· 2ms ✗ FAIL
├─ ✗ SNIConfig ································· 1ms ✗ FAIL
│ [Err] sni.yaml failed to load
└─ ✔ SSLCertificateConfig ······················ 0ms
[Warn] Cannot open SSL certificate configuration "ssl_multicert.yaml" - No such file or directory

All entries from state transitions and ``CfgLoad*`` macros carry a severity tag
(e.g. ``[Dbg]``, ``[Note]``, ``[Warn]``, ``[Err]``). Entries without a tag are
"unleveled" (from the one-argument ``ctx.log(text)``) and always pass the filter.

.. tip::

For deeper investigation beyond what ``traffic_ctl config status`` shows, enable the
``config.reload`` debug tag. This writes a full dump of every subtask and its log entries
(with severity tags) to ``diags.log`` after each reload completes.
See :ref:`config-reload-diags-log` in the developer guide for details and examples.

Enable at runtime without restarting:

.. code-block:: bash

$ traffic_ctl server debug enable --tags "config.reload"

Or persistently in ``records.yaml``:

.. code-block:: yaml

records:
diags:
debug:
enabled: 1
tags: config.reload

**JSON output:**

All ``config status`` commands support the global ``--format json`` option to output the raw
Expand Down
229 changes: 227 additions & 2 deletions doc/developer-guide/config-reload-framework.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ Logging Best Practices
======================

- Use ``ctx.log()`` for operational messages that appear in
``traffic_ctl config status -l`` and :ref:`get_reload_config_status` responses.
``traffic_ctl config status`` and :ref:`get_reload_config_status` responses.
- Use ``ctx.fail(errata, summary)`` when you have a ``swoc::Errata`` with detailed error context.
- Use ``ctx.fail(reason)`` for simple error strings.
- Keep log messages concise — they are stored in memory and included in JSONRPC responses.
Expand All @@ -672,6 +672,228 @@ See the :ref:`get_reload_config_status` response examples for how log messages a
task tree output.


.. _config-reload-unified-macros:

Unified Diagnostic Macros (``CfgLoad*``)
=========================================

Config handlers often need the same message in two places: the ATS diagnostic log
(``diags.log`` / ``error.log``) **and** the reload task log (visible via
:option:`traffic_ctl config status`). The ``CfgLoad*`` macros in
``mgmt/config/ConfigContextDiags.h`` format the message once and dispatch to both destinations.

Include the header in any source file that uses these macros:

.. code-block:: cpp

#include "mgmt/config/ConfigContextDiags.h"

Quick Reference
---------------

.. list-table::
:header-rows: 1
:widths: 15 15 40

* - Want in diags?
- Want in task log?
- Use
* - Note
- yes + in_progress
- ``CfgLoadInProgress(ctx, ...)`` (subtasks)
* - Note
- yes + complete
- ``CfgLoadComplete(ctx, ...)``
* - Error
- yes + fail
- ``CfgLoadFail(ctx, ...)``
* - Error + Errata
- yes + fail
- ``CfgLoadFailWithErrata(ctx, errata, ...)``
* - Note / Warning
- yes (no state change)
- ``CfgLoadLog(ctx, DL_Note|DL_Warning, ...)``
* - Dbg (conditional on tag)
- yes
- ``CfgLoadDbg(ctx, ctl, ...)``
* - no
- yes
- ``ctx.log(...)``
* - no
- yes + state
- ``ctx.complete()`` / ``ctx.fail()``
* - yes
- no
- ``Note()`` / ``Warning()`` / ``Error()`` / ``Dbg()`` directly

Macro Details
-------------

``CfgLoadInProgress(ctx, fmt, ...)``
Emits a ``Note()`` to ``diags.log`` and calls ``ctx.in_progress(msg)``. The framework
sets ``IN_PROGRESS`` on handler tasks automatically, so this macro is primarily useful
for subtasks created via ``add_dependent_ctx()``:

.. code-block:: cpp

CfgLoadInProgress(ctx, "%s loading ...", filename);

``CfgLoadComplete(ctx, fmt, ...)``
Emits a ``Note()`` to ``diags.log`` and calls ``ctx.complete(msg)``. Use when a config
operation finishes successfully:

.. code-block:: cpp

CfgLoadComplete(ctx, "%s finished loading", filename);

``CfgLoadFail(ctx, fmt, ...)``
Emits an ``Error()`` to ``diags.log`` and the task log, then marks the task as FAIL.
Fail always implies ``DL_Error`` — if the condition is merely degraded (not fatal to
the load), use ``CfgLoadLog(ctx, DL_Warning, ...)`` + ``CfgLoadComplete()`` instead:

.. code-block:: cpp

CfgLoadFail(ctx, "%s failed to load", filename);

``CfgLoadFailWithErrata(ctx, errata, fmt, ...)``
Like ``CfgLoadFail`` but also appends ``swoc::Errata`` annotations to the task log.
Combines ``CfgLoadFail`` + ``ctx.fail(errata)`` in one call — see
:ref:`config-reload-errata-handling` below.

``CfgLoadLog(ctx, level, fmt, ...)``
Emits at the given ``DiagsLevel`` and calls ``ctx.log(level, msg)`` **without changing
task state**. Use for intermediate informational messages:

.. code-block:: cpp

CfgLoadLog(ctx, DL_Warning, "ControlMatcher - Cannot open config file: %s", path);
CfgLoadLog(ctx, DL_Note, "loaded %d categories from %s", count, filename);

``CfgLoadDbg(ctx, dbg_ctl, fmt, ...)``
Emits via ``Dbg()`` (conditional on the tag being enabled) and always adds to the task log
at ``DL_Debug``. Use for debug-level messages that should also appear in reload status:

.. code-block:: cpp

CfgLoadDbg(ctx, dbg_ctl_ssl, "Reload SNI file");

.. _config-reload-errata-handling:

Errata Handling
---------------

For failures with ``swoc::Errata`` detail, use ``CfgLoadFailWithErrata`` to combine
the diags summary, errata detail, and state change in a single call:

.. code-block:: cpp

CfgLoadFailWithErrata(ctx, errata, "%s failed to load", filename);

This logs the formatted message to ``diags.log`` at ``DL_Error``, appends it to
the task log, then calls ``ctx.fail(errata)`` which stores each errata annotation
(with its own severity) in the task log and marks the task as FAIL.

For errors that should not change state, pair ``CfgLoadLog`` with ``ctx.log(errata)``:

.. code-block:: cpp

CfgLoadLog(ctx, DL_Error, "Cannot open %s", path);
ctx.log(errata); // errata detail -> task log only

When NOT to Use Macros
-----------------------

- **Task-log-only messages** — use ``ctx.log()`` directly when the message is only useful in
``traffic_ctl`` output and should not appear in ``diags.log``.
- **State-only transitions** — use ``ctx.in_progress()`` / ``ctx.complete()`` / ``ctx.fail()``
directly when there is no message to emit to ``diags.log``.
- **Fatal errors** — ``Fatal()`` terminates the process; reload status is irrelevant.
Call ``Fatal()`` directly.


Severity-Aware Task Logs
=========================

Each task log entry carries a ``DiagsLevel`` severity. State-transition methods carry implicit
severity: ``in_progress(text)`` and ``complete(text)`` store ``DL_Note``, ``fail(text)`` stores
``DL_Error``. The ``CfgLoad*`` macros and ``ctx.log(level, text)`` store the caller-specified
level. Only the one-argument ``ctx.log(text)`` (no level) stores ``DL_Undefined`` — these
entries are always shown regardless of ``--min-level`` filtering.

In :option:`traffic_ctl config status` output, entries with a defined severity are prefixed
with a tag:

.. code-block:: text

✗ ssl_client_coordinator ······················· 2ms ✗ FAIL
│ [Note] SSL configs reloaded
├─ ✔ SSLConfig ································· 1ms
│ [Note] SSLConfig loading ...
│ [Note] SSLConfig reloaded
├─ ✗ SNIConfig ································· 1ms ✗ FAIL
│ [Note] sni.yaml loading ...
│ [Err] sni.yaml failed to load
└─ ✔ SSLCertificateConfig ······················ 0ms
[Note] (ssl) ssl_multicert.yaml loading ...
[Warn] Cannot open SSL certificate configuration "ssl_multicert.yaml" - No such file or directory
[Note] (ssl) ssl_multicert.yaml finished loading

The ``--min-level`` option on :option:`traffic_ctl config status` filters log entries
by severity — see :option:`traffic_ctl config status` for details.

The severity is also available in JSON output (``--format json``) as an integer ``level``
field on each log entry, where the value maps to the ``DiagsLevel`` enum (e.g. ``1`` = Debug,
``3`` = Note, ``4`` = Warning, ``5`` = Error).


.. _config-reload-diags-log:

Reload Summary in ``diags.log``
================================

After a reload reaches a terminal state (confirmed after a 5-second grace period), a summary
line is logged to ``diags.log``:

**Success:**

.. code-block:: text

NOTE: Config reload [my-token] completed successfully: 3 tasks succeeded (3 total)

**Failure:**

.. code-block:: text

WARNING: Config reload [my-token] finished with failures: 1 succeeded, 1 failed (3 total) — run: traffic_ctl config status -t my-token

When the ``config.reload`` debug tag is enabled, a detailed dump of all subtasks and their
log entries is written to ``traffic.out`` / ``diags.log``:

.. code-block:: text

DIAG: (config.reload) [fail] ssl_client_coordinator
DIAG: (config.reload) [Note] SSL configs reloaded
DIAG: (config.reload) [success] SSLConfig
DIAG: (config.reload) [Note] SSLConfig loading ...
DIAG: (config.reload) [Note] SSLConfig reloaded
DIAG: (config.reload) [fail] SNIConfig
DIAG: (config.reload) [Note] sni.yaml loading ...
DIAG: (config.reload) [Err] sni.yaml failed to load
DIAG: (config.reload) [success] ssl_ticket_key
DIAG: (config.reload) [Note] SSL ticket key loading ...
DIAG: (config.reload) [Note] SSL ticket key reloaded

Enable this tag for troubleshooting:

.. code-block:: yaml

records:
diags:
debug:
enabled: 1
tags: config.reload


Testing
========

Expand All @@ -683,7 +905,10 @@ After registering a new handler:
3. Run :option:`traffic_ctl config status` to verify the handler appears in the task tree with
the correct status.
4. Introduce a parse error in the config file and reload — verify the handler reports ``FAIL``.
5. Use :option:`traffic_ctl config status` ``--format json`` to inspect the raw
5. Check that severity tags (``[Dbg]``, ``[Err]``, etc.) appear correctly in
:option:`traffic_ctl config status` output and that ``--min-level`` filtering works.
6. Enable the ``config.reload`` debug tag and verify the detailed dump appears in ``diags.log``.
7. Use :option:`traffic_ctl config status` ``--format json`` to inspect the raw
:ref:`get_reload_config_status` response for automation testing.

**Autests** — the project includes autest helpers for config reload testing. Use
Expand Down
5 changes: 3 additions & 2 deletions include/iocore/net/SSLSNIConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "iocore/eventsystem/ConfigProcessor.h"
#include "iocore/net/SNIActionItem.h"
#include "iocore/net/YamlSNIConfig.h"
#include "mgmt/config/ConfigContext.h"

#include <functional>

Expand Down Expand Up @@ -90,8 +91,8 @@ class SNIConfigParams : public ConfigInfo
~SNIConfigParams() override;

const NextHopProperty *get_property_config(const std::string &servername) const;
bool initialize();
bool initialize(const std::string &sni_filename);
bool initialize(ConfigContext ctx = {});
bool initialize(const std::string &sni_filename, ConfigContext ctx = {});
/** Walk sni.yaml config and populate sni_action_list
@return 0 for success, 1 is failure
*/
Expand Down
Loading