journal: properly serialize fields with multiple values into JSON

This now matches the JSON serialization spec from:

http://www.freedesktop.org/wiki/Software/systemd/json
This commit is contained in:
Lennart Poettering 2012-10-25 01:19:24 +02:00
parent cae356ad49
commit d99ae53a73
5 changed files with 192 additions and 22 deletions

View File

@ -212,7 +212,10 @@
information). <literal>json</literal>
formats entries as JSON data
structures, one per
line. <literal>json-pretty</literal>
line (see <ulink
url="http://www.freedesktop.org/wiki/Software/systemd/json">Journal
JSON Format</ulink> for more
information). <literal>json-pretty</literal>
also formats entries as JSON data
structures, but formats them in
multiple lines in order to make them

View File

@ -47,5 +47,15 @@ int main(int argc, char *argv[]) {
huge,
NULL);
sd_journal_send("MESSAGE=uiui",
"VALUE=A",
"VALUE=B",
"VALUE=C",
"SINGLETON=1",
"OTHERVALUE=X",
"OTHERVALUE=Y",
"WITH_BINARY=this is a binary value \a",
NULL);
return 0;
}

View File

@ -328,7 +328,8 @@ int hashmap_put(Hashmap *h, const void *key, void *value) {
hash = h->hash_func(key) % NBUCKETS;
if ((e = hash_scan(h, hash, key))) {
e = hash_scan(h, hash, key);
if (e) {
if (e->value == value)
return 0;
@ -359,8 +360,8 @@ int hashmap_replace(Hashmap *h, const void *key, void *value) {
assert(h);
hash = h->hash_func(key) % NBUCKETS;
if ((e = hash_scan(h, hash, key))) {
e = hash_scan(h, hash, key);
if (e) {
e->key = key;
e->value = value;
return 0;
@ -369,6 +370,21 @@ int hashmap_replace(Hashmap *h, const void *key, void *value) {
return hashmap_put(h, key, value);
}
int hashmap_update(Hashmap *h, const void *key, void *value) {
struct hashmap_entry *e;
unsigned hash;
assert(h);
hash = h->hash_func(key) % NBUCKETS;
e = hash_scan(h, hash, key);
if (!e)
return -ENOENT;
e->value = value;
return 0;
}
void* hashmap_get(Hashmap *h, const void *key) {
unsigned hash;
struct hashmap_entry *e;
@ -384,6 +400,24 @@ void* hashmap_get(Hashmap *h, const void *key) {
return e->value;
}
void* hashmap_get2(Hashmap *h, const void *key, void **key2) {
unsigned hash;
struct hashmap_entry *e;
if (!h)
return NULL;
hash = h->hash_func(key) % NBUCKETS;
e = hash_scan(h, hash, key);
if (!e)
return NULL;
if (key2)
*key2 = (void*) e->key;
return e->value;
}
bool hashmap_contains(Hashmap *h, const void *key) {
unsigned hash;

View File

@ -51,8 +51,10 @@ Hashmap *hashmap_copy(Hashmap *h);
int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func);
int hashmap_put(Hashmap *h, const void *key, void *value);
int hashmap_update(Hashmap *h, const void *key, void *value);
int hashmap_replace(Hashmap *h, const void *key, void *value);
void* hashmap_get(Hashmap *h, const void *key);
void* hashmap_get2(Hashmap *h, const void *key, void **rkey);
bool hashmap_contains(Hashmap *h, const void *key);
void* hashmap_remove(Hashmap *h, const void *key);
void* hashmap_remove_value(Hashmap *h, const void *key, void *value);

View File

@ -29,6 +29,7 @@
#include "log.h"
#include "util.h"
#include "utf8.h"
#include "hashmap.h"
#define PRINT_THRESHOLD 128
#define JSON_THRESHOLD 4096
@ -464,12 +465,14 @@ static int output_json(
OutputFlags flags) {
uint64_t realtime, monotonic;
char *cursor;
char *cursor, *k;
const void *data;
size_t length;
sd_id128_t boot_id;
char sid[33];
int r;
Hashmap *h = NULL;
bool done, separator;
assert(j);
@ -518,31 +521,141 @@ static int output_json(
}
free(cursor);
SD_JOURNAL_FOREACH_DATA(j, data, length) {
const char *c;
h = hashmap_new(string_hash_func, string_compare_func);
if (!h)
return -ENOMEM;
/* First round, iterate through the entry and count how often each field appears */
SD_JOURNAL_FOREACH_DATA(j, data, length) {
const char *eq;
char *n;
unsigned u;
/* We already printed the boot id, from the data in
* the header, hence let's suppress it here */
if (length >= 9 &&
memcmp(data, "_BOOT_ID=", 9) == 0)
continue;
c = memchr(data, '=', length);
if (!c) {
log_error("Invalid field.");
return -EINVAL;
eq = memchr(data, '=', length);
if (!eq)
continue;
n = strndup(data, eq - (const char*) data);
if (!n) {
r = -ENOMEM;
goto finish;
}
if (mode == OUTPUT_JSON_PRETTY)
fputs(",\n\t", f);
else
fputs(", ", f);
json_escape(f, data, c - (const char*) data, flags);
fputs(" : ", f);
json_escape(f, c + 1, length - (c - (const char*) data) - 1, flags);
u = PTR_TO_UINT(hashmap_get(h, n));
if (u == 0) {
r = hashmap_put(h, n, UINT_TO_PTR(1));
if (r < 0) {
free(n);
goto finish;
}
} else {
r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
free(n);
if (r < 0)
goto finish;
}
}
separator = true;
do {
done = true;
SD_JOURNAL_FOREACH_DATA(j, data, length) {
const char *eq;
char *kk, *n;
size_t m;
unsigned u;
/* We already printed the boot id, from the data in
* the header, hence let's suppress it here */
if (length >= 9 &&
memcmp(data, "_BOOT_ID=", 9) == 0)
continue;
eq = memchr(data, '=', length);
if (!eq)
continue;
if (separator) {
if (mode == OUTPUT_JSON_PRETTY)
fputs(",\n\t", f);
else
fputs(", ", f);
}
m = eq - (const char*) data;
n = strndup(data, m);
if (!n) {
r = -ENOMEM;
goto finish;
}
u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
if (u == 0) {
/* We already printed this, let's jump to the next */
free(n);
separator = false;
continue;
} else if (u == 1) {
/* Field only appears once, output it directly */
json_escape(f, data, m, flags);
fputs(" : ", f);
json_escape(f, eq + 1, length - m - 1, flags);
hashmap_remove(h, n);
free(kk);
free(n);
separator = true;
continue;
} else {
/* Field appears multiple times, output it as array */
json_escape(f, data, m, flags);
fputs(" : [ ", f);
json_escape(f, eq + 1, length - m - 1, flags);
/* Iterate through the end of the list */
while (sd_journal_enumerate_data(j, &data, &length) > 0) {
if (length < m + 1)
continue;
if (memcmp(data, n, m) != 0)
continue;
if (((const char*) data)[m] != '=')
continue;
fputs(", ", f);
json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
}
fputs(" ]", f);
hashmap_remove(h, n);
free(kk);
free(n);
/* Iterate data fields form the beginning */
done = false;
separator = true;
break;
}
}
} while (!done);
if (mode == OUTPUT_JSON_PRETTY)
fputs("\n}\n", f);
else if (mode == OUTPUT_JSON_SSE)
@ -550,7 +663,15 @@ static int output_json(
else
fputs(" }\n", f);
return 0;
r = 0;
finish:
while ((k = hashmap_steal_first_key(h)))
free(k);
hashmap_free(h);
return r;
}
static int output_cat(