1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-11 09:18:07 +03:00

Merge pull request #30417 from YHNdnzj/unit-log-resource

core/unit: clean up unit_log_resources
This commit is contained in:
Luca Boccassi 2023-12-14 08:45:26 +00:00 committed by GitHub
commit 266e8d0eb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 114 additions and 216 deletions

View File

@ -62,8 +62,10 @@ char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const ch
return x;
}
void iovec_array_free(struct iovec *iovec, size_t n) {
FOREACH_ARRAY(i, iovec, n)
void iovec_array_free(struct iovec *iovec, size_t n_iovec) {
assert(iovec || n_iovec == 0);
FOREACH_ARRAY(i, iovec, n_iovec)
free(i->iov_base);
free(iovec);

View File

@ -41,4 +41,4 @@ static inline bool iovec_is_set(const struct iovec *iovec) {
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
void iovec_array_free(struct iovec *iovec, size_t n);
void iovec_array_free(struct iovec *iovec, size_t n_iovec);

View File

@ -67,14 +67,16 @@
#endif
/* Thresholds for logging at INFO level about resource consumption */
#define MENTIONWORTHY_CPU_NSEC (1 * NSEC_PER_SEC)
#define MENTIONWORTHY_IO_BYTES (1024 * 1024ULL)
#define MENTIONWORTHY_IP_BYTES (0ULL)
#define MENTIONWORTHY_CPU_NSEC (1 * NSEC_PER_SEC)
#define MENTIONWORTHY_MEMORY_BYTES (64 * U64_MB)
#define MENTIONWORTHY_IO_BYTES (1 * U64_MB)
#define MENTIONWORTHY_IP_BYTES UINT64_C(0)
/* Thresholds for logging at INFO level about resource consumption */
#define NOTICEWORTHY_CPU_NSEC (10*60 * NSEC_PER_SEC) /* 10 minutes */
#define NOTICEWORTHY_IO_BYTES (10 * 1024 * 1024ULL) /* 10 MB */
#define NOTICEWORTHY_IP_BYTES (128 * 1024 * 1024ULL) /* 128 MB */
/* Thresholds for logging at NOTICE level about resource consumption */
#define NOTICEWORTHY_CPU_NSEC (10 * NSEC_PER_MINUTE)
#define NOTICEWORTHY_MEMORY_BYTES (512 * U64_MB)
#define NOTICEWORTHY_IO_BYTES (10 * U64_MB)
#define NOTICEWORTHY_IP_BYTES (128 * U64_MB)
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = &service_vtable,
@ -2339,273 +2341,167 @@ static int raise_level(int log_level, bool condition_info, bool condition_notice
}
static int unit_log_resources(Unit *u) {
struct iovec iovec[1 + 2 + _CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4];
bool any_traffic = false, have_ip_accounting = false, any_io = false, have_io_accounting = false;
_cleanup_free_ char *igress = NULL, *egress = NULL, *rr = NULL, *wr = NULL;
int log_level = LOG_DEBUG; /* May be raised if resources consumed over a threshold */
size_t n_message_parts = 0, n_iovec = 0;
char* message_parts[1 + 2 + 2 + 2 + 1], *t;
nsec_t nsec = NSEC_INFINITY;
uint64_t memory_peak = UINT64_MAX, memory_swap_peak = UINT64_MAX;
int r;
const char* const ip_fields[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IP_INGRESS_BYTES] = "IP_METRIC_INGRESS_BYTES",
[CGROUP_IP_INGRESS_PACKETS] = "IP_METRIC_INGRESS_PACKETS",
[CGROUP_IP_EGRESS_BYTES] = "IP_METRIC_EGRESS_BYTES",
[CGROUP_IP_EGRESS_PACKETS] = "IP_METRIC_EGRESS_PACKETS",
};
const char* const io_fields[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IO_READ_BYTES] = "IO_METRIC_READ_BYTES",
[CGROUP_IO_WRITE_BYTES] = "IO_METRIC_WRITE_BYTES",
[CGROUP_IO_READ_OPERATIONS] = "IO_METRIC_READ_OPERATIONS",
[CGROUP_IO_WRITE_OPERATIONS] = "IO_METRIC_WRITE_OPERATIONS",
static const struct {
const char *journal_field;
const char *message_suffix;
} memory_fields[_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1] = {
[CGROUP_MEMORY_PEAK] = { "MEMORY_PEAK", "memory peak" },
[CGROUP_MEMORY_SWAP_PEAK] = { "MEMORY_SWAP_PEAK", "memory swap peak" },
}, ip_fields[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IP_INGRESS_BYTES] = { "IP_METRIC_INGRESS_BYTES", "incoming IP traffic" },
[CGROUP_IP_EGRESS_BYTES] = { "IP_METRIC_EGRESS_BYTES", "outgoing IP traffic" },
[CGROUP_IP_INGRESS_PACKETS] = { "IP_METRIC_INGRESS_PACKETS", NULL },
[CGROUP_IP_EGRESS_PACKETS] = { "IP_METRIC_EGRESS_PACKETS", NULL },
}, io_fields[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IO_READ_BYTES] = { "IO_METRIC_READ_BYTES", "read from disk" },
[CGROUP_IO_WRITE_BYTES] = { "IO_METRIC_WRITE_BYTES", "written to disk" },
[CGROUP_IO_READ_OPERATIONS] = { "IO_METRIC_READ_OPERATIONS", NULL },
[CGROUP_IO_WRITE_OPERATIONS] = { "IO_METRIC_WRITE_OPERATIONS", NULL },
};
struct iovec *iovec = NULL;
size_t n_iovec = 0;
_cleanup_free_ char *message = NULL, *t = NULL;
nsec_t cpu_nsec = NSEC_INFINITY;
int log_level = LOG_DEBUG; /* May be raised if resources consumed over a threshold */
assert(u);
CLEANUP_ARRAY(iovec, n_iovec, iovec_array_free);
iovec = new(struct iovec, 1 + (_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1) +
_CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4);
if (!iovec)
return log_oom();
/* Invoked whenever a unit enters failed or dead state. Logs information about consumed resources if resource
* accounting was enabled for a unit. It does this in two ways: a friendly human readable string with reduced
* information and the complete data in structured fields. */
(void) unit_get_cpu_usage(u, &nsec);
if (nsec != NSEC_INFINITY) {
(void) unit_get_cpu_usage(u, &cpu_nsec);
if (cpu_nsec != NSEC_INFINITY) {
/* Format the CPU time for inclusion in the structured log message */
if (asprintf(&t, "CPU_USAGE_NSEC=%" PRIu64, nsec) < 0) {
r = log_oom();
goto finish;
}
iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
if (asprintf(&t, "CPU_USAGE_NSEC=%" PRIu64, cpu_nsec) < 0)
return log_oom();
iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
/* Format the CPU time for inclusion in the human language message string */
t = strjoin("consumed ", FORMAT_TIMESPAN(nsec / NSEC_PER_USEC, USEC_PER_MSEC), " CPU time");
if (!t) {
r = log_oom();
goto finish;
}
message_parts[n_message_parts++] = t;
if (strextendf_with_separator(&message, ", ",
"Consumed %s CPU time",
FORMAT_TIMESPAN(cpu_nsec / NSEC_PER_USEC, USEC_PER_MSEC)) < 0)
return log_oom();
log_level = raise_level(log_level,
nsec > MENTIONWORTHY_CPU_NSEC,
nsec > NOTICEWORTHY_CPU_NSEC);
cpu_nsec > MENTIONWORTHY_CPU_NSEC,
cpu_nsec > NOTICEWORTHY_CPU_NSEC);
}
(void) unit_get_memory_accounting(u, CGROUP_MEMORY_PEAK, &memory_peak);
if (memory_peak != UINT64_MAX) {
/* Format peak memory for inclusion in the structured log message */
if (asprintf(&t, "MEMORY_PEAK=%" PRIu64, memory_peak) < 0) {
r = log_oom();
goto finish;
}
iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
for (CGroupMemoryAccountingMetric metric = 0; metric <= _CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST; metric++) {
uint64_t v = UINT64_MAX;
/* Format peak memory for inclusion in the human language message string */
t = strjoin(FORMAT_BYTES(memory_peak), " memory peak");
if (!t) {
r = log_oom();
goto finish;
}
message_parts[n_message_parts++] = t;
}
assert(memory_fields[metric].journal_field);
assert(memory_fields[metric].message_suffix);
(void) unit_get_memory_accounting(u, CGROUP_MEMORY_SWAP_PEAK, &memory_swap_peak);
if (memory_swap_peak != UINT64_MAX) {
/* Format peak swap memory for inclusion in the structured log message */
if (asprintf(&t, "MEMORY_SWAP_PEAK=%" PRIu64, memory_swap_peak) < 0) {
r = log_oom();
goto finish;
}
iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
(void) unit_get_memory_accounting(u, metric, &v);
if (v == UINT64_MAX)
continue;
/* Format peak swap memory for inclusion in the human language message string */
t = strjoin(FORMAT_BYTES(memory_swap_peak), " memory swap peak");
if (!t) {
r = log_oom();
goto finish;
}
message_parts[n_message_parts++] = t;
if (asprintf(&t, "%s=%" PRIu64, memory_fields[metric].journal_field, v) < 0)
return log_oom();
iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
if (strextendf_with_separator(&message, ", ", "%s %s",
FORMAT_BYTES(v), memory_fields[metric].message_suffix) < 0)
return log_oom();
log_level = raise_level(log_level,
v > MENTIONWORTHY_MEMORY_BYTES,
v > NOTICEWORTHY_MEMORY_BYTES);
}
for (CGroupIOAccountingMetric k = 0; k < _CGROUP_IO_ACCOUNTING_METRIC_MAX; k++) {
uint64_t value = UINT64_MAX;
assert(io_fields[k]);
assert(io_fields[k].journal_field);
(void) unit_get_io_accounting(u, k, k > 0, &value);
if (value == UINT64_MAX)
continue;
have_io_accounting = true;
if (value > 0)
any_io = true;
/* Format IO accounting data for inclusion in the structured log message */
if (asprintf(&t, "%s=%" PRIu64, io_fields[k], value) < 0) {
r = log_oom();
goto finish;
}
iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
if (asprintf(&t, "%s=%" PRIu64, io_fields[k].journal_field, value) < 0)
return log_oom();
iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
/* Format the IO accounting data for inclusion in the human language message string, but only
* for the bytes counters (and not for the operations counters) */
if (k == CGROUP_IO_READ_BYTES) {
assert(!rr);
rr = strjoin("read ", strna(FORMAT_BYTES(value)), " from disk");
if (!rr) {
r = log_oom();
goto finish;
}
} else if (k == CGROUP_IO_WRITE_BYTES) {
assert(!wr);
wr = strjoin("written ", strna(FORMAT_BYTES(value)), " to disk");
if (!wr) {
r = log_oom();
goto finish;
}
}
if (io_fields[k].message_suffix) {
if (strextendf_with_separator(&message, ", ", "%s %s",
FORMAT_BYTES(value), io_fields[k].message_suffix) < 0)
return log_oom();
if (IN_SET(k, CGROUP_IO_READ_BYTES, CGROUP_IO_WRITE_BYTES))
log_level = raise_level(log_level,
value > MENTIONWORTHY_IO_BYTES,
value > NOTICEWORTHY_IO_BYTES);
}
if (have_io_accounting) {
if (any_io) {
if (rr)
message_parts[n_message_parts++] = TAKE_PTR(rr);
if (wr)
message_parts[n_message_parts++] = TAKE_PTR(wr);
} else {
char *k;
k = strdup("no IO");
if (!k) {
r = log_oom();
goto finish;
}
message_parts[n_message_parts++] = k;
}
}
for (CGroupIPAccountingMetric m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
uint64_t value = UINT64_MAX;
assert(ip_fields[m]);
assert(ip_fields[m].journal_field);
(void) unit_get_ip_accounting(u, m, &value);
if (value == UINT64_MAX)
continue;
have_ip_accounting = true;
if (value > 0)
any_traffic = true;
/* Format IP accounting data for inclusion in the structured log message */
if (asprintf(&t, "%s=%" PRIu64, ip_fields[m], value) < 0) {
r = log_oom();
goto finish;
}
iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
if (asprintf(&t, "%s=%" PRIu64, ip_fields[m].journal_field, value) < 0)
return log_oom();
iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
/* Format the IP accounting data for inclusion in the human language message string, but only for the
* bytes counters (and not for the packets counters) */
if (m == CGROUP_IP_INGRESS_BYTES) {
assert(!igress);
igress = strjoin("received ", strna(FORMAT_BYTES(value)), " IP traffic");
if (!igress) {
r = log_oom();
goto finish;
}
} else if (m == CGROUP_IP_EGRESS_BYTES) {
assert(!egress);
egress = strjoin("sent ", strna(FORMAT_BYTES(value)), " IP traffic");
if (!egress) {
r = log_oom();
goto finish;
}
}
/* Format the IP accounting data for inclusion in the human language message string, but only
* for the bytes counters (and not for the packets counters) */
if (ip_fields[m].message_suffix) {
if (strextendf_with_separator(&message, ", ", "%s %s",
FORMAT_BYTES(value), ip_fields[m].message_suffix) < 0)
return log_oom();
if (IN_SET(m, CGROUP_IP_INGRESS_BYTES, CGROUP_IP_EGRESS_BYTES))
log_level = raise_level(log_level,
value > MENTIONWORTHY_IP_BYTES,
value > NOTICEWORTHY_IP_BYTES);
}
/* This check is here because it is the earliest point following all possible log_level assignments. If
* log_level is assigned anywhere after this point, move this check. */
if (!unit_log_level_test(u, log_level)) {
r = 0;
goto finish;
}
if (have_ip_accounting) {
if (any_traffic) {
if (igress)
message_parts[n_message_parts++] = TAKE_PTR(igress);
if (egress)
message_parts[n_message_parts++] = TAKE_PTR(egress);
} else {
char *k;
k = strdup("no IP traffic");
if (!k) {
r = log_oom();
goto finish;
}
message_parts[n_message_parts++] = k;
}
}
/* This check is here because it is the earliest point following all possible log_level assignments.
* (If log_level is assigned anywhere after this point, move this check.) */
if (!unit_log_level_test(u, log_level))
return 0;
/* Is there any accounting data available at all? */
if (n_iovec == 0) {
r = 0;
goto finish;
assert(!message);
return 0;
}
if (n_message_parts == 0)
t = strjoina("MESSAGE=", u->id, ": Completed.");
else {
_cleanup_free_ char *joined = NULL;
t = strjoin("MESSAGE=", u->id, ": ", message ?: "Completed", ".");
if (!t)
return log_oom();
iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
message_parts[n_message_parts] = NULL;
if (!set_iovec_string_field(iovec, &n_iovec, "MESSAGE_ID=", SD_MESSAGE_UNIT_RESOURCES_STR))
return log_oom();
joined = strv_join(message_parts, ", ");
if (!joined) {
r = log_oom();
goto finish;
}
if (!set_iovec_string_field(iovec, &n_iovec, u->manager->unit_log_field, u->id))
return log_oom();
joined[0] = ascii_toupper(joined[0]);
t = strjoina("MESSAGE=", u->id, ": ", joined, ".");
}
if (!set_iovec_string_field(iovec, &n_iovec, u->manager->invocation_log_field, u->invocation_id_string))
return log_oom();
/* The following four fields we allocate on the stack or are static strings, we hence don't want to free them,
* and hence don't increase n_iovec for them */
iovec[n_iovec] = IOVEC_MAKE_STRING(t);
iovec[n_iovec + 1] = IOVEC_MAKE_STRING("MESSAGE_ID=" SD_MESSAGE_UNIT_RESOURCES_STR);
t = strjoina(u->manager->unit_log_field, u->id);
iovec[n_iovec + 2] = IOVEC_MAKE_STRING(t);
t = strjoina(u->manager->invocation_log_field, u->invocation_id_string);
iovec[n_iovec + 3] = IOVEC_MAKE_STRING(t);
log_unit_struct_iovec(u, log_level, iovec, n_iovec + 4);
r = 0;
finish:
free_many_charp(message_parts, n_message_parts);
for (size_t i = 0; i < n_iovec; i++)
free(iovec[i].iov_base);
return r;
log_unit_struct_iovec(u, log_level, iovec, n_iovec);
return 0;
}
static void unit_update_on_console(Unit *u) {

View File

@ -158,6 +158,10 @@
__atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \
})
#define U64_KB UINT64_C(1024)
#define U64_MB (UINT64_C(1024) * U64_KB)
#define U64_GB (UINT64_C(1024) * U64_MB)
#undef MAX
#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
#define __MAX(aq, a, bq, b) \

View File

@ -47,10 +47,6 @@
#define DEFAULT_COMPRESS_THRESHOLD (512ULL)
#define MIN_COMPRESS_THRESHOLD (8ULL)
#define U64_KB UINT64_C(1024)
#define U64_MB (UINT64_C(1024) * U64_KB)
#define U64_GB (UINT64_C(1024) * U64_MB)
/* This is the minimum journal file size */
#define JOURNAL_FILE_SIZE_MIN (512 * U64_KB) /* 512 KiB */
#define JOURNAL_COMPACT_SIZE_MAX ((uint64_t) UINT32_MAX) /* 4 GiB */