-
Notifications
You must be signed in to change notification settings - Fork 3
Examples DynamicControl
Eduard Mishkurov edited this page Jun 3, 2026
·
7 revisions
This example shows how to enable runtime control of Logme and change settings from another terminal.
This example shows how to enable runtime control of Logme and change settings from another terminal.
- Starts the Logme control server (TCP, no SSL).
- Spawns two worker threads (
t1,t2). - Each thread logs:
- to the default channel,
- to its own channel (
t1/t2), - and then to the same channel with two different subsystem IDs.
The example prints the control server address and a list of useful commands on startup.
Enable the example and the tool:
cmake -S . -B build -G Ninja \
-DLOGME_BUILD_EXAMPLES=ON \
-DLOGME_BUILD_TOOLS=ON
cmake --build build -jTerminal 1:
./DynamicControlTerminal 2 (replace the port if you changed it):
./logmectl -p 7791 list
./logmectl -p 7791 channel disable t1
./logmectl -p 7791 channel enable t1
./logmectl -p 7791 channel level t2 error
./logmectl -p 7791 subsystem list
./logmectl -p 7791 subsystem mode blacklist
./logmectl -p 7791 subsystem add wrk1-
subsystem mode blacklistblocks subsystems present in the list. -
subsystem mode whitelistallows only subsystems present in the list.
Dynamic control demo
This example starts Logme control server and prints from two worker threads.
Use 'logmectl' (built with -DLOGME_BUILD_TOOLS=ON) from another console to change settings.
Control server: 127.0.0.1:7791
Useful commands:
logmectl -p 7791 backend --channel ch1 --add console
logmectl -p 7791 subsystem --unblock-reported
logmectl -p 7791 subsystem --report s1
logmectl -p 7791 channel --disable ch1
logmectl -p 7791 backend --channel ch2 --add console
logmectl -p 7791 channel --enable ch1
Press ENTER to stop.
2026-06-03 16:33:09:133 Worker::Run(): t1 started
2026-06-03 16:33:09:133 Worker::Run(): t1 stopped
2026-06-03 16:33:09:133 Worker::Run(): t2 started
2026-06-03 16:33:12:145 Worker::Run(): t2 stopped
Repository folder: examples/DynamicControl
Files:
#ifdef _WIN32
#include <winsock2.h>
#endif
#include "Worker.h"
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <Logme/Logger.h>
#include <Logme/Logme.h>
#include <Logme/Ssl.h>
static void PrintInstructions(
int port
, bool ssl
, const std::string& pass
)
{
const char* sslArg = ssl ? " --ssl" : "";
std::string passArg;
if (!pass.empty())
passArg = " --pass " + pass;
std::cout
<< "Dynamic control demo\n"
<< "\n"
<< "This example starts Logme control server and prints from two worker threads.\n"
<< "Use 'logmectl' (built with -DLOGME_BUILD_TOOLS=ON) from another console to change settings.\n"
<< "\n"
<< "Control server: 127.0.0.1:" << port << "\n"
<< "\n"
<< "Useful commands:\n"
<< " logmectl" << sslArg << passArg << " -p " << port << " backend --channel ch1 --add console\n"
<< " logmectl" << sslArg << passArg << " -p " << port << " subsystem --unblock-reported\n"
<< " logmectl" << sslArg << passArg << " -p " << port << " subsystem --report s1\n"
<< " logmectl" << sslArg << passArg << " -p " << port << " channel --disable ch1\n"
<< " logmectl" << sslArg << passArg << " -p " << port << " backend --channel ch2 --add console\n"
<< " logmectl" << sslArg << passArg << " -p " << port << " channel --enable ch1\n"
<< "\n";
if (ssl)
{
std::cout << "Note: TLS is enabled. Use logmectl --ssl when connecting." << std::endl;
}
std::cout
<< "Press ENTER to stop.\n"
<< std::endl;
}
static bool ParseArgs(
int argc
, char** argv
, bool& ssl
, std::string& certFile
, std::string& keyFile
, std::string& pass
)
{
ssl = false;
pass.clear();
for (int i = 1; i < argc; ++i)
{
const std::string a = argv[i];
if (a == "--ssl")
{
ssl = true;
continue;
}
if (a == "--cert" && i + 1 < argc)
{
certFile = argv[++i];
continue;
}
if (a == "--key" && i + 1 < argc)
{
keyFile = argv[++i];
continue;
}
if (a == "--pass" && i + 1 < argc)
{
pass = argv[++i];
continue;
}
if (a == "--help" || a == "help")
return false;
}
return true;
}
static void PrintUsage(const char* exe)
{
std::cout
<< "Usage:\n"
<< " " << exe << " [--ssl [--cert file] [--key file]] [--pass password]\n"
<< std::endl;
}
int main(int argc, char* argv[])
{
#ifdef _WIN32
WSADATA wsa = {0};
int rc = WSAStartup(MAKEWORD(2, 2), &wsa);
if (rc != 0)
{
std::cerr << "WSAStartup failed: " << rc << std::endl;
return 1;
}
#endif
bool ssl = false;
std::string certFile;
std::string keyFile;
std::string pass;
if (!ParseArgs(argc, argv, ssl, certFile, keyFile, pass))
{
PrintUsage(argv[0]);
return 1;
}
const int port = 7791;
Logme::ControlConfig cfg{};
cfg.Enable = true;
cfg.Port = port;
cfg.Interface = 0;
cfg.Password = pass.empty() ? nullptr : pass.c_str();
X509* cert = nullptr;
EVP_PKEY* key = nullptr;
if (ssl)
{
if (certFile.empty() && keyFile.empty())
{
certFile = "dynamiccontrol_demo_cert.pem";
keyFile = "dynamiccontrol_demo_key.pem";
std::string genError;
if (!Logme::GenerateSelfSignedCertificateFiles(certFile.c_str(), keyFile.c_str(), genError))
{
std::cerr << "ERROR: " << genError << std::endl;
return 2;
}
std::cout
<< "Demo TLS certificate generated:\n"
<< " cert: " << certFile << "\n"
<< " key : " << keyFile << "\n"
<< std::endl;
}
std::string error;
if (!Logme::LoadSslCertificateFromFile(certFile.c_str(), &cert, error))
{
std::cerr << "ERROR: " << error << std::endl;
return 3;
}
if (!Logme::LoadSslPrivateKeyFromFile(keyFile.c_str(), &key, error))
{
Logme::FreeSslCertificate(cert);
std::cerr << "ERROR: " << error << std::endl;
return 4;
}
cfg.Cert = cert;
cfg.Key = key;
}
if (!Logme::Instance->StartControlServer(cfg))
{
std::cerr << "Failed to start control server" << std::endl;
#ifdef _WIN32
WSACleanup();
#endif
return 2;
}
PrintInstructions(port, ssl, pass);
Worker w1("t1", "ch1", "s1", "s2");
Worker w2("t2", "ch2", "s1", "s2");
w1.Start();
w2.Start();
std::string line;
std::getline(std::cin, line);
w1.Stop();
w2.Stop();
if (key)
Logme::FreeSslPrivateKey(key);
if (cert)
Logme::FreeSslCertificate(cert);
#ifdef _WIN32
WSACleanup();
#endif
return 0;
}#include "Worker.h"
#include <chrono>
#include <Logme/Logme.h>
Worker::Worker(
const char* name
, const char* channel
, const char* subsystemA
, const char* subsystemB
)
: Name(name ? name : "")
, ChannelId{channel ? channel : ""}
, SubsystemA(Logme::SID::Build(subsystemA ? subsystemA : ""))
, SubsystemB(Logme::SID::Build(subsystemB ? subsystemB : ""))
, SubsystemAName(subsystemA ? subsystemA : "")
, SubsystemBName(subsystemB ? subsystemB : "")
, StopFlag(false)
{
}
Worker::~Worker()
{
Stop();
}
void Worker::Start()
{
StopFlag = false;
Thread = std::thread(&Worker::Run, this);
}
void Worker::Stop()
{
StopFlag = true;
if (Thread.joinable())
Thread.join();
}
void Worker::WriteA()
{
LogmeI(
ChannelId
, SubsystemA
, "%s [%s/%s]: A: channel + subsystem"
, Name.c_str()
, (ChannelId.Name ? ChannelId.Name : "")
, SubsystemAName.c_str()
);
}
void Worker::WriteB()
{
LogmeI(
ChannelId
, SubsystemB
, "%s [%s/%s]: B: channel + subsystem"
, Name.c_str()
, (ChannelId.Name ? ChannelId.Name : "")
, SubsystemBName.c_str()
);
}
void Worker::Run()
{
LogmeI("%s started", Name.c_str());
while (!StopFlag)
{ LogmeI(
ChannelId
, "%s [%s]: periodic message"
, Name.c_str()
, (ChannelId.Name ? ChannelId.Name : "")
);
WriteA();
WriteB();
std::this_thread::sleep_for(std::chrono::seconds(3));
}
LogmeI("%s stopped", Name.c_str());
}#pragma once
#include <atomic>
#include <string>
#include <thread>
#include <Logme/ID.h>
#include <Logme/SID.h>
class Worker
{
std::string Name;
Logme::ID ChannelId;
Logme::SID SubsystemA;
Logme::SID SubsystemB;
std::string SubsystemAName;
std::string SubsystemBName;
std::thread Thread;
std::atomic_bool StopFlag;
public:
Worker(
const char* name
, const char* channel
, const char* subsystemA
, const char* subsystemB
);
~Worker();
void Start();
void Stop();
private:
void Run();
void WriteA();
void WriteB();
};Previous: DumpBuffer | Next: FormatApi
logme — flexible runtime logging system
Home · Getting Started · Architecture · Output · Backends · Configuration
GitHub: https://github.com/efmsoft/logme
- Home
- Getting Started
- Why logme?
- Core Concepts
- Logging Macros
- Fatal Handling
- Crash Logging
- glog Compatibility
- C API
- Choosing Logging Macros
- Function tracing
- Trace Points
- Override Scopes
- Advanced Features
- Collapse Logging
- Feature Map
- Overview
- Console Backend
- Debugger Backend
- File Backend
- File Rotation & Retention
- Buffer Backend
- Ring Buffer Backend
- SharedFile Backend
- Callback Backend
- Windows Event Log Backend
- Custom Backends
- Runtime Control
- Configuration
- Configuration JSON
- Control Server
- Environment Control
- Control Policies
- Trace Points
- Message Filtering