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>
* 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;
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;
*/
#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 */
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;
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;
#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,
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 */
*/
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;
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;
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 */
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];
/* 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
{ "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, },
};
}
}
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 */
}
}
+ 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;
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;
}
* 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;
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;
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;
#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(®ex, DATETIME_REGEX, 0);
+ if (ret) {
+ ERR("Regex compilation failed with %d", ret);
+ goto error;
+ }
+
+ ret = regexec(®ex, 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(®ex);
+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;
*
* 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;
}
* 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 */