Skip to content

Commit 1c3994e

Browse files
committed
Implement streaming json output
Currently when enabling json output, the results are only written once the test concludes. This is done to output one full json document containing all relevant informations. To allow status output during the run while using json as output, this patch adds a newline-delimited JSON output. In order to achive this multiple event objects are emitted. These are serialized as json and printed with a newline seperating them. Each event contains a event name and its data. The following events have been introduced: start, interval, end, error, server_output_text and server_output_json. The data contains the relevant portion of the normal JSON output.
1 parent 21581a7 commit 1c3994e

File tree

6 files changed

+90
-4
lines changed

6 files changed

+90
-4
lines changed

src/iperf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ struct iperf_test
300300
int bidirectional; /* --bidirectional */
301301
int verbose; /* -V option - verbose mode */
302302
int json_output; /* -J option - JSON output */
303+
int json_stream; /* --json-stream */
303304
int zerocopy; /* -Z option - use sendfile */
304305
int debug; /* -d option - enable debug */
305306
int get_server_output; /* --get-server-output */

src/iperf3.1

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,11 @@ test by specifying the --get-server-output flag.
9696
Either the client or the server can produce its output in a JSON
9797
structure, useful for integration with other programs, by passing it
9898
the -J flag.
99-
Because the contents of the JSON structure are only competely known
99+
Normally the contents of the JSON structure are only competely known
100100
after the test has finished, no JSON output will be emitted until the
101101
end of the test.
102+
By enabling line-delimited JSON multiple objects will be emitted to
103+
provide a real-time parsable JSON output.
102104
.PP
103105
iperf3 has a (overly) large set of command-line options that can be
104106
used to set the parameters of a test.
@@ -150,6 +152,9 @@ give more detailed output
150152
.BR -J ", " --json " "
151153
output in JSON format
152154
.TP
155+
.BR --json-stream " "
156+
output in line-delimited JSON format
157+
.TP
153158
.BR --logfile " \fIfile\fR"
154159
send output to a log file.
155160
.TP

src/iperf_api.c

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ static int diskfile_recv(struct iperf_stream *sp);
103103
static int JSON_write(int fd, cJSON *json);
104104
static void print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON *json_interval_streams);
105105
static cJSON *JSON_read(int fd);
106+
static int JSONStream_Output(struct iperf_test *test, const char* event_name, cJSON* obj);
106107

107108

108109
/*************************** Print usage functions ****************************/
@@ -315,6 +316,12 @@ iperf_get_test_json_output_string(struct iperf_test *ipt)
315316
return ipt->json_output_string;
316317
}
317318

319+
int
320+
iperf_get_test_json_stream(struct iperf_test *ipt)
321+
{
322+
return ipt->json_stream;
323+
}
324+
318325
int
319326
iperf_get_test_zerocopy(struct iperf_test *ipt)
320327
{
@@ -599,6 +606,12 @@ iperf_set_test_json_output(struct iperf_test *ipt, int json_output)
599606
ipt->json_output = json_output;
600607
}
601608

609+
void
610+
iperf_set_test_json_stream(struct iperf_test *ipt, int json_stream)
611+
{
612+
ipt->json_stream = json_stream;
613+
}
614+
602615
int
603616
iperf_has_zerocopy( void )
604617
{
@@ -779,8 +792,12 @@ iperf_on_test_start(struct iperf_test *test)
779792
iperf_printf(test, test_start_time, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->duration, test->settings->tos);
780793
}
781794
}
795+
if (test->json_stream) {
796+
JSONStream_Output(test, "start", test->json_start);
797+
}
782798
}
783799

800+
784801
/* This converts an IPv6 string address from IPv4-mapped format into regular
785802
** old IPv4 format, which is easier on the eyes of network veterans.
786803
**
@@ -890,6 +907,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
890907
{"one-off", no_argument, NULL, '1'},
891908
{"verbose", no_argument, NULL, 'V'},
892909
{"json", no_argument, NULL, 'J'},
910+
{"json-stream", no_argument, NULL, OPT_JSON_STREAM},
893911
{"version", no_argument, NULL, 'v'},
894912
{"server", no_argument, NULL, 's'},
895913
{"client", required_argument, NULL, 'c'},
@@ -1030,6 +1048,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
10301048
case 'J':
10311049
test->json_output = 1;
10321050
break;
1051+
case OPT_JSON_STREAM:
1052+
test->json_output = 1;
1053+
test->json_stream = 1;
1054+
break;
10331055
case 'v':
10341056
printf("%s (cJSON %s)\n%s\n%s\n", version, cJSON_Version(), get_system_info(),
10351057
get_optional_features());
@@ -2404,6 +2426,29 @@ JSON_read(int fd)
24042426
return json;
24052427
}
24062428

2429+
/*************************************************************/
2430+
/**
2431+
* JSONStream_Output - outputs an obj as event without distrubing it
2432+
*/
2433+
2434+
static int
2435+
JSONStream_Output(struct iperf_test * test, const char * event_name, cJSON * obj)
2436+
{
2437+
cJSON *event = cJSON_CreateObject();
2438+
if (!event)
2439+
return -1;
2440+
cJSON_AddStringToObject(event, "event", event_name);
2441+
cJSON_AddItemReferenceToObject(event, "data", obj);
2442+
char *str = cJSON_PrintUnformatted(event);
2443+
if (str == NULL)
2444+
return -1;
2445+
fprintf(test->outfile, "%s\n", str);
2446+
iflush(test);
2447+
cJSON_free(str);
2448+
cJSON_Delete(event);
2449+
return 0;
2450+
}
2451+
24072452
/*************************************************************/
24082453
/**
24092454
* add_to_interval_list -- adds new interval to the interval_list
@@ -3031,6 +3076,7 @@ iperf_print_intermediate(struct iperf_test *test)
30313076

30323077
int lower_mode, upper_mode;
30333078
int current_mode;
3079+
int discard_json;
30343080

30353081
/*
30363082
* Due to timing oddities, there can be cases, especially on the
@@ -3076,11 +3122,20 @@ iperf_print_intermediate(struct iperf_test *test)
30763122
return;
30773123
}
30783124

3125+
/*
3126+
* When we use streamed json, we don't actually need to keep the interval
3127+
* results around unless we're the server and the client requested the server output.
3128+
*
3129+
* This avoids unneeded memory build up for long sessions.
3130+
*/
3131+
discard_json = test->json_stream == 1 && !(test->role == 's' && test->get_server_output);
3132+
30793133
if (test->json_output) {
30803134
json_interval = cJSON_CreateObject();
30813135
if (json_interval == NULL)
30823136
return;
3083-
cJSON_AddItemToArray(test->json_intervals, json_interval);
3137+
if (!discard_json)
3138+
cJSON_AddItemToArray(test->json_intervals, json_interval);
30843139
json_interval_streams = cJSON_CreateArray();
30853140
if (json_interval_streams == NULL)
30863141
return;
@@ -3213,6 +3268,11 @@ iperf_print_intermediate(struct iperf_test *test)
32133268
}
32143269
}
32153270
}
3271+
3272+
if (test->json_stream)
3273+
JSONStream_Output(test, "interval", json_interval);
3274+
if (discard_json)
3275+
cJSON_Delete(json_interval);
32163276
}
32173277

32183278
/**
@@ -4282,8 +4342,23 @@ iperf_json_finish(struct iperf_test *test)
42824342
cJSON_free(str);
42834343
if (test->json_output_string == NULL)
42844344
return -1;
4285-
fprintf(test->outfile, "%s\n", test->json_output_string);
4286-
iflush(test);
4345+
if (test->json_stream) {
4346+
cJSON *error = cJSON_GetObjectItem(test->json_top, "error");
4347+
if (error) {
4348+
JSONStream_Output(test, "error", error);
4349+
}
4350+
if (test->json_server_output) {
4351+
JSONStream_Output(test, "server_output_json", test->json_server_output);
4352+
}
4353+
if (test->server_output_text) {
4354+
JSONStream_Output(test, "server_output_text", cJSON_CreateString(test->server_output_text));
4355+
}
4356+
JSONStream_Output(test, "end", test->json_end);
4357+
}
4358+
else {
4359+
fprintf(test->outfile, "%s\n", test->json_output_string);
4360+
iflush(test);
4361+
}
42874362
cJSON_Delete(test->json_top);
42884363
test->json_top = test->json_start = test->json_connected = test->json_intervals = test->json_server_output = test->json_end = NULL;
42894364
return 0;

src/iperf_api.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ typedef uint64_t iperf_size_t;
8181
#define OPT_TIMESTAMPS 22
8282
#define OPT_SERVER_SKEW_THRESHOLD 23
8383
#define OPT_BIND_DEV 24
84+
#define OPT_JSON_STREAM 25
8485

8586
/* states */
8687
#define TEST_START 1
@@ -129,6 +130,7 @@ char* iperf_get_test_template( struct iperf_test* ipt );
129130
int iperf_get_test_protocol_id( struct iperf_test* ipt );
130131
int iperf_get_test_json_output( struct iperf_test* ipt );
131132
char* iperf_get_test_json_output_string ( struct iperf_test* ipt );
133+
int iperf_get_test_json_stream( struct iperf_test* ipt );
132134
int iperf_get_test_zerocopy( struct iperf_test* ipt );
133135
int iperf_get_test_get_server_output( struct iperf_test* ipt );
134136
char* iperf_get_test_bind_address ( struct iperf_test* ipt );
@@ -166,6 +168,7 @@ void iperf_set_test_server_hostname( struct iperf_test* ipt, const char* server_
166168
void iperf_set_test_template( struct iperf_test *ipt, const char *tmp_template );
167169
void iperf_set_test_reverse( struct iperf_test* ipt, int reverse );
168170
void iperf_set_test_json_output( struct iperf_test* ipt, int json_output );
171+
void iperf_set_test_json_stream( struct iperf_test* ipt, int json_stream );
169172
int iperf_has_zerocopy( void );
170173
void iperf_set_test_zerocopy( struct iperf_test* ipt, int zerocopy );
171174
void iperf_set_test_get_server_output( struct iperf_test* ipt, int get_server_output );

src/iperf_locale.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
110110
#endif /* HAVE_SO_BINDTODEVICE */
111111
" -V, --verbose more detailed output\n"
112112
" -J, --json output in JSON format\n"
113+
" --json-stream output in line-delimited JSON format\n"
113114
" --logfile f send output to a log file\n"
114115
" --forceflush force flushing output at every interval\n"
115116
" --timestamps<=format> emit a timestamp at the start of each output line\n"

src/libiperf.3

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Setting test parameters:
3232
void iperf_set_test_blksize( struct iperf_test *t, int blksize );
3333
void iperf_set_test_num_streams( struct iperf_test *t, int num_streams );
3434
void iperf_set_test_json_output( struct iperf_test *t, int json_output );
35+
void iperf_set_test_json_stream( struct iperf_test *t, int json_stream );
3536
int iperf_has_zerocopy( void );
3637
void iperf_set_test_zerocopy( struct iperf_test* t, int zerocopy );
3738
void iperf_set_test_tos( struct iperf_test* t, int tos );

0 commit comments

Comments
 (0)