<variablelist>
<varlistentry>
- <term><command>metrics</command></term>
+ <term><command>metrics</command> <arg choice="opt" rep="repeat">MATCH</arg></term>
- <listitem><para>Acquire a list of metrics values from all local services providing them, and write them to standard output.</para>
+ <listitem><para>Acquire a list of metrics values from all local services providing them, and write
+ them to standard output. Optionally takes one or more match expressions for filtering the metrics to
+ show. The expression either may be a literal metric family name to search for, or a prefix of one
+ (which will be matched only at dot boundaries). If multiple matches are specified as multiple
+ paramaters, any metric matching <emphasis>any</emphasis> of the specified matches are shown.</para>
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
</varlistentry>
<varlistentry>
- <term><command>describe-metrics</command></term>
+ <term><command>describe-metrics</command> <arg choice="opt" rep="repeat">MATCH</arg></term>
<listitem><para>Acquire a list of metric families from all local services providing them, and write
- them to standard output. This returns information about metrics, their data types and human readable
- description without values.</para>
+ them to standard output. This returns primarily static information about metrics, their data types
+ and human readable description, without values.</para>
+
+ <para>Match expressions similar to those supported by <command>metrics</command> are supported for
+ <command>describe-metrics</command>, too.</para>
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
</varlistentry>
static PagerFlags arg_pager_flags = 0;
static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF|SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
+static char **arg_matches = NULL;
+
+STATIC_DESTRUCTOR_REGISTER(arg_matches, strv_freep);
typedef enum Action {
ACTION_LIST,
return !isempty(m) && m[0] == '.';
}
-static bool metrics_validate_one(LinkInfo *li, sd_json_variant *metric) {
+typedef enum {
+ VERDICT_INVALID,
+ VERDICT_MATCH,
+ VERDICT_MISMATCH,
+} Verdict;
+
+static Verdict metrics_verdict(LinkInfo *li, sd_json_variant *metric) {
int r;
assert(li);
r = sd_json_dispatch(metric, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &metric_name);
if (r < 0) {
log_debug_errno(r, "Failed to get metric name, assuming name is not valid: %m");
- return false;
+ return VERDICT_INVALID;
}
+ /* Validate metric name is generally valid */
r = metrics_name_valid(metric_name);
if (r < 0) {
log_debug_errno(r, "Failed to determine if '%s' is a valid metric name: %m", metric_name);
- return false;
+ return VERDICT_INVALID;
}
if (!r) {
log_debug("Metric name '%s' is not valid, skipping.", metric_name);
- return false;
+ return VERDICT_INVALID;
}
- return metric_startswith_prefix(metric_name, li->metric_prefix);
+ /* Validate metric name matches the Varlink service it was found on */
+ if (!metric_startswith_prefix(metric_name, li->metric_prefix)) {
+ log_debug("Metric name '%s' does not match service name '%s', skipping.", metric_name, li->metric_prefix);
+ return VERDICT_INVALID;
+ }
+
+ /* Check it against any specified matches */
+ bool matches;
+ if (strv_isempty(arg_matches))
+ matches = true;
+ else {
+ matches = false;
+
+ /* Allow exact matches or prefix matches */
+ STRV_FOREACH(i, arg_matches)
+ if (streq(metric_name, *i) ||
+ metric_startswith_prefix(metric_name, *i)) {
+ matches = true;
+ break;
+ }
+ }
+
+ if (!matches) {
+ log_debug("Metric '%s' does not match search, ignoring.", metric_name);
+ return VERDICT_MISMATCH;
+ }
+
+ return VERDICT_MATCH;
}
static int metrics_on_query_reply(
goto finish;
}
- if (!metrics_validate_one(li, parameters)) {
+ Verdict v = metrics_verdict(li, parameters);
+ if (v == VERDICT_INVALID) {
context->n_invalid_metrics++;
goto finish;
}
+ if (v == VERDICT_MISMATCH)
+ goto finish;
/* Collect metrics for later sorting */
if (!GREEDY_REALLOC(context->metrics, context->n_metrics + 1))
return 0;
}
+static int parse_metrics_matches(char **matches) {
+ int r;
+
+ STRV_FOREACH(i, matches) {
+ r = metrics_name_valid(*i);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine if '%s' is a valid metric name: %m", *i);
+ if (!r && !varlink_idl_interface_name_is_valid(*i))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Match is not a valid family name or prefix: %s", *i);
+
+ if (strv_extend(&arg_matches, *i) < 0)
+ return log_oom();
+ }
+
+ strv_sort_uniq(arg_matches);
+ return 0;
+}
+
+static bool test_service_matches(const char *service) {
+ assert(service);
+
+ if (strv_isempty(arg_matches))
+ return true;
+
+ /* Only contact services whose name is either a prefix of any of the specified metrics families, or
+ * if the specified metric families are a prefix of the service.
+ *
+ * Example: if user specifies "foo" we want to match sockets "foo" and "foo.bar".
+ * if user specifies "foo.waldo" we want to match sockets "foo" and "foo.waldo" as well as "foo.waldo.quux".
+ *
+ * in other words: it should be fine to specify any prefix of a service name to get all metrics from all matching services.
+ * it should also be fine to specify a full metric name, and then go directly to the relevant services, and ask for matching metrics.
+ */
+
+ STRV_FOREACH(i, arg_matches) {
+ if (streq(service, *i))
+ return true;
+
+ if (metric_startswith_prefix(*i, service) ||
+ metric_startswith_prefix(service, *i))
+ return true;
+ }
+
+ return false;
+}
+
static int readdir_sources(char **ret_directory, DirectoryEntries **ret) {
int r;
if (!varlink_idl_interface_name_is_valid(d->d_name))
continue;
+ if (!test_service_matches(d->d_name))
+ continue;
+
de->entries[m++] = *i;
}
Action action;
int r;
- assert(argc == 1);
+ assert(argc >= 1);
assert(argv);
if (streq_ptr(argv[0], "metrics"))
action = ACTION_DESCRIBE;
}
+ r = parse_metrics_matches(argv + 1);
+ if (r < 0)
+ return r;
+
_cleanup_(context_done) Context context = {
.action = action,
};
printf("%1$s [OPTIONS...] COMMAND ...\n"
"\n%5$sAcquire metrics from local sources.%6$s\n"
"\n%3$sCommands:%4$s\n"
- " metrics Acquire list of metrics and their values\n"
- " describe-metrics Describe available metrics\n"
+ " metrics [MATCH...] Acquire list of metrics and their values\n"
+ " describe-metrics [MATCH...]\n"
+ " Describe available metrics\n"
" list-sources Show list of known metrics sources\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
static int report_main(int argc, char *argv[]) {
static const Verb verbs[] = {
- { "help", VERB_ANY, 1, 0, verb_help },
- { "metrics", VERB_ANY, 1, 0, verb_metrics },
- { "describe-metrics", VERB_ANY, 1, 0, verb_metrics },
- { "list-sources", VERB_ANY, 1, 0, verb_list_sources },
+ { "help", VERB_ANY, 1, 0, verb_help },
+ { "metrics", VERB_ANY, VERB_ANY, 0, verb_metrics },
+ { "describe-metrics", VERB_ANY, VERB_ANY, 0, verb_metrics },
+ { "list-sources", VERB_ANY, 1, 0, verb_list_sources },
{}
};