Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion doc/appendices/command-line/traffic_ctl.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ traffic_ctl metric
Display the current value of the specified metric(s) using an interval time
and a count value. Use ``-i`` to set the interval time between requests, and
``-c`` to set the number of requests the program will send in total per metric.
The program will terminate execution after requesting <count> metrics.
If ``count=0`` is passed or ``count`` is not specified then the program should be terminated
by SIGINT.
Note that the metric will display `+` or `-` depending on the value of the last
metric and the current being shown, if current is greater, then `+` will be
added beside the metric value, `-` if the last value is less than current,
Expand All @@ -345,7 +348,7 @@ traffic_ctl metric
proxy.process.eventloop.time.min.10s: 4011194
proxy.process.eventloop.time.min.10s: 4011194
proxy.process.eventloop.time.min.10s: 4018669 +
--- metric monitor statistics ---
--- metric monitor statistics(10) ---
┌ proxy.process.eventloop.time.min.10s
└─ min/avg/max = 4011194/4017498/4025085

Expand Down
72 changes: 44 additions & 28 deletions src/traffic_ctl/CtrlCommands.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,32 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "CtrlCommands.h"

#include <fstream>
#include <unordered_map>
#include <chrono>
#include <thread>
#include <csignal>
#include <unistd.h>

#include <swoc/TextView.h>
#include <swoc/BufferWriter.h>
#include <swoc/bwf_base.h>

#include "CtrlCommands.h"
#include "jsonrpc/CtrlRPCRequests.h"
#include "jsonrpc/ctrl_yaml_codecs.h"
#include "swoc/TextView.h"
#include "swoc/BufferWriter.h"
#include "swoc/bwf_base.h"

namespace
{
/// We use yamlcpp as codec implementation.
using Codec = yamlcpp_json_emitter;
} // namespace

const std::unordered_map<std::string_view, BasePrinter::Options::OutputFormat> _Fmt_str_to_enum = {
{"json", BasePrinter::Options::OutputFormat::JSON},
{"rpc", BasePrinter::Options::OutputFormat::RPC }
};
} // namespace

BasePrinter::Options::OutputFormat
parse_format(ts::Arguments *args)
Expand All @@ -57,13 +62,13 @@ parse_format(ts::Arguments *args)
}
return val;
}

BasePrinter::Options
parse_print_opts(ts::Arguments *args)
{
return {parse_format(args)};
}

std::atomic_int CtrlCommand::Signal_Flagged{0};
//------------------------------------------------------------------------------------------------------------------------------------
CtrlCommand::CtrlCommand(ts::Arguments *args) : _arguments(args) {}

Expand Down Expand Up @@ -289,11 +294,12 @@ MetricCommand::metric_monitor()
//
// Note: if any of the string->number fails, the exception will be caught by the invoke function from the ArgParser.
//
const int32_t count = std::stoi(get_parsed_arguments()->get("count").value());
const int32_t count = std::stoi(get_parsed_arguments()->get("count").value());
int32_t query_count{0};
const int32_t interval = std::stoi(get_parsed_arguments()->get("interval").value());
if (count <= 0 || interval <= 0) {
throw std::runtime_error(
ts::bwprint(err_text, "monitor: invalid input, count({}), interval({}) should be > than '0'", count, interval));
// default count is 0.
if (count < 0 || interval <= 0) {
throw std::runtime_error(swoc::bwprint(err_text, "monitor: invalid input, count: {}(>=0), interval: {}(>=1)", count, interval));
}

// keep track of each metric
Expand All @@ -305,9 +311,27 @@ MetricCommand::metric_monitor()
};

// Keep track of the requested metric(s), we support more than one at the same time.

// To be used to print all the stats. This is a lambda function as this could
// be called when SIGINT is invoked, so we dump what we have before exit.
auto dump = [&](std::unordered_map<std::string, ctx> const &_summary) {
if (_summary.size() == 0) {
// nothing to report.
return;
}

_printer->write_output(swoc::bwprint(err_text, "--- metric monitor statistics({}) ---", query_count));

for (auto const &item : _summary) {
ctx const &s = item.second;
const int avg = s.sum / query_count;
_printer->write_output(swoc::bwprint(err_text, "┌ {}\n└─ min/avg/max = {:.5}/{}/{:.5}", item.first, s.min, avg, s.max));
}
};

std::unordered_map<std::string, ctx> summary;

for (int idx = 0;; idx++) {
while (!Signal_Flagged.load()) {
// Request will hold all metrics in a single message.
shared::rpc::JSONRPCResponse const &resp = record_fetch(arg, shared::rpc::NOT_REGEX, RecordQueryType::METRIC);

Expand All @@ -323,41 +347,33 @@ MetricCommand::metric_monitor()
}

for (auto &&rec : response.recordList) { // requested metric(s)
auto &s = summary[rec.name]; // We will update it.
// Note: To avoid
auto &s = summary[rec.name]; // We will update it.
const float val = std::stof(rec.currentValue);

s.sum += val;
s.max = std::max<float>(s.max, val);
s.min = std::min<float>(s.min, val);
std::string symbol;
if (idx > 0) {
if (query_count > 0) {
if (val > s.last) {
symbol = "+";
} else if (val < s.last) {
symbol = "-";
}
}
s.last = val;
_printer->write_output(ts::bwprint(err_text, "{}: {} {}", rec.name, rec.currentValue, symbol));
_printer->write_output(swoc::bwprint(err_text, "{}: {} {}", rec.name, rec.currentValue, symbol));
}
if (idx == count - 1) {

if ((query_count++ == count - 1) && count > 0 /* could be a forever loop*/) {
break;
}
std::this_thread::sleep_for(std::chrono::seconds(interval));
}
if (summary.size() == 0) {
// nothing to report.
return;
}

_printer->write_output("--- metric monitor statistics ---");

for (auto &&item : summary) {
ctx const &s = item.second;
const int avg = s.sum / count;
_printer->write_output(ts::bwprint(err_text, "┌ {}\n└─ min/avg/max = {:.5}/{}/{:.5}", item.first, s.min, avg, s.max));
sleep(interval);
}

// all done, print summary.
dump(summary);
}
//------------------------------------------------------------------------------------------------------------------------------------
// TODO, let call the super const
Expand Down
4 changes: 4 additions & 0 deletions src/traffic_ctl/CtrlCommands.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ class CtrlCommand
/// If @c _invoked_func is not properly set, the function will throw @c logic_error
virtual void execute();

/// @brief This variable is used to mark if a Signal was flagged by the application. Default value is 0 and the signal number
/// should be set when the signal is handled.
static std::atomic_int Signal_Flagged;

protected:
/// @brief The whole design is that the command will execute the @c _invoked_func once invoked. This function ptr should be
/// set by the appropriated derived class base on the passed parameters. The derived class have the option to override
Expand Down
37 changes: 35 additions & 2 deletions src/traffic_ctl/traffic_ctl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@
*/

#include <iostream>
#include <csignal>

#include "tscore/I_Layout.h"
#include "tscore/runroot.h"
#include "tscore/ArgParser.h"
#include "tscore/ink_assert.h"
#include "tscore/signals.h"

#include "CtrlCommands.h"
#include "FileConfigCommand.h"
Expand All @@ -36,6 +39,28 @@ constexpr int CTRL_EX_UNIMPLEMENTED = 3;

int status_code{CTRL_EX_OK};

namespace
{
void
handle_signal(int signal_num, siginfo_t *, void *)
{
CtrlCommand::Signal_Flagged = signal_num;
}

void
signal_register_handler(int signal_num, signal_handler_t handle_signal)
{
struct sigaction act;

act.sa_handler = nullptr;
act.sa_sigaction = handle_signal;
act.sa_flags = SA_NODEFER | SA_RESETHAND;
sigemptyset(&(act.sa_mask));

ink_release_assert(sigaction(signal_num, &act, nullptr) == 0);
}
} // namespace

int
main(int argc, const char **argv)
{
Expand Down Expand Up @@ -124,9 +149,14 @@ main(int argc, const char **argv)
metric_command.add_command("match", "Get metrics matching a regular expression", "", MORE_THAN_ZERO_ARG_N,
[&]() { command->execute(); });
metric_command
.add_command("monitor", "Display the value of a metric(s) over time", "", MORE_THAN_ZERO_ARG_N, [&]() { command->execute(); })
.add_command(
"monitor",
"Display the value of a metric(s) over time. Program stops after <count> or with a SIGINT. A brief summary is displayed.", "",
MORE_THAN_ZERO_ARG_N, [&]() { command->execute(); })
.add_example_usage("traffic_ctl metric monitor METRIC -i 3 -c 10")
.add_option("--count", "-c", "Stop after requesting count metrics.", "", 1, "10")
.add_option("--count", "-c",
"Terminate execution after requesting <count> metrics. If 0 is passed, program should be terminated by a SIGINT",
"", 1, "0")
.add_option("--interval", "-i", "Wait interval seconds between sending each metric request. Minimum value is 1s.", "", 1, "5");
metric_command.add_command("zero", "Clear one or more metric values", "", MORE_THAN_ONE_ARG_N, [&]() { command->execute(); });

Expand Down Expand Up @@ -183,6 +213,9 @@ main(int argc, const char **argv)
.add_example_usage("traffic_ctl rpc invoke foo_bar -p \"numbers: [1, 2, 3]\"");

try {
// for now we only care about SIGINT(SIGQUIT, ... ?)
signal_register_handler(SIGINT, handle_signal);

auto args = parser.parse(argv);
argparser_runroot_handler(args.get("run-root").value(), argv[0]);
Layout::create();
Expand Down