relayd: introduce --group-output-by-session
authorJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Thu, 19 Sep 2019 20:56:11 +0000 (16:56 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Fri, 8 Nov 2019 22:33:41 +0000 (17:33 -0500)
LTTng-relayd now support the grouping of traces per session.
This mode can be used via the "--group-output-per-session" switch.

The default, and current way, of grouping is done around the host
(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>

We also need to support base path information that come from the URIs
set at the client level:

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

When grouping by host (current behaviour), it result in the following
hierarchy:

    <hostname>/<base_path>/<trace>

e.g:
    <hostname>/extra/path/information/<trace>

We want to part from this way of handling the base path since it can lead
to unexpected conflict between session.

When grouping by session using a base path, the following hierarchy is
produced:

    <session_name>/<hostname>-<datetime>/<base_path>/<trace>

e.g:
    <session_name>/<hostname>-<datetime>/extra/path/information/<trace>

We encourage user to move away from using base path entirely as it
bypasses introduces potential name clashes and completely bypasses the
relay daemon's storage policy.

Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
src/bin/lttng-relayd/lttng-relayd.h
src/bin/lttng-relayd/main.c
src/bin/lttng-relayd/session.c
src/common/time.c
src/common/time.h

index 40fecd603d5ea5c06994fed66affa81f1ad95016..748083ddfa65e145a6ebde0a98ea9575db35ce86 100644 (file)
@@ -37,6 +37,12 @@ struct relay_conn_queue {
        int32_t futex;
 };
 
+enum relay_group_output_by {
+       RELAYD_GROUP_OUTPUT_BY_UNKNOWN,
+       RELAYD_GROUP_OUTPUT_BY_HOST,
+       RELAYD_GROUP_OUTPUT_BY_SESSION,
+};
+
 /*
  * Contains stream indexed by ID. This is important since many commands lookup
  * streams only by ID thus also keeping them in this hash table makes the
@@ -50,6 +56,7 @@ extern struct sessiond_trace_chunk_registry *sessiond_trace_chunk_registry;
 extern char *opt_output_path;
 extern const char *tracing_group_name;
 extern const char * const config_section_name;
+extern enum relay_group_output_by opt_group_output_by;
 
 extern int thread_quit_pipe[2];
 
index 5f72c32a906f700ec14009766d056ead3e49b375..e7c5b6c52d28dfcd1fa80fdf925c8d5317f45b27 100644 (file)
@@ -98,6 +98,7 @@ enum relay_connection_status {
 /* command line options */
 char *opt_output_path, *opt_working_directory;
 static int opt_daemon, opt_background, opt_print_version;
+enum relay_group_output_by opt_group_output_by = RELAYD_GROUP_OUTPUT_BY_UNKNOWN;
 
 /*
  * We need to wait for listener and live listener threads, as well as
@@ -185,6 +186,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, },
 };
 
@@ -337,6 +340,20 @@ static int set_option(int opt, const char *arg, const char *optname)
                        }
                }
                break;
+       case 's':
+               if (opt_group_output_by != RELAYD_GROUP_OUTPUT_BY_UNKNOWN) {
+                       ERR("Cannot set --group-output-by-session, another --group-output-by argument is present");
+                       exit(EXIT_FAILURE);
+               }
+               opt_group_output_by = RELAYD_GROUP_OUTPUT_BY_SESSION;
+               break;
+       case 'p':
+               if (opt_group_output_by != RELAYD_GROUP_OUTPUT_BY_UNKNOWN) {
+                       ERR("Cannot set --group-output-by-host, another --group-output-by argument is present");
+                       exit(EXIT_FAILURE);
+               }
+               opt_group_output_by = RELAYD_GROUP_OUTPUT_BY_HOST;
+               break;
        default:
                /* Unknown option or other error.
                 * Error is printed by getopt, just return */
@@ -541,6 +558,10 @@ static int set_options(int argc, char **argv)
                }
        }
 
+       if (opt_group_output_by == RELAYD_GROUP_OUTPUT_BY_UNKNOWN) {
+               opt_group_output_by = RELAYD_GROUP_OUTPUT_BY_HOST;
+       }
+
 exit:
        free(optstring);
        return retval;
index 2f65848a9cedc2ed18bd56a630541b6922db8905..05a1dc215f6d01d070a416cdb2ec483f314072d0 100644 (file)
@@ -19,8 +19,9 @@
 
 #define _LGPL_SOURCE
 #include <common/common.h>
-#include <common/utils.h>
 #include <common/compat/uuid.h>
+#include <common/time.h>
+#include <common/utils.h>
 #include <urcu/rculist.h>
 
 #include <sys/stat.h>
@@ -37,7 +38,7 @@
 static uint64_t last_relay_session_id;
 static pthread_mutex_t last_relay_session_id_lock = PTHREAD_MUTEX_INITIALIZER;
 
-static int init_session_output_path(struct relay_session *session)
+static int init_session_output_path_group_by_host(struct relay_session *session)
 {
        /*
         * session_directory:
@@ -71,38 +72,18 @@ static int init_session_output_path(struct relay_session *session)
                ret = asprintf(&session_directory, "%s/%s", session->hostname,
                                session->session_name);
        } else {
-               char session_creation_datetime[16];
-               size_t strftime_ret;
-               struct tm *timeinfo;
-               time_t creation_time;
+               char session_creation_datetime[DATETIME_STR_LEN];
 
-               /*
-                * The 2.11+ protocol guarantees that a creation time
-                * is provided for a session. This would indicate a
-                * protocol error or an improper use of this util.
-                */
-               if (!session->creation_time.is_set) {
-                       ERR("Creation time missing for session \"%s\" (protocol error)",
-                                       session->session_name);
-                       ret = -1;
-                       goto end;
-               }
-               creation_time = LTTNG_OPTIONAL_GET(session->creation_time);
-
-               timeinfo = localtime(&creation_time);
-               if (!timeinfo) {
-                       ERR("Failed to get timeinfo while initializing session output directory handle");
-                       ret = -1;
-                       goto end;
-               }
-               strftime_ret = strftime(session_creation_datetime,
-                               sizeof(session_creation_datetime),
-                               "%Y%m%d-%H%M%S", timeinfo);
-               if (strftime_ret == 0) {
+               ret = time_to_datetime_str(
+                               LTTNG_OPTIONAL_GET(session->creation_time),
+                               session_creation_datetime,
+                               sizeof(session_creation_datetime));
+               if (ret) {
                        ERR("Failed to format session creation timestamp while initializing session output directory handle");
                        ret = -1;
                        goto end;
                }
+
                ret = asprintf(&session_directory, "%s/%s-%s",
                                session->hostname, session->session_name,
                                session_creation_datetime);
@@ -125,6 +106,79 @@ end:
        return ret;
 }
 
+static int init_session_output_path_group_by_session(
+               struct relay_session *session)
+{
+       /*
+        * session_directory:
+        *
+        *   session_name/hostname-creation_time/base_path
+        *
+        * For session name including the datetime, use it as the complete name
+        * since. Do not perform modification on it since the datetime is an
+        * integral part of the name and how a user identify a session.
+        */
+       int ret = 0;
+       char *session_directory = NULL;
+       char creation_datetime[DATETIME_STR_LEN];
+
+       if (session->output_path[0] != '\0') {
+               /* output_path as been generated already */
+               goto end;
+       }
+
+       ret = time_to_datetime_str(LTTNG_OPTIONAL_GET(session->creation_time),
+                       creation_datetime, sizeof(creation_datetime));
+       if (ret) {
+               ERR("Failed to format session creation timestamp while initializing session output directory handle");
+               ret = -1;
+               goto end;
+       }
+
+       ret = asprintf(&session_directory, "%s/%s-%s%s%s",
+                       session->session_name, session->hostname,
+                       creation_datetime,
+                       session->base_path[0] != '\0' ? "/" : "",
+                       session->base_path);
+       if (ret < 0) {
+               PERROR("Failed to format session directory name");
+               goto end;
+       }
+
+       if (strlen(session_directory) >= LTTNG_PATH_MAX) {
+               ERR("Session output directory exceeds maximal length");
+               ret = -1;
+               goto end;
+       }
+
+       strcpy(session->output_path, session_directory);
+       ret = 0;
+
+end:
+       free(session_directory);
+       return ret;
+}
+
+static int init_session_output_path(struct relay_session *session)
+{
+       int ret;
+
+       switch (opt_group_output_by) {
+       case RELAYD_GROUP_OUTPUT_BY_HOST:
+               ret = init_session_output_path_group_by_host(session);
+               break;
+       case RELAYD_GROUP_OUTPUT_BY_SESSION:
+               ret = init_session_output_path_group_by_session(session);
+               break;
+       case RELAYD_GROUP_OUTPUT_BY_UNKNOWN:
+       default:
+               abort();
+               break;
+       }
+
+       return ret;
+}
+
 static int session_set_anonymous_chunk(struct relay_session *session)
 {
        int ret = 0;
index c69002f3456c33de8ea6ff33fef7610d5745d3b2..d2d2503d9f42962e5a83371b22309ef19c13001a 100644 (file)
@@ -114,3 +114,35 @@ int time_to_iso8601_str(time_t time, char *str, size_t len)
 end:
        return ret;
 }
+
+LTTNG_HIDDEN
+int time_to_datetime_str(time_t time, char *str, size_t len)
+{
+       int ret = 0;
+       struct tm *tm_result;
+       struct tm tm_storage;
+       size_t strf_ret;
+
+       if (len < DATETIME_STR_LEN) {
+               ERR("Buffer too short to format to datetime: %zu bytes provided when at least %zu are needed",
+                               len, DATETIME_STR_LEN);
+               ret = -1;
+               goto end;
+       }
+
+       tm_result = localtime_r(&time, &tm_storage);
+       if (!tm_result) {
+               ret = -1;
+               PERROR("Failed to break down timestamp to tm structure");
+               goto end;
+       }
+
+       strf_ret = strftime(str, len, "%Y%m%d-%H%M%S", tm_result);
+       if (strf_ret == 0) {
+               ret = -1;
+               ERR("Failed to format timestamp as local time");
+               goto end;
+       }
+end:
+       return ret;
+}
index 4cfdf856c521d155fe55dd7580d5180605097119..8588cf554c487912875253cd81f1631529bda38a 100644 (file)
@@ -38,6 +38,7 @@
 #define USEC_PER_HOURS  (USEC_PER_MINUTE * MINUTE_PER_HOUR)
 
 #define ISO8601_STR_LEN sizeof("YYYYmmddTHHMMSS+HHMM")
+#define DATETIME_STR_LEN sizeof("YYYYmmdd-HHMMSS")
 
 LTTNG_HIDDEN
 bool locale_supports_utf8(void);
@@ -74,4 +75,7 @@ struct timespec timespec_abs_diff(struct timespec ts_a, struct timespec ts_b);
 LTTNG_HIDDEN
 int time_to_iso8601_str(time_t time, char *str, size_t len);
 
+LTTNG_HIDDEN
+int time_to_datetime_str(time_t time, char *str, size_t len);
+
 #endif /* LTTNG_TIME_H */
This page took 0.03798 seconds and 4 git commands to generate.