Backport: Relayd: introduce --group-output-by-session
authorJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Tue, 19 Jun 2018 02:46:25 +0000 (22:46 -0400)
committerJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Thu, 28 Jun 2018 12:59:00 +0000 (08:59 -0400)
LTTng-relayd now support the grouping of trace data per session name.
This mode can be used via the "--group-output-per-session"

The default, and current way, of grouping is done around the hostname of
the traced system.

When grouped by host the following folder hierarchy is mostly found on the filesystem:

    <hostname>/<session_name>[-<datetime>]/<trace>

When using "--group-output-per-session", the following hierarchy is
found on the filesystem:

    <session_name>/<hostname>[-<datetime>]/<trace>

The datetime is not always present given how it is generated in certain
situations either on the client (cli) or in lttng-sessiond and specified
output type.

This commit ensure maximum compatibility with all lttng-relayd client
(lttng-consumerd) version.

Further work can be accomplished on the
client (cli), lttng-sessiond, lttng-consumerd to pass individual path
information: session name, datetime and hostname. Note that the
automatic naming can be problematic since the datetime is used inside
the session name while when using a defined session name it is not part
of the session name. Still, this would leave older lttng-relayd client
version in the dark given that such modification would require
communication API changes. The current solution ensure that even when
dealing with older client the grouping option is respected.

The received paths are tokenized in 3 tokens.

The first one is the hostname. This is respected across all
communications. Note that, the hostname is already known since the
create session command (version >= 2.4).

The second one can either be a session name, with or without a datetime,
or extra path information. The extra path information come from the URIs
set at the client level:

    lttng create --set-url=net://localhost/extra/path/information

When the second token is extra path information the session name is
never present in the path. We reuse the name passed on the create
session command (version >= 2.4).

The datetime, if present, is extracted from the session name passed by
the client or if not present from the session name passed by the create
session command. This enable the support of automatic session name.

The third one is the rest of the path. No information extraction is done
on this token.

Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
src/bin/lttng-relayd/cmd-2-1.c
src/bin/lttng-relayd/cmd-2-1.h
src/bin/lttng-relayd/cmd-2-2.c
src/bin/lttng-relayd/cmd-2-2.h
src/bin/lttng-relayd/lttng-relayd.h
src/bin/lttng-relayd/main.c
src/bin/lttng-relayd/utils.c
src/bin/lttng-relayd/utils.h

index 99aa86668048e719daa4100a09ff425de806dd3a..291e1e3806998afeefee43f32e587f100f3e2ae4 100644 (file)
@@ -32,7 +32,8 @@
  * cmd_recv_stream_2_1 allocates path_name and channel_name.
  */
 int cmd_recv_stream_2_1(const struct lttng_buffer_view *payload,
-               char **ret_path_name, char **ret_channel_name)
+               char **ret_path_name, char **ret_channel_name,
+               struct relay_session *session)
 {
        int ret;
        struct lttcomm_relayd_add_stream stream_info;
@@ -55,7 +56,7 @@ int cmd_recv_stream_2_1(const struct lttng_buffer_view *payload,
                ERR("Path name too long");
                goto error;
        }
-       path_name = create_output_path(stream_info.pathname);
+       path_name = create_output_path(stream_info.pathname, session->session_name);
        if (!path_name) {
                PERROR("Path name allocation");
                ret = -ENOMEM;
index c1eb6b32779256ea8bc9664c0c80750559330dd3..47b8f3d141b14ab0127b8deed39f440c39115b95 100644 (file)
  */
 
 #include "lttng-relayd.h"
+#include "session.h"
 #include <common/buffer-view.h>
 
 int cmd_recv_stream_2_1(const struct lttng_buffer_view *payload,
-               char **path_name, char **channel_name);
+               char **path_name, char **channel_name,
+               struct relay_session *session);
 
 #endif /* RELAYD_CMD_2_1_H */
index 5ff6280509a2119291df7065a01af7d2d5bd1122..70415d7bb0c5dc8d29c72740dc555951edc535c8 100644 (file)
@@ -35,7 +35,8 @@
  */
 int cmd_recv_stream_2_2(const struct lttng_buffer_view *payload,
                char **ret_path_name, char **ret_channel_name,
-               uint64_t *tracefile_size, uint64_t *tracefile_count)
+               uint64_t *tracefile_size, uint64_t *tracefile_count,
+               struct relay_session *session)
 {
        int ret;
        struct lttcomm_relayd_add_stream_2_2 stream_info;
@@ -58,7 +59,7 @@ int cmd_recv_stream_2_2(const struct lttng_buffer_view *payload,
                ERR("Path name too long");
                goto error;
        }
-       path_name = create_output_path(stream_info.pathname);
+       path_name = create_output_path(stream_info.pathname, session->session_name);
        if (!path_name) {
                PERROR("Path name allocation");
                ret = -ENOMEM;
index 822f627b1d07dbcf5e2c10fa9f76160a37dbed4b..864995aa6ea443ecd40b40f4ad441e9272fb0d5f 100644 (file)
@@ -25,6 +25,7 @@
 
 int cmd_recv_stream_2_2(const struct lttng_buffer_view *payload,
                char **path_name, char **channel_name,
-               uint64_t *tracefile_size, uint64_t *tracefile_count);
+               uint64_t *tracefile_size, uint64_t *tracefile_count,
+               struct relay_session *session);
 
 #endif /* RELAYD_CMD_2_2_H */
index e4e29e781e3ed8233cfdb6c82ddbe7ee32618038..a3976852db2d2f40863f4e8defba770479df056d 100644 (file)
@@ -47,6 +47,8 @@ extern struct lttng_ht *viewer_streams_ht;
 extern char *opt_output_path;
 extern const char *tracing_group_name;
 extern const char * const config_section_name;
+extern int opt_group_output_by_session;
+extern int opt_group_output_by_host;
 
 extern int thread_quit_pipe[2];
 
index c171dfdfe41de54b126d8928a680c496e5890e18..c3c9a4fa667bd38c7ec0d866c402d5bf736d39f3 100644 (file)
@@ -85,6 +85,8 @@ enum relay_connection_status {
 /* command line options */
 char *opt_output_path, *opt_working_directory;
 static int opt_daemon, opt_background;
+int opt_group_output_by_session;
+int opt_group_output_by_host;
 
 /*
  * We need to wait for listener and live listener threads, as well as
@@ -170,6 +172,8 @@ static struct option long_options[] = {
        { "config", 1, 0, 'f' },
        { "version", 0, 0, 'V' },
        { "working-directory", 1, 0, 'w', },
+       { "group-output-by-session", 0, 0, 's', },
+       { "group-output-by-host", 0, 0, 'p', },
        { NULL, 0, 0, 0, },
 };
 
@@ -305,6 +309,20 @@ static int set_option(int opt, const char *arg, const char *optname)
                        }
                }
                break;
+       case 's':
+               if (opt_group_output_by_host) {
+                       ERR("Cannot set --group-output-by-session, --group-output-by-host already defined");
+                       exit(EXIT_FAILURE);
+               }
+               opt_group_output_by_session = 1;
+               break;
+       case 'p':
+               if (opt_group_output_by_session) {
+                       ERR("Cannot set --group-output-by-host, --group-output-by-session already defined");
+                       exit(EXIT_FAILURE);
+               }
+               opt_group_output_by_host = 1;
+               break;
        default:
                /* Unknown option or other error.
                 * Error is printed by getopt, just return */
@@ -502,6 +520,11 @@ static int set_options(int argc, char **argv)
                }
        }
 
+       if (!opt_group_output_by_session && !opt_group_output_by_host) {
+               /* Group by host by default */
+               opt_group_output_by_host = 1;
+       }
+
 exit:
        free(optstring);
        return retval;
@@ -1219,12 +1242,13 @@ static int relay_add_stream(const struct lttcomm_relayd_hdr *recv_hdr,
        switch (session->minor) {
        case 1: /* LTTng sessiond 2.1. Allocates path_name and channel_name. */
                ret = cmd_recv_stream_2_1(payload, &path_name,
-                       &channel_name);
+                       &channel_name, session);
                break;
        case 2: /* LTTng sessiond 2.2. Allocates path_name and channel_name. */
        default:
                ret = cmd_recv_stream_2_2(payload, &path_name,
-                       &channel_name, &tracefile_size, &tracefile_count);
+                       &channel_name, &tracefile_size, &tracefile_count,
+                       session);
                break;
        }
        if (ret < 0) {
index 51fe19348d09f1378999d5ab3b6041b856f6132c..682c5dd595a669576c032ac4b7e3396b6faa5b37 100644 (file)
@@ -21,6 +21,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <regex.h>
 
 #include <common/common.h>
 #include <common/defaults.h>
 #include "lttng-relayd.h"
 #include "utils.h"
 
-static char *create_output_path_auto(char *path_name)
+#define DATETIME_STRING_SIZE 16
+
+#define DATETIME_REGEX ".*-[0-9][0-9][0-9][0-9][0-1][0-9][0-3][0-9]-[0-2][0-9][0-5][0-9][0-5][0-9]$"
+
+static char *get_filesystem_per_session(const char *path, const char *local_session_name) {
+       int ret;
+       char *local_copy = NULL;
+       char *session_name = NULL;
+       char *datetime = NULL;
+       char *extra_path = NULL;
+       char *hostname_ptr;
+       const char *second_token_ptr;
+       char *leftover_ptr;
+       char *filepath_per_session = NULL;
+       regex_t regex;
+
+       /* Get a local copy for strtok */
+       local_copy = strdup(path);
+       if (!local_copy) {
+               ERR("strdup of local copy failed");
+               goto error;
+       }
+
+       /*
+        * The use of strtok with '/' as delimiter is valid since we refuse '/'
+        * in session name and '/' is not a valid hostname character based on
+        * RFC-952 [1], RFC-921 [2] and refined in RFC-1123 [2].
+        * [1] https://tools.ietf.org/html/rfc952
+        * [2] https://tools.ietf.org/html/rfc921
+        * [3] https://tools.ietf.org/html/rfc1123#page-13
+        */
+
+       /*
+        * Get the hostname and possible session_name.
+        * Note that we can get the hostname and session name from the
+        * relay_session object we already have. Still, it is easier to
+        * tokenized the passed path to obtain the start of the path leftover.
+        */
+       hostname_ptr = strtok_r(local_copy, "/", &leftover_ptr);
+       if (!hostname_ptr) {
+               ERR("hostname token not found");
+               goto error;
+       }
+
+       second_token_ptr = strtok_r(NULL, "/", &leftover_ptr);
+       if (!second_token_ptr) {
+               ERR("Session name token not found");
+               goto error;
+       }
+
+       /*
+        * Check if the second token is a extra path set at url level. This is
+        * legal in streaming, live and snapshot [1]. Otherwise it is the
+        * session name with possibly a datetime attached [2]. Note that when
+        * "adding" snapshot output (lttng snapshot add-output), no session name
+        * is present in the path by default. The handling for "extra path" take
+        * care of this case as well.
+        * [1] e.g --set-url net://localhost/my_marvellous_path
+        * [2] Can be:
+        *            <session_name>
+        *                When using --snapshot on session create.
+        *            <session_name>-<date>-<time>
+        *            auto-<date>-<time>
+        */
+       if (strncmp(second_token_ptr, local_session_name, strlen(local_session_name)) != 0) {
+               /* Match */
+               extra_path = strdup(second_token_ptr);
+               /*
+                * Point the second token ptr to local_session_name for further
+                * information extraction based on the session name
+                */
+               second_token_ptr = local_session_name;
+       } else {
+               extra_path = strdup("");
+       }
+       if (!extra_path) {
+               ERR("strdup extra path failed");
+               goto error;
+       }
+
+       /*
+        * The recovery of the session datetime is a best effort here.
+        * We use a regex to validate that a datetime is present.
+        * We can end up in corner case were the end of a
+        * session name is the same format as our datetime but is not really a
+        * datetime. This is not so much of an issue since most of the time the
+        * datetime will be appended and result in the correct case.
+        * Possible cases:
+        *            <session_name>
+        *            <session_name>-<date>-<time>
+        *            auto-<date>-<time>
+        */
+       ret = regcomp(&regex, DATETIME_REGEX, 0);
+       if (ret) {
+               ERR("Regex compilation failed with %d", ret);
+               goto error;
+       }
+
+       ret = regexec(&regex, second_token_ptr, 0, NULL, 0);
+       if (!ret) {
+               /* Match */
+               session_name = strndup(second_token_ptr, strlen(second_token_ptr) - DATETIME_STRING_SIZE);
+               datetime = strdup(&second_token_ptr[strlen(second_token_ptr) - DATETIME_STRING_SIZE +1]);
+       } else {
+               /* No datetime present */
+               session_name = strdup(second_token_ptr);
+               datetime = strdup("");
+       }
+
+       if (!session_name) {
+               ERR("strdup session_name on regex match failed");
+               goto error_regex;
+       }
+       if (!datetime) {
+               ERR("strdup datetime on regex match failed");
+               goto error_regex;
+       }
+
+       ret = asprintf(&filepath_per_session, "%s/%s%s%s/%s/%s", session_name,
+                       hostname_ptr,
+                       datetime[0] != '\0' ? "-" : "",
+                       datetime, extra_path, leftover_ptr);
+       if (ret < 0) {
+               filepath_per_session = NULL;
+               goto error;
+       }
+error_regex:
+       regfree(&regex);
+error:
+       free(extra_path);
+       free(local_copy);
+       free(session_name);
+       free(datetime);
+       return filepath_per_session;
+}
+
+static char *create_output_path_auto(const char *path_name)
 {
        int ret;
        char *traces_path = NULL;
@@ -84,13 +221,32 @@ exit:
  *
  * Return the allocated string containing the path name or else NULL.
  */
-char *create_output_path(char *path_name)
+char *create_output_path(const char *path_name, char *session_name)
 {
+       char *real_path = NULL;
+       char *return_path = NULL;
        assert(path_name);
 
+       if (opt_group_output_by_session) {
+               real_path = get_filesystem_per_session(path_name, session_name);
+       } else if (opt_group_output_by_host) {
+               /* By default the output is by host */
+               real_path = strdup(path_name);
+       } else {
+               ERR("Configuration error");
+               assert(0);
+       }
+
+       if (!real_path) {
+               goto error;
+       }
+
        if (opt_output_path == NULL) {
-               return create_output_path_auto(path_name);
+               return_path = create_output_path_auto(real_path);
        } else {
-               return create_output_path_noauto(path_name);
+               return_path = create_output_path_noauto(real_path);
        }
+error:
+       free(real_path);
+       return return_path;
 }
index 4a56980e47e7dbbeb7d0b584ab24d32ef3174fd8..33789598872cd7f37d35def5ce0f6cbbe06d002b 100644 (file)
@@ -20,6 +20,6 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-char *create_output_path(char *path_name);
+char *create_output_path(const char *path_name, char* session_name);
 
 #endif /* RELAYD_UTILS_H */
This page took 0.032258 seconds and 5 git commands to generate.