This example demonstrates how to create and use dynamically loaded collector plugins.
The monitoring system supports loading collector plugins from shared libraries at runtime. This allows:
- Third-party plugins without recompilation
- Plugin distribution as separate libraries
- On-demand plugin loading
- Plugin updates without rebuilding the main application
example_plugin.cpp- Sample plugin implementationplugin_loader_example.cpp- Example program that loads and uses the pluginCMakeLists.txt- Build configuration for the plugin
cd examples/plugin_example
mkdir build
cd build
cmake ..
makeThis creates:
- Linux:
libexample_plugin.so - macOS:
libexample_plugin.dylib - Windows:
example_plugin.dll
From the project root:
mkdir build
cd build
cmake ..
make# Linux
./plugin_loader_example ./libexample_plugin.so
# macOS
./plugin_loader_example ./libexample_plugin.dylib
# Windows
plugin_loader_example.exe example_plugin.dll=== Dynamic Plugin Loading Example ===
Loading plugin from: ./libexample_plugin.so
Plugin loaded successfully
Plugin Metadata:
Name: example_plugin
Description: Example dynamically loaded collector plugin
Version: 1.0.0
Available: yes
Initializing plugin...
Plugin initialized
Collecting metrics (5 iterations)...
Iteration 1:
example.cpu_usage: 45.23 % [plugin=example, type=cpu]
example.memory_usage: 512.67 MB [plugin=example, type=memory]
example.request_count: 1 requests [plugin=example, type=counter]
Iteration 2:
example.cpu_usage: 38.91 % [plugin=example, type=cpu]
example.memory_usage: 487.32 MB [plugin=example, type=memory]
example.request_count: 2 requests [plugin=example, type=counter]
...
Shutting down plugin...
Unloading plugin...
Plugin unloaded successfully
=== Example Complete ===
#include "kcenon/monitoring/plugins/collector_plugin.h"
#include "kcenon/monitoring/plugins/plugin_api.h"
class my_plugin : public kcenon::monitoring::collector_plugin {
public:
auto name() const -> std::string_view override {
return "my_plugin";
}
auto initialize(const config_map& config) -> bool override {
// Initialize your plugin
return true;
}
auto shutdown() -> void override {
// Clean up resources
}
auto collect() -> std::vector<metric_data> override {
// Collect and return metrics
std::vector<metric_data> metrics;
// ... add metrics ...
return metrics;
}
auto is_available() const -> bool override {
// Check if plugin is available on this platform
return true;
}
auto get_metadata() const -> plugin_metadata_t override {
return plugin_metadata_t{
plugin_category::custom,
plugin_type::collector,
"My Plugin",
"1.0.0",
"Description"
};
}
};Use the IMPLEMENT_PLUGIN macro at the end of your source file:
IMPLEMENT_PLUGIN(
my_plugin, // Plugin class name
"my_plugin", // Plugin name (must match name() method)
"1.0.0", // Version
"My custom plugin", // Description
"Your Name", // Author
"custom" // Category
)Create a CMakeLists.txt:
cmake_minimum_required(VERSION 3.20)
project(my_plugin VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(my_plugin SHARED my_plugin.cpp)
target_include_directories(my_plugin PRIVATE
${MONITORING_SYSTEM_INCLUDE_DIR}
)
set_target_properties(my_plugin PROPERTIES
POSITION_INDEPENDENT_CODE ON
)Build:
mkdir build
cd build
cmake ..
make#include "kcenon/monitoring/plugins/collector_registry.h"
auto& registry = kcenon::monitoring::collector_registry::instance();
// Load plugin
if (!registry.load_plugin("/path/to/libmy_plugin.so")) {
std::cerr << "Load failed: " << registry.get_plugin_loader_error() << "\n";
return;
}
// Get and use plugin
auto* plugin = registry.get_plugin("my_plugin");
if (plugin) {
plugin->initialize({});
auto metrics = plugin->collect();
// ... use metrics ...
}
// Unload when done
registry.unload_plugin("my_plugin");Plugins must be compiled with the same PLUGIN_API_VERSION as the monitoring system. If the versions don't match, the plugin will fail to load with an error message:
Incompatible API version: plugin=2, expected=1
To check the API version:
#include "kcenon/monitoring/plugins/plugin_api.h"
std::cout << "API Version: " << PLUGIN_API_VERSION << "\n";- Use
.soextension - Compile with
-fPICflag - Link with
-ldlfor dynamic loading
- Use
.dylibextension - Compile with
-fPICflag - May need to set
DYLD_LIBRARY_PATHenvironment variable
- Use
.dllextension - Compile with
/DEXPORT_PLUGINor setWINDOWS_EXPORT_ALL_SYMBOLS - Put DLL in same directory as executable or in PATH
Check for errors after loading:
if (!registry.load_plugin(path)) {
std::string error = registry.get_plugin_loader_error();
// Handle error...
}Common errors:
file_not_found- Plugin file doesn't existlibrary_load_failed- Failed to load shared library (missing dependencies)symbol_not_found- Required export function not foundincompatible_api_version- Plugin compiled with different API versionplugin_unavailable- Plugin'sis_available()returned falsealready_loaded- Plugin with same name already loaded
- Validate plugin paths before loading
- Only load plugins from trusted sources
- Check API version compatibility
- Consider implementing plugin signing/verification
- Be aware that plugins run with full application permissions
- Plugin loading incurs one-time cost for library loading
- Subsequent metric collection has same performance as built-in collectors
- Unloading plugins releases all associated resources