Skip to content

Commit a4bff6e

Browse files
committed
basic: Move log context functions to log-context.h
1 parent 40d6072 commit a4bff6e

File tree

10 files changed

+318
-288
lines changed

10 files changed

+318
-288
lines changed

src/basic/log-context.c

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
3+
#include <threads.h>
4+
5+
#include "env-util.h"
6+
#include "iovec-util.h"
7+
#include "log.h"
8+
#include "log-context.h"
9+
#include "strv.h"
10+
11+
static int saved_log_context_enabled = -1;
12+
thread_local LIST_HEAD(LogContext, _log_context) = NULL;
13+
thread_local size_t _log_context_num_fields = 0;
14+
15+
bool log_context_enabled(void) {
16+
int r;
17+
18+
if (log_get_max_level() == LOG_DEBUG)
19+
return true;
20+
21+
if (saved_log_context_enabled >= 0)
22+
return saved_log_context_enabled;
23+
24+
r = secure_getenv_bool("SYSTEMD_ENABLE_LOG_CONTEXT");
25+
if (r < 0 && r != -ENXIO)
26+
log_debug_errno(r, "Failed to parse $SYSTEMD_ENABLE_LOG_CONTEXT, ignoring: %m");
27+
28+
saved_log_context_enabled = r > 0;
29+
30+
return saved_log_context_enabled;
31+
}
32+
33+
static LogContext* log_context_attach(LogContext *c) {
34+
assert(c);
35+
36+
_log_context_num_fields += strv_length(c->fields);
37+
_log_context_num_fields += c->n_input_iovec;
38+
_log_context_num_fields += !!c->key;
39+
40+
return LIST_PREPEND(ll, _log_context, c);
41+
}
42+
43+
static LogContext* log_context_detach(LogContext *c) {
44+
if (!c)
45+
return NULL;
46+
47+
assert(_log_context_num_fields >= strv_length(c->fields) + c->n_input_iovec +!!c->key);
48+
_log_context_num_fields -= strv_length(c->fields);
49+
_log_context_num_fields -= c->n_input_iovec;
50+
_log_context_num_fields -= !!c->key;
51+
52+
LIST_REMOVE(ll, _log_context, c);
53+
return NULL;
54+
}
55+
56+
LogContext* log_context_new(const char *key, const char *value) {
57+
assert(key);
58+
assert(endswith(key, "="));
59+
assert(value);
60+
61+
LIST_FOREACH(ll, i, _log_context)
62+
if (i->key == key && i->value == value)
63+
return log_context_ref(i);
64+
65+
LogContext *c = new(LogContext, 1);
66+
if (!c)
67+
return NULL;
68+
69+
*c = (LogContext) {
70+
.n_ref = 1,
71+
.key = (char *) key,
72+
.value = (char *) value,
73+
};
74+
75+
return log_context_attach(c);
76+
}
77+
78+
LogContext* log_context_new_strv(char **fields, bool owned) {
79+
if (!fields)
80+
return NULL;
81+
82+
LIST_FOREACH(ll, i, _log_context)
83+
if (i->fields == fields) {
84+
assert(!owned);
85+
return log_context_ref(i);
86+
}
87+
88+
LogContext *c = new(LogContext, 1);
89+
if (!c)
90+
return NULL;
91+
92+
*c = (LogContext) {
93+
.n_ref = 1,
94+
.fields = fields,
95+
.owned = owned,
96+
};
97+
98+
return log_context_attach(c);
99+
}
100+
101+
LogContext* log_context_new_iov(struct iovec *input_iovec, size_t n_input_iovec, bool owned) {
102+
if (!input_iovec || n_input_iovec == 0)
103+
return NULL;
104+
105+
LIST_FOREACH(ll, i, _log_context)
106+
if (i->input_iovec == input_iovec && i->n_input_iovec == n_input_iovec) {
107+
assert(!owned);
108+
return log_context_ref(i);
109+
}
110+
111+
LogContext *c = new(LogContext, 1);
112+
if (!c)
113+
return NULL;
114+
115+
*c = (LogContext) {
116+
.n_ref = 1,
117+
.input_iovec = input_iovec,
118+
.n_input_iovec = n_input_iovec,
119+
.owned = owned,
120+
};
121+
122+
return log_context_attach(c);
123+
}
124+
125+
static LogContext* log_context_free(LogContext *c) {
126+
if (!c)
127+
return NULL;
128+
129+
log_context_detach(c);
130+
131+
if (c->owned) {
132+
strv_free(c->fields);
133+
iovec_array_free(c->input_iovec, c->n_input_iovec);
134+
free(c->key);
135+
free(c->value);
136+
}
137+
138+
return mfree(c);
139+
}
140+
141+
DEFINE_TRIVIAL_REF_UNREF_FUNC(LogContext, log_context, log_context_free);
142+
143+
LogContext* log_context_new_strv_consume(char **fields) {
144+
LogContext *c = log_context_new_strv(fields, /*owned=*/ true);
145+
if (!c)
146+
strv_free(fields);
147+
148+
return c;
149+
}
150+
151+
LogContext* log_context_new_iov_consume(struct iovec *input_iovec, size_t n_input_iovec) {
152+
LogContext *c = log_context_new_iov(input_iovec, n_input_iovec, /*owned=*/ true);
153+
if (!c)
154+
iovec_array_free(input_iovec, n_input_iovec);
155+
156+
return c;
157+
}
158+
159+
LogContext* log_context_head(void) {
160+
return _log_context;
161+
}
162+
163+
size_t log_context_num_contexts(void) {
164+
size_t n = 0;
165+
166+
LIST_FOREACH(ll, c, _log_context)
167+
n++;
168+
169+
return n;
170+
}
171+
172+
size_t log_context_num_fields(void) {
173+
return _log_context_num_fields;
174+
}
175+
176+
void _reset_log_level(int *saved_log_level) {
177+
assert(saved_log_level);
178+
179+
log_set_max_level(*saved_log_level);
180+
}

src/basic/log-context.h

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
#pragma once
3+
4+
#include <stdbool.h>
5+
#include <stddef.h>
6+
7+
#include "macro.h"
8+
9+
/*
10+
* The log context allows attaching extra metadata to log messages written to the journal via log.h. We keep
11+
* track of a thread local log context onto which we can push extra metadata fields that should be logged.
12+
*
13+
* LOG_CONTEXT_PUSH() will add the provided field to the log context and will remove it again when the
14+
* current block ends. LOG_CONTEXT_PUSH_STRV() will do the same but for all fields in the given strv.
15+
* LOG_CONTEXT_PUSHF() is like LOG_CONTEXT_PUSH() but takes a format string and arguments.
16+
*
17+
* Using the macros is as simple as putting them anywhere inside a block to add a field to all following log
18+
* messages logged from inside that block.
19+
*
20+
* void myfunction(...) {
21+
* ...
22+
*
23+
* LOG_CONTEXT_PUSHF("MYMETADATA=%s", "abc");
24+
*
25+
* // Every journal message logged will now have the MYMETADATA=abc
26+
* // field included.
27+
* }
28+
*
29+
* One special case to note is async code, where we use callbacks that are invoked to continue processing
30+
* when some event occurs. For async code, there's usually an associated "userdata" struct containing all the
31+
* information associated with the async operation. In this "userdata" struct, we can store a log context
32+
* allocated with log_context_new() and freed with log_context_free(). We can then add and remove fields to
33+
* the `fields` member of the log context object and all those fields will be logged along with each log
34+
* message.
35+
*/
36+
37+
struct iovec;
38+
39+
typedef struct LogContext {
40+
unsigned n_ref;
41+
/* Depending on which destructor is used (log_context_free() or log_context_detach()) the memory
42+
* referenced by this is freed or not */
43+
char **fields;
44+
struct iovec *input_iovec;
45+
size_t n_input_iovec;
46+
char *key;
47+
char *value;
48+
bool owned;
49+
LIST_FIELDS(struct LogContext, ll);
50+
} LogContext;
51+
52+
bool log_context_enabled(void);
53+
54+
LogContext* log_context_new(const char *key, const char *value);
55+
LogContext* log_context_new_strv(char **fields, bool owned);
56+
LogContext* log_context_new_iov(struct iovec *input_iovec, size_t n_input_iovec, bool owned);
57+
58+
/* Same as log_context_new(), but frees the given fields strv/iovec on failure. */
59+
LogContext* log_context_new_strv_consume(char **fields);
60+
LogContext* log_context_new_iov_consume(struct iovec *input_iovec, size_t n_input_iovec);
61+
62+
LogContext *log_context_ref(LogContext *c);
63+
LogContext *log_context_unref(LogContext *c);
64+
65+
DEFINE_TRIVIAL_CLEANUP_FUNC(LogContext*, log_context_unref);
66+
67+
/* Returns the head of the log context list. */
68+
LogContext* log_context_head(void);
69+
/* Returns the number of attached log context objects. */
70+
size_t log_context_num_contexts(void);
71+
/* Returns the number of fields in all attached log contexts. */
72+
size_t log_context_num_fields(void);
73+
74+
void _reset_log_level(int *saved_log_level);
75+
76+
#define LOG_CONTEXT_SET_LOG_LEVEL(level) \
77+
_cleanup_(_reset_log_level) _unused_ int _saved_log_level_ = log_set_max_level(level);
78+
79+
#define LOG_CONTEXT_PUSH(...) \
80+
LOG_CONTEXT_PUSH_STRV(STRV_MAKE(__VA_ARGS__))
81+
82+
#define LOG_CONTEXT_PUSHF(...) \
83+
LOG_CONTEXT_PUSH(snprintf_ok((char[LINE_MAX]) {}, LINE_MAX, __VA_ARGS__))
84+
85+
#define _LOG_CONTEXT_PUSH_KEY_VALUE(key, value, c) \
86+
_unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new(key, value);
87+
88+
#define LOG_CONTEXT_PUSH_KEY_VALUE(key, value) \
89+
_LOG_CONTEXT_PUSH_KEY_VALUE(key, value, UNIQ_T(c, UNIQ))
90+
91+
#define _LOG_CONTEXT_PUSH_STRV(strv, c) \
92+
_unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_strv(strv, /*owned=*/ false);
93+
94+
#define LOG_CONTEXT_PUSH_STRV(strv) \
95+
_LOG_CONTEXT_PUSH_STRV(strv, UNIQ_T(c, UNIQ))
96+
97+
#define _LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec, c) \
98+
_unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_iov(input_iovec, n_input_iovec, /*owned=*/ false);
99+
100+
#define LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec) \
101+
_LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec, UNIQ_T(c, UNIQ))
102+
103+
/* LOG_CONTEXT_CONSUME_STR()/LOG_CONTEXT_CONSUME_STRV()/LOG_CONTEXT_CONSUME_IOV() are identical to
104+
* LOG_CONTEXT_PUSH_STR()/LOG_CONTEXT_PUSH_STRV()/LOG_CONTEXT_PUSH_IOV() except they take ownership of the
105+
* given str/strv argument.
106+
*/
107+
108+
#define _LOG_CONTEXT_CONSUME_STR(s, c, strv) \
109+
_unused_ _cleanup_strv_free_ strv = strv_new(s); \
110+
if (!strv) \
111+
free(s); \
112+
_unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_strv_consume(TAKE_PTR(strv))
113+
114+
#define LOG_CONTEXT_CONSUME_STR(s) \
115+
_LOG_CONTEXT_CONSUME_STR(s, UNIQ_T(c, UNIQ), UNIQ_T(sv, UNIQ))
116+
117+
#define _LOG_CONTEXT_CONSUME_STRV(strv, c) \
118+
_unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_strv_consume(strv);
119+
120+
#define LOG_CONTEXT_CONSUME_STRV(strv) \
121+
_LOG_CONTEXT_CONSUME_STRV(strv, UNIQ_T(c, UNIQ))
122+
123+
#define _LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec, c) \
124+
_unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_iov_consume(input_iovec, n_input_iovec);
125+
126+
#define LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec) \
127+
_LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec, UNIQ_T(c, UNIQ))

0 commit comments

Comments
 (0)