mirror of
https://github.com/systemd/systemd.git
synced 2024-12-25 01:34:28 +03:00
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:
parent
cae356ad49
commit
d99ae53a73
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user