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:
commit
266e8d0eb6
@ -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);
|
||||
|
@ -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);
|
||||
|
314
src/core/unit.c
314
src/core/unit.c
@ -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) {
|
||||
|
@ -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) \
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user