From a8b66566890a353cc3a89836281541195ad69d73 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Thu, 19 Sep 2019 16:56:11 -0400 Subject: [PATCH] relayd: introduce --group-output-by-session MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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: /-/ When using "--group-output-per-session", the following hierarchy is found on the filesystem: /-/ 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: // e.g: /extra/path/information/ 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: /-// e.g: /-/extra/path/information/ 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 Signed-off-by: Jérémie Galarneau --- src/bin/lttng-relayd/lttng-relayd.h | 7 ++ src/bin/lttng-relayd/main.c | 21 ++++++ src/bin/lttng-relayd/session.c | 112 +++++++++++++++++++++------- src/common/time.c | 32 ++++++++ src/common/time.h | 4 + 5 files changed, 147 insertions(+), 29 deletions(-) diff --git a/src/bin/lttng-relayd/lttng-relayd.h b/src/bin/lttng-relayd/lttng-relayd.h index 40fecd603..748083ddf 100644 --- a/src/bin/lttng-relayd/lttng-relayd.h +++ b/src/bin/lttng-relayd/lttng-relayd.h @@ -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]; diff --git a/src/bin/lttng-relayd/main.c b/src/bin/lttng-relayd/main.c index 5f72c32a9..e7c5b6c52 100644 --- a/src/bin/lttng-relayd/main.c +++ b/src/bin/lttng-relayd/main.c @@ -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; diff --git a/src/bin/lttng-relayd/session.c b/src/bin/lttng-relayd/session.c index 2f65848a9..05a1dc215 100644 --- a/src/bin/lttng-relayd/session.c +++ b/src/bin/lttng-relayd/session.c @@ -19,8 +19,9 @@ #define _LGPL_SOURCE #include -#include #include +#include +#include #include #include @@ -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; diff --git a/src/common/time.c b/src/common/time.c index c69002f34..d2d2503d9 100644 --- a/src/common/time.c +++ b/src/common/time.c @@ -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; +} diff --git a/src/common/time.h b/src/common/time.h index 4cfdf856c..8588cf554 100644 --- a/src/common/time.h +++ b/src/common/time.h @@ -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 */ -- 2.34.1