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>
Wed, 27 Jun 2018 00:42:14 +0000 (20:42 -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-11.c
src/bin/lttng-relayd/cmd-2-11.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 e2d71a0d1351f848a1df2493b6554e737b303278..a71bca260b0a8904e14f9f3e2fd66ed9be3dc2a3 100644 (file)
@@ -113,7 +113,7 @@ error:
 int cmd_recv_stream_2_11(const struct lttng_buffer_view *payload,
                char **ret_path_name, char **ret_channel_name,
                uint64_t *tracefile_size, uint64_t *tracefile_count,
-               uint64_t *trace_archive_id)
+               uint64_t *trace_archive_id, struct relay_session *session)
 {
        int ret;
        struct lttcomm_relayd_add_stream_2_11 header;
@@ -184,7 +184,7 @@ int cmd_recv_stream_2_11(const struct lttng_buffer_view *payload,
                goto error;
        }
 
-       path_name = create_output_path(pathname_view.data);
+       path_name = create_output_path(pathname_view.data, session->session_name);
        if (!path_name) {
                PERROR("Path name allocation");
                ret = -ENOMEM;
index a50872bd360773a44a5bb48e977809c3e10bd624..b281c50065d016ce81f49134e4b56d3d80e746da 100644 (file)
@@ -18,6 +18,7 @@
 #define RELAYD_CMD_2_11_H
 
 #include "lttng-relayd.h"
+#include "session.h"
 #include <common/buffer-view.h>
 
 int cmd_create_session_2_11(const struct lttng_buffer_view *payload,
@@ -27,6 +28,6 @@ int cmd_create_session_2_11(const struct lttng_buffer_view *payload,
 int cmd_recv_stream_2_11(const struct lttng_buffer_view *payload,
                char **ret_path_name, char **ret_channel_name,
                uint64_t *tracefile_size, uint64_t *tracefile_count,
-               uint64_t *trace_archive_id);
+               uint64_t *trace_archive_id, struct relay_session *session);
 
 #endif /* RELAYD_CMD_2_11_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 5a3bcb43fbc8aea671e4cc7b0a3c15451522181e..d5720e8831e16da6d3ae0da90184ffab0b335ab8 100644 (file)
@@ -94,6 +94,8 @@ enum relay_connection_status {
 /* command line options */
 char *opt_output_path;
 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
@@ -179,6 +181,8 @@ static struct option long_options[] = {
        { "verbose", 0, 0, 'v', },
        { "config", 1, 0, 'f' },
        { "version", 0, 0, 'V' },
+       { "group-output-by-session", 0, 0, 's', },
+       { "group-output-by-host", 0, 0, 'p', },
        { NULL, 0, 0, 0, },
 };
 
@@ -300,6 +304,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 */
@@ -487,6 +505,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;
@@ -1209,16 +1232,17 @@ static int relay_add_stream(const struct lttcomm_relayd_hdr *recv_hdr,
        if (session->minor == 1) {
                /* For 2.1 */
                ret = cmd_recv_stream_2_1(payload, &path_name,
-                       &channel_name);
+                       &channel_name, session);
        } else if (session->minor > 1 && session->minor < 11) {
                /* From 2.2 to 2.10 */
                ret = cmd_recv_stream_2_2(payload, &path_name,
-                       &channel_name, &tracefile_size, &tracefile_count);
+                       &channel_name, &tracefile_size, &tracefile_count,
+                       session);
        } else {
                /* From 2.11 to ... */
                ret = cmd_recv_stream_2_11(payload, &path_name,
                        &channel_name, &tracefile_size, &tracefile_count,
-                       &stream_chunk_id.value);
+                       &stream_chunk_id.value, session);
                stream_chunk_id.is_set = true;
        }
 
@@ -2471,7 +2495,7 @@ static int relay_rotate_session_stream(const struct lttcomm_relayd_hdr *recv_hdr
         * change).
         */
        free(stream->path_name);
-       stream->path_name = create_output_path(new_path_view.data);
+       stream->path_name = create_output_path(new_path_view.data, session->session_name);
        if (!stream->path_name) {
                ERR("Failed to create a new output path");
                ret = -1;
@@ -2587,7 +2611,7 @@ static int relay_mkdir(const struct lttcomm_relayd_hdr *recv_hdr,
        path_view = lttng_buffer_view_from_view(payload, header_len,
                        path_info_header.length);
 
-       path = create_output_path(path_view.data);
+       path = create_output_path(path_view.data, session->session_name);
        if (!path) {
                ERR("Failed to create output path");
                ret = -1;
@@ -2717,14 +2741,14 @@ static int relay_rotate_rename(const struct lttcomm_relayd_hdr *recv_hdr,
                goto end;
        }
 
-       complete_old_path = create_output_path(old_path_view.data);
+       complete_old_path = create_output_path(old_path_view.data, session->session_name);
        if (!complete_old_path) {
                ERR("Failed to build old output path in rotate_rename command");
                ret = -1;
                goto end;
        }
 
-       complete_new_path = create_output_path(new_path_view.data);
+       complete_new_path = create_output_path(new_path_view.data, session->session_name);
        if (!complete_new_path) {
                ERR("Failed to build new output path in rotate_rename command");
                ret = -1;
index 837c828d22b935cb7eb353a60f6d3e93f2dc277d..6c6f6f8056f6c52ee9f434c5c18796891bb1f1cc 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"
 
+#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;
@@ -84,13 +221,32 @@ exit:
  *
  * Return the allocated string containing the path name or else NULL.
  */
-char *create_output_path(const 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 f0b618420ab56d4da52265a2951db818463ef84c..33789598872cd7f37d35def5ce0f6cbbe06d002b 100644 (file)
@@ -20,6 +20,6 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-char *create_output_path(const char *path_name);
+char *create_output_path(const char *path_name, char* session_name);
 
 #endif /* RELAYD_UTILS_H */
This page took 0.035195 seconds and 5 git commands to generate.