}
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);
- log_level = raise_level(log_level,
- memory_peak > MENTIONWORTHY_MEMORY_BYTES,
- memory_peak > NOTICEWORTHY_MEMORY_BYTES);
- }
+ (void) unit_get_memory_accounting(u, metric, &v);
+ if (v == UINT64_MAX)
+ continue;
- (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);
+ if (asprintf(&t, "%s=%" PRIu64, memory_fields[metric].journal_field, v) < 0)
+ return log_oom();
+ iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
- /* 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 (strextendf_with_separator(&message, ", ", "%s %s",
+ FORMAT_BYTES(v), memory_fields[metric].message_suffix) < 0)
+ return log_oom();
log_level = raise_level(log_level,
- memory_swap_peak > MENTIONWORTHY_MEMORY_BYTES,
- memory_swap_peak > NOTICEWORTHY_MEMORY_BYTES);
+ 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);
-
- /* 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;
- }
- }
+ 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 (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;
- }
-
- if (n_message_parts == 0)
- t = strjoina("MESSAGE=", u->id, ": Completed.");
- else {
- _cleanup_free_ char *joined = NULL;
-
- message_parts[n_message_parts] = NULL;
-
- joined = strv_join(message_parts, ", ");
- if (!joined) {
- r = log_oom();
- goto finish;
- }
-
- joined[0] = ascii_toupper(joined[0]);
- t = strjoina("MESSAGE=", u->id, ": ", joined, ".");
+ assert(!message);
+ return 0;
}
- /* 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);
+ t = strjoin("MESSAGE=", u->id, ": ", message ?: "Completed", ".");
+ if (!t)
+ return log_oom();
+ iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
- log_unit_struct_iovec(u, log_level, iovec, n_iovec + 4);
- r = 0;
+ if (!set_iovec_string_field(iovec, &n_iovec, "MESSAGE_ID=", SD_MESSAGE_UNIT_RESOURCES_STR))
+ return log_oom();
-finish:
- free_many_charp(message_parts, n_message_parts);
+ if (!set_iovec_string_field(iovec, &n_iovec, u->manager->unit_log_field, u->id))
+ return log_oom();
- for (size_t i = 0; i < n_iovec; i++)
- free(iovec[i].iov_base);
+ if (!set_iovec_string_field(iovec, &n_iovec, u->manager->invocation_log_field, u->invocation_id_string))
+ return log_oom();
- return r;
+ log_unit_struct_iovec(u, log_level, iovec, n_iovec);
+ return 0;
}
static void unit_update_on_console(Unit *u) {