Skip to content

Examples DynamicControl

Eduard Mishkurov edited this page Apr 24, 2026 · 7 revisions

Dynamic control demo

This example shows how to enable runtime control of Logme and change settings from another terminal.

DynamicControl

This example shows how to enable runtime control of Logme and change settings from another terminal.

What it does

  • 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.

Build

Enable the example and the tool:

cmake -S . -B build -G Ninja \
  -DLOGME_BUILD_EXAMPLES=ON \
  -DLOGME_BUILD_TOOLS=ON
cmake --build build -j

Run

Terminal 1:

./DynamicControl

Terminal 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

Notes

  • subsystem mode blacklist blocks subsystems present in the list.
  • subsystem mode whitelist allows only subsystems present in the list.

Source code

Repository: examples/DynamicControl

Files:

main.cpp

Open in repository

#ifdef _WIN32
#include <winsock2.h>
#endif

#include "Worker.h"

#include <iostream>
#include <string>
#include <cstring>

#include <fstream>

#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;
}

Worker.cpp

Open in repository

#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());
}

Worker.h

Open in repository

#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

Clone this wiki locally