This commit adds support for attaching extra metadata to log
messages written to the journal via log.h. We keep track of a
thread local log context in log.c onto which we can push extra
metadata fields that should be logged. Once a field is no longer
relevant, it can be popped again from the log context.
On top of this, we then add macros to allow pushing extra fields
onto the log context.
LOG_CONTEXT_PUSH() will push the provided field onto the log context
and pop the last field from the log context when the current block
ends. LOG_CONTEXT_PUSH_STRV() will do the same but for all fields in
the given strv.
Using the macros is as simple as putting them anywhere inside a block
to add a field to all following log messages logged from inside that
block.
void myfunction(...) {
...
LOG_CONTEXT_PUSH("MYMETADATA=abc");
// Every journal message logged will now have the MYMETADATA=abc
// field included.
}
For convenience, there's also LOG_CONTEXT_PUSHF() to allow constructing
the field to be logged using printf() syntax.
log_context_new()/log_context_free() can be used to attach a log context
to an async operation by storing it in the associated userdata struct.
195 lines
11 KiB
C
195 lines
11 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
#pragma once
|
|
|
|
/* The head of the linked list. Use this in the structure that shall
|
|
* contain the head of the linked list */
|
|
#define LIST_HEAD(t,name) \
|
|
t *name
|
|
|
|
/* The pointers in the linked list's items. Use this in the item structure */
|
|
#define LIST_FIELDS(t,name) \
|
|
t *name##_next, *name##_prev
|
|
|
|
/* Initialize the list's head */
|
|
#define LIST_HEAD_INIT(head) \
|
|
do { \
|
|
(head) = NULL; \
|
|
} while (false)
|
|
|
|
/* Initialize a list item */
|
|
#define LIST_INIT(name,item) \
|
|
do { \
|
|
typeof(*(item)) *_item = (item); \
|
|
assert(_item); \
|
|
_item->name##_prev = _item->name##_next = NULL; \
|
|
} while (false)
|
|
|
|
/* Prepend an item to the list */
|
|
#define LIST_PREPEND(name,head,item) \
|
|
({ \
|
|
typeof(*(head)) **_head = &(head), *_item = (item); \
|
|
assert(_item); \
|
|
if ((_item->name##_next = *_head)) \
|
|
_item->name##_next->name##_prev = _item; \
|
|
_item->name##_prev = NULL; \
|
|
*_head = _item; \
|
|
_item; \
|
|
})
|
|
|
|
/* Append an item to the list */
|
|
#define LIST_APPEND(name,head,item) \
|
|
({ \
|
|
typeof(*(head)) **_hhead = &(head), *_tail; \
|
|
_tail = LIST_FIND_TAIL(name, *_hhead); \
|
|
LIST_INSERT_AFTER(name, *_hhead, _tail, item); \
|
|
})
|
|
|
|
/* Remove an item from the list */
|
|
#define LIST_REMOVE(name,head,item) \
|
|
({ \
|
|
typeof(*(head)) **_head = &(head), *_item = (item); \
|
|
assert(_item); \
|
|
if (_item->name##_next) \
|
|
_item->name##_next->name##_prev = _item->name##_prev; \
|
|
if (_item->name##_prev) \
|
|
_item->name##_prev->name##_next = _item->name##_next; \
|
|
else { \
|
|
assert(*_head == _item); \
|
|
*_head = _item->name##_next; \
|
|
} \
|
|
_item->name##_next = _item->name##_prev = NULL; \
|
|
_item; \
|
|
})
|
|
|
|
/* Find the head of the list */
|
|
#define LIST_FIND_HEAD(name,item) \
|
|
({ \
|
|
typeof(*(item)) *_item = (item); \
|
|
while (_item && _item->name##_prev) \
|
|
_item = _item->name##_prev; \
|
|
_item; \
|
|
})
|
|
|
|
/* Find the tail of the list */
|
|
#define LIST_FIND_TAIL(name,item) \
|
|
({ \
|
|
typeof(*(item)) *_item = (item); \
|
|
while (_item && _item->name##_next) \
|
|
_item = _item->name##_next; \
|
|
_item; \
|
|
})
|
|
|
|
/* Insert an item after another one (a = where, b = what) */
|
|
#define LIST_INSERT_AFTER(name,head,a,b) \
|
|
({ \
|
|
typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
|
|
assert(_b); \
|
|
if (!_a) { \
|
|
if ((_b->name##_next = *_head)) \
|
|
_b->name##_next->name##_prev = _b; \
|
|
_b->name##_prev = NULL; \
|
|
*_head = _b; \
|
|
} else { \
|
|
if ((_b->name##_next = _a->name##_next)) \
|
|
_b->name##_next->name##_prev = _b; \
|
|
_b->name##_prev = _a; \
|
|
_a->name##_next = _b; \
|
|
} \
|
|
_b; \
|
|
})
|
|
|
|
/* Insert an item before another one (a = where, b = what) */
|
|
#define LIST_INSERT_BEFORE(name,head,a,b) \
|
|
({ \
|
|
typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
|
|
assert(_b); \
|
|
if (!_a) { \
|
|
if (!*_head) { \
|
|
_b->name##_next = NULL; \
|
|
_b->name##_prev = NULL; \
|
|
*_head = _b; \
|
|
} else { \
|
|
typeof(*(head)) *_tail = (head); \
|
|
while (_tail->name##_next) \
|
|
_tail = _tail->name##_next; \
|
|
_b->name##_next = NULL; \
|
|
_b->name##_prev = _tail; \
|
|
_tail->name##_next = _b; \
|
|
} \
|
|
} else { \
|
|
if ((_b->name##_prev = _a->name##_prev)) \
|
|
_b->name##_prev->name##_next = _b; \
|
|
else \
|
|
*_head = _b; \
|
|
_b->name##_next = _a; \
|
|
_a->name##_prev = _b; \
|
|
} \
|
|
_b; \
|
|
})
|
|
|
|
#define LIST_JUST_US(name,item) \
|
|
(!(item)->name##_prev && !(item)->name##_next)
|
|
|
|
/* The type of the iterator 'i' is automatically determined by the type of 'head', and declared in the
|
|
* loop. Hence, do not declare the same variable in the outer scope. Sometimes, we set 'head' through
|
|
* hashmap_get(). In that case, you need to explicitly cast the result. */
|
|
#define LIST_FOREACH_WITH_NEXT(name,i,n,head) \
|
|
for (typeof(*(head)) *n, *i = (head); i && (n = i->name##_next, true); i = n)
|
|
|
|
#define LIST_FOREACH(name,i,head) \
|
|
LIST_FOREACH_WITH_NEXT(name, i, UNIQ_T(n, UNIQ), head)
|
|
|
|
#define _LIST_FOREACH_WITH_PREV(name,i,p,start) \
|
|
for (typeof(*(start)) *p, *i = (start); i && (p = i->name##_prev, true); i = p)
|
|
|
|
#define LIST_FOREACH_BACKWARDS(name,i,start) \
|
|
_LIST_FOREACH_WITH_PREV(name, i, UNIQ_T(p, UNIQ), start)
|
|
|
|
/* Iterate through all the members of the list p is included in, but skip over p */
|
|
#define LIST_FOREACH_OTHERS(name,i,p) \
|
|
for (typeof(*(p)) *_p = (p), *i = ({ \
|
|
typeof(*_p) *_j = _p; \
|
|
while (_j && _j->name##_prev) \
|
|
_j = _j->name##_prev; \
|
|
if (_j == _p) \
|
|
_j = _p->name##_next; \
|
|
_j; \
|
|
}); \
|
|
i; \
|
|
i = i->name##_next == _p ? _p->name##_next : i->name##_next)
|
|
|
|
/* Loop starting from p->next until p->prev. p can be adjusted meanwhile. */
|
|
#define LIST_LOOP_BUT_ONE(name,i,head,p) \
|
|
for (typeof(*(p)) *i = (p)->name##_next ? (p)->name##_next : (head); \
|
|
i != (p); \
|
|
i = i->name##_next ? i->name##_next : (head))
|
|
|
|
/* Join two lists tail to head: a->b, c->d to a->b->c->d and de-initialise second list */
|
|
#define LIST_JOIN(name,a,b) \
|
|
({ \
|
|
assert(b); \
|
|
if (!(a)) \
|
|
(a) = (b); \
|
|
else { \
|
|
typeof(*(a)) *_head = (b), *_tail; \
|
|
_tail = LIST_FIND_TAIL(name, (a)); \
|
|
_tail->name##_next = _head; \
|
|
_head->name##_prev = _tail; \
|
|
} \
|
|
(b) = NULL; \
|
|
a; \
|
|
})
|
|
|
|
#define LIST_POP(name, a) \
|
|
({ \
|
|
typeof(a)* _a = &(a); \
|
|
typeof(a) _p = *_a; \
|
|
if (_p) \
|
|
LIST_REMOVE(name, *_a, _p); \
|
|
_p; \
|
|
})
|
|
|
|
/* Now include "macro.h", because we want our definition of assert() which the macros above use. We include
|
|
* it down here instead of up top, since macro.h pulls in log.h which in turn needs our own definitions. */
|
|
#include "macro.h"
|