Enable bmcweb dynamic logging
Create a CLI app called "bmcweb" that can set logging levels on
"bmcwebd", the new bmcweb daemon. Create a dbus connection to set log
level using the CLI app Define the "setLogLevel" method on dbus to
control logging level in bmcwebd Add logic to move logging level from
build option to dynamic overloading
Reason: bmcweb picks up logging level as a compile flag. We want it to
be more flexible to debug errors in the field. Using the bmcweb CLI
app, we can set log levels on the bmcweb daemon during runtime.
Splitting bmcweb.
For example, to set logging level to INFO on the target:
bmcweb -l INFO
Change-Id: I7192e4d0ac7aa3a91babecc473521be27ea8acd1
Signed-off-by: Aushim Nagarkatti <anagarkatti@nvidia.com>
diff --git a/DEVELOPING.md b/DEVELOPING.md
index a2dc3c9..bf5c42f 100644
--- a/DEVELOPING.md
+++ b/DEVELOPING.md
@@ -246,3 +246,11 @@
```bash
EXTRA_OEMESON:pn-bmcweb:append = "-Dbmcweb-logging='debug'"
```
+
+bmcweb also supports setting logging level at runtime. The bmcweb CLI
+application can be used to set the daemon's logging level on target. For
+example, to set logging level to INFO:
+
+```bash
+bmcweb -l INFO
+```
diff --git a/TESTING.md b/TESTING.md
index 5052d7b..2847214 100644
--- a/TESTING.md
+++ b/TESTING.md
@@ -29,17 +29,17 @@
- Reduce binary size by stripping it when ready for testing
```sh
- arm-openbmc-linux-gnueabi-strip bmcweb
+ arm-openbmc-linux-gnueabi-strip bmcwebd
```
**Note:** Stripping is not required and having the debug symbols could be
useful depending on your testing. Leaving them will drastically increase your
transfer time to the BMC.
-- Copy your bmcweb you want to test to /tmp/ in QEMU
+- Copy your bmcweb daemon you want to test to /tmp/ in QEMU
```sh
- scp -P 2222 bmcweb root@127.0.0.1:/tmp/
+ scp -P 2222 bmcwebd root@127.0.0.1:/tmp/
```
**Special Notes:** The address and port shown here (127.0.0.1 and 2222)
@@ -52,11 +52,11 @@
systemctl stop bmcweb
```
- **Note:** bmcweb supports being started directly in parallel with the bmcweb
- running as a service. The standalone bmcweb will be available on port 18080.
- An advantage of this is you can compare between the two easily for testing. In
- QEMU you would need to open up port 18080 when starting QEMU. Your curl
- commands would need to use 18080 to communicate.
+ **Note:** bmcweb daemon supports being started directly in parallel with the
+ bmcweb running as a service. The standalone bmcweb daemon will be available on
+ port 18080. An advantage of this is you can compare between the two easily for
+ testing. In QEMU you would need to open up port 18080 when starting QEMU. Your
+ curl commands would need to use 18080 to communicate.
- If running within a system that has read-only /usr/ filesystem, issue the
following commands one time per QEMU boot to make the filesystem writable
@@ -67,16 +67,16 @@
mount -t overlay -o lowerdir=/usr,upperdir=/var/persist/usr,workdir=/var/persist/work/usr overlay /usr
```
-- Remove the existing bmcweb from the filesystem in QEMU
+- Remove the existing bmcweb daemon from the filesystem in QEMU
```sh
- rm /usr/bin/bmcweb
+ rm /usr/libexec/bmcwebd
```
-- Link to your new bmcweb in /tmp/
+- Link to your new bmcweb daemon in /tmp/
```sh
- ln -sf /tmp/bmcweb /usr/bin/bmcweb
+ ln -sf /tmp/bmcwebd /usr/libexec/bmcwebd
```
- Test your changes. bmcweb will be started automatically upon your first REST
diff --git a/config/bmcweb.service.in b/config/bmcweb.service.in
index a0b6777..040e519 100644
--- a/config/bmcweb.service.in
+++ b/config/bmcweb.service.in
@@ -1,12 +1,12 @@
[Unit]
-Description=Start bmcweb server
+Description=Start bmcwebd server
Wants=network.target
After=network.target
[Service]
ExecReload=kill -s HUP $MAINPID
-ExecStart=@MESON_INSTALL_PREFIX@/bin/bmcweb
+ExecStart=@MESON_INSTALL_PREFIX@/libexec/bmcwebd
Type=simple
WorkingDirectory=/home/root
diff --git a/config/bmcweb_config.h.in b/config/bmcweb_config.h.in
index f5a1213..31af658 100644
--- a/config/bmcweb_config.h.in
+++ b/config/bmcweb_config.h.in
@@ -9,4 +9,4 @@
@BMCWEB_OPTIONS@
// NOLINTEND(readability-identifier-naming)
-// clang-format on
\ No newline at end of file
+// clang-format on
diff --git a/http/logging.hpp b/http/logging.hpp
index e2b9fef..526d186 100644
--- a/http/logging.hpp
+++ b/http/logging.hpp
@@ -53,8 +53,24 @@
}
// configured bmcweb LogLevel
-constexpr crow::LogLevel bmcwebCurrentLoggingLevel =
- getLogLevelFromName(BMCWEB_LOGGING_LEVEL);
+inline crow::LogLevel& getBmcwebCurrentLoggingLevel()
+{
+ static crow::LogLevel level = getLogLevelFromName(BMCWEB_LOGGING_LEVEL);
+ return level;
+}
+
+struct FormatString
+{
+ std::string_view str;
+ std::source_location loc;
+
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ FormatString(const char* stringIn, const std::source_location& locIn =
+ std::source_location::current()) :
+ str(stringIn),
+ loc(locIn)
+ {}
+};
template <typename T>
const void* logPtr(T p)
@@ -68,7 +84,7 @@
inline void vlog(std::format_string<Args...>&& format, Args&&... args,
const std::source_location& loc) noexcept
{
- if constexpr (bmcwebCurrentLoggingLevel < level)
+ if (getBmcwebCurrentLoggingLevel() < level)
{
return;
}
diff --git a/meson.build b/meson.build
index 065be7f..2eec10b 100644
--- a/meson.build
+++ b/meson.build
@@ -56,6 +56,10 @@
# Include Directories
incdir = include_directories('include', 'redfish-core/include', 'redfish-core/lib', 'http')
+incdir_cli = include_directories(
+ 'http',
+ 'include'
+)
if (get_option('tests').allowed())
summary('unittest', 'NA', section: 'Features')
@@ -184,6 +188,7 @@
# Find the dependency modules, if not found use meson wrap to get them
# automatically during the configure step
bmcweb_dependencies = []
+bmcweb_cli_dependencies = []
pam = cxx.find_library('pam', required: true)
atomic = cxx.find_library('atomic', required: true)
@@ -223,6 +228,16 @@
sdbusplus = sdbusplus.as_system('system')
endif
bmcweb_dependencies += sdbusplus
+bmcweb_cli_dependencies += sdbusplus
+
+cli11 = dependency('CLI11', required : false, include_type: 'system')
+if not cli11.found()
+ cli11_proj = subproject('cli11', required: true)
+ cli11 = cli11_proj.get_variable('CLI11_dep')
+ cli11 = cli11.as_system('system')
+endif
+bmcweb_cli_dependencies += cli11
+
tinyxml = dependency(
'tinyxml2',
@@ -256,7 +271,8 @@
include_type: 'system',
)
if boost.found()
- bmcweb_dependencies += [boost]
+ bmcweb_dependencies += [boost]
+ bmcweb_cli_dependencies += [boost]
else
cmake = import('cmake')
opt = cmake.subproject_options()
@@ -285,6 +301,7 @@
foreach boost_lib : boost_libs
boost_lib_instance = boost.dependency('boost_' + boost_lib).as_system()
bmcweb_dependencies += [boost_lib_instance]
+ bmcweb_cli_dependencies += [boost_lib_instance]
endforeach
endif
@@ -308,7 +325,8 @@
systemd_system_unit_dir = systemd.get_variable('systemdsystemunitdir')
-bindir = get_option('prefix') + '/' + get_option('bindir')
+bindir = get_option('prefix') + '/' +get_option('bindir')
+libexec = get_option('prefix') + '/' + get_option('libexecdir')
summary(
{
@@ -325,6 +343,7 @@
# Config subdirectory
subdir('config')
bmcweb_dependencies += conf_h_dep
+bmcweb_cli_dependencies += conf_h_dep
# Source files
fs = import('fs')
@@ -356,16 +375,26 @@
dependencies: bmcweb_dependencies,
)
-# Generate the bmcweb executable
+# Generate the bmcwebd daemon
executable(
- 'bmcweb',
- 'src/webserver_main.cpp',
- include_directories: incdir,
- dependencies: bmcweb_dependencies,
- link_with: bmcweblib,
- link_args: '-Wl,--gc-sections',
- install: true,
- install_dir: bindir,
+ 'bmcwebd',
+ 'src/webserver_main.cpp',
+ include_directories : incdir,
+ dependencies: bmcweb_dependencies,
+ link_with: bmcweblib,
+ link_args: '-Wl,--gc-sections',
+ install: true,
+ install_dir:libexec
+)
+
+# Generate the bmcweb CLI application
+executable(
+ 'bmcweb',
+ ['src/webserver_cli.cpp','src/boost_asio.cpp'],
+ include_directories : incdir_cli,
+ dependencies: bmcweb_cli_dependencies,
+ install: true,
+ install_dir:bindir
)
srcfiles_unittest = files(
diff --git a/src/webserver_cli.cpp b/src/webserver_cli.cpp
new file mode 100644
index 0000000..e17d800
--- /dev/null
+++ b/src/webserver_cli.cpp
@@ -0,0 +1,56 @@
+#include "boost_formatters.hpp"
+#include "logging.hpp"
+
+#include <CLI/CLI.hpp>
+#include <boost/asio/io_context.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/bus.hpp>
+
+#include <string>
+
+// Override default log option:
+static void cliLogLevel(const std::string& logLevel)
+{
+ crow::getBmcwebCurrentLoggingLevel() = crow::getLogLevelFromName(logLevel);
+}
+
+int main(int argc, char** argv) noexcept(false)
+{
+ CLI::App app("BMCWeb SetLogLevel CLI");
+
+ cliLogLevel("INFO");
+
+ // Define sdbus interfaces:
+ std::string service = "xyz.openbmc_project.bmcweb";
+ std::string path = "/xyz/openbmc_project/bmcweb";
+ std::string iface = "xyz.openbmc_project.bmcweb";
+ std::string method = "SetLogLevel";
+
+ std::string loglevel;
+ app.add_option("-l,--loglevel", loglevel, "Set bmcweb log level");
+
+ CLI11_PARSE(app, argc, argv)
+
+ BMCWEB_LOG_INFO("Working on log-level: {}", loglevel);
+
+ // Set up dbus connection:
+ boost::asio::io_context io;
+ auto conn = std::make_shared<sdbusplus::asio::connection>(io);
+
+ // Attempt to async_call to set logging level
+ conn->async_method_call(
+ [&io](boost::system::error_code& ec) mutable {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR("SetLogLevel returned error with {}", ec);
+ return;
+ }
+ BMCWEB_LOG_INFO("Successfully changed log-level ");
+ io.stop();
+ },
+ service, path, iface, method, loglevel);
+
+ io.run();
+
+ return 0;
+}
diff --git a/src/webserver_run.cpp b/src/webserver_run.cpp
index 5013669..9079ad6 100644
--- a/src/webserver_run.cpp
+++ b/src/webserver_run.cpp
@@ -23,16 +23,41 @@
#include <boost/asio/io_context.hpp>
#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
#include <memory>
+static void setLogLevel(const std::string& logLevel)
+{
+ const std::basic_string_view<char>* iter =
+ std::ranges::find(crow::mapLogLevelFromName, logLevel);
+ if (iter == crow::mapLogLevelFromName.end())
+ {
+ BMCWEB_LOG_ERROR("log-level {} not found", logLevel);
+ return;
+ }
+ crow::getBmcwebCurrentLoggingLevel() = crow::getLogLevelFromName(logLevel);
+ BMCWEB_LOG_INFO("Requested log-level change to: {}", logLevel);
+}
+
int run()
{
auto io = std::make_shared<boost::asio::io_context>();
App app(io);
- sdbusplus::asio::connection systemBus(*io);
- crow::connections::systemBus = &systemBus;
+ std::shared_ptr<sdbusplus::asio::connection> systemBus =
+ std::make_shared<sdbusplus::asio::connection>(*io);
+ crow::connections::systemBus = systemBus.get();
+
+ auto server = sdbusplus::asio::object_server(systemBus);
+
+ std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
+ server.add_interface("/xyz/openbmc_project/bmcweb",
+ "xyz.openbmc_project.bmcweb");
+
+ iface->register_method("SetLogLevel", setLogLevel);
+
+ iface->initialize();
// Static assets need to be initialized before Authorization, because auth
// needs to build the whitelist from the static routes
@@ -106,6 +131,9 @@
bmcweb::registerUserRemovedSignal();
app.run();
+
+ systemBus->request_name("xyz.openbmc_project.bmcweb");
+
io->run();
crow::connections::systemBus = nullptr;
diff --git a/subprojects/cli11.wrap b/subprojects/cli11.wrap
new file mode 100644
index 0000000..dd26d59
--- /dev/null
+++ b/subprojects/cli11.wrap
@@ -0,0 +1,10 @@
+[wrap-file]
+directory = CLI11-2.4.1
+source_url = https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.4.1.tar.gz
+source_filename = CLI11-2.4.1.tar.gz
+source_hash = 73b7ec52261ce8fe980a29df6b4ceb66243bb0b779451dbd3d014cfec9fdbb58
+source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/cli11_2.4.1-1/CLI11-2.4.1.tar.gz
+wrapdb_version = 2.4.1-1
+
+[provide]
+cli11 = CLI11_dep