Project: Envoy (Orchestration & Management / Service Proxy)
File: fine_grain_log.md

Fine-Grain Logger is a logger with finer grained log level control and runtime logger update using administration interface. Compared to the existing logger in Envoy, Fine-Grain Logger provides file level control which can be easily extended to finer grained logger with function or line level control, and it is completely automatic and never requires developers to explicitly specify the logging component. Besides, it has a comparable speed as Envoy's logger. The basic usage of Fine-Grain Logger is to explicitly call its macros: ``` FINEGRAINLOG(info, "Hello world! Here's a line of fine-grain log!"); FINEGRAINLOG(error, "FineGrainLog Error! Here's the second message!"); ``` If the level of log message is higher than that of the file, macros above will print messages with the file name like this: ``` FineGrainLog Error! Here\'s the second message! ``` More macros with connection and stream information: ``` NiceMock<Network::MockConnection> connection_; NiceMock<Http::MockStreamDecoderFilterCallbacks> stream_; FINEGRAINCONNLOG(warn, "Fake info {} of connection", connection, 1); FINEGRAINSTREAMLOG(warn, "Fake warning {} of stream", stream, 1); ``` To flush a logger, `FINEGRAINFLUSH_LOG()` can be used. A command line option is provided to enable Fine-Grain Logger: `--enable-fine-grain-logging`. It enables Fine-Grain Logger for Envoy, i.e. replaces most Envoy's log macros (`ENVOYLOG, ENVOYFLUSHLOG, ENVOYCONNLOG, ENVOYSTREAM_LOG`) with corresponding Fine-Grain Logger's macros. If Fine-Grain Logger is enabled, the default log format is `"[%l] [%g:%#] %v"`, where the logger name is omitted compared to Envoy's default as it's the same as file name. The default log level is info, if not specified by user of any logging context. Note that Envoy's logger can still be used in Fine-Grain mode. These macros are not replaced: `GETMISCLOGGER, ENVOYLOGMISC, ENVOYLOGGER, ENVOYLOGTOLOGGER, ENVOYCONNLOGTOLOGGER, ENVOYSTREAMLOGTOLOGGER`. For example, `ENVOYLOGLOGGER(ENVOYLOGGER(), LEVEL, ...)` is equivalent to `ENVOYLOG` in Envoy mode. If Fine-Grain Logger is not enabled, existing Envoy's logger is used. In this mode, basic macros like `FINEGRAINLOG` can be used but the main part of `ENVOY_LOG` will keep the same. One limitation is that logger update in admin page is not supported by default as it detects Envoy mode. The reason is: Envoy mode is designed only to be back compatible. To address it, developers can use `Logger::Context::enableFineGrainLogger()` to manually enable Fine-Grain Logger. Runtime update of Fine-Grain Logger is supported with administration interface, i.e. admin page, and Fine-Grain mode needs to be enabled to use it. Same as Envoy's logger, the following functionalities are provided: `POST /logging`: List all names (i.e. file paths) of all active loggers and their levels; `POST /logging?<file_path>=<level>`: Given the current file path, change the log level of the file; `POST /logging?paths=filepath1:level1,filepath2:level2...`: Change log level of multiple file paths at once; `POST /logging?level=<level>`: Change levels of all

Users can view and change the log level by file granularity at runtime through the admin page. Envoy admin `/logging` supports the file basename, glob ``*`` and ``?`` match for the fine-grain loggers update as well. For example, the following loggers are active in Envoy right now, and 0 means trace level. ``` source/server/admin/admin_filter.cc: 0 source/common/event/dispatcher_impl.cc: 0 source/common/network/tcplistenerimpl.cc: 0 source/common/network/udplistenerimpl.cc: 0 ``` ``POST /logging?paths=source/common/event/dispatcherimpl.cc:debug`` will make the level of ``source/common/event/dispatcherimpl.cc`` be debug. ``POST /logging?adminfilter=info`` will make the level of ``source/server/admin/adminfilter.cc`` be info, and other unmatched loggers will be the default trace. ``POST /logging?paths=source/common*:warning`` will make the level of ``source/common/event/dispatcherimpl.cc:``, ``source/common/network/tcplistener_impl.cc`` be warning. Other unmatched loggers will be the default trace, e.g., `admin_filter.cc`, even it was updated to info from the previous post update. ``POST /logging?paths=???listenerimpl:info`` will make the level of ``source/common/network/tcplistenerimpl.cc``, ``source/common/network/udplistenerimpl.cc`` be info. ``POST /logging?paths=???listenerimpl:info,tcplistenerimpl:warning``, the level of ``source/common/network/tcplistenerimpl.cc`` will be info, since the first match will take effect. ``POST /logging?level=info`` will change the default verbosity level to info. All the unmatched loggers in the following update will be this default level. Fine-Grain Logger can be divided into two parts: Core part: file level logger without explicit inheriting `Envoy::Logger::Loggable`; Hook part: control interfaces, such as command line and admin page. The core of Fine-Grain Logger is implemented in `class FineGrainLogContext`, which is a singleton class. Filenames (i.e. keys) and logger pointers are stored in `FineGrainLogContext::finegrainlogmap`. There are several code paths when `FINEGRAINLOG` is called. Slow path: if `FINEGRAINLOG` is first called in a file, `FineGrainLogContext::initFineGrainLogger(key, localloggerptr)` is called to create a new logger globally and store the `<key, globalloggerptr>` pair in `finegrainlogmap`. Then local logger pointer is updated and will never be changed. Medium path: if `FINEGRAINLOG` is first called at the call site but not in the file, `FineGrainLogContext::initFineGrainLogger(key, localloggerptr)` is still called but local logger pointer is quickly set to the global logger, as there is already global record of the given filename. Fast path: if `FINEGRAINLOG` is called after first call at the site, the log is directly printed using local logger pointer. Fine-Grain Logger provides control interfaces through command line and admin page. To pass the arguments such as log format and default log level to Fine-Grain Logger, `finegrainlogformat` and `finegraindefaultlevel` are added in `class Context` and they are updated when a new logging context is activated. `getFineGrainLogContext().setDefaultFineGrainLogLevelFormat(level, format)` is called in `Context::activate()` to set log format and update loggers' previous default level to the new level. To support the runtime update in admin page, log handler in admin page uses `getFineGrainLogContext().listFineGrainLoggers()` to show all Fine-Grain Loggers, `getFineGrainLogContext().setFineGrainLogger(name, level)` to set a specific logger, `getFineGrainLogContext().setAllFineGrainLoggers(level)` to set all loggers, and `getFineGrainLogContext().updateVerbositySetting(glob_levels)` to set glob matched loggers.