Skip to content

Commit 0e7d767

Browse files
committed
Apparently, it's possible for an environment to have the memory.limit_in_bytes file, but not the memory.usage_in_bytes. To avoid raising FileDoesNotExist errors, we now check if the files are readable when the metric set is initialized.
1 parent c09355c commit 0e7d767

File tree

2 files changed

+64
-7
lines changed

2 files changed

+64
-7
lines changed

elasticapm/metrics/sets/cpu_linux.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import re
3434
import resource
3535
import threading
36-
from collections import namedtuple
3736

3837
from elasticapm.metrics.base_metrics import MetricsSet
3938

@@ -65,7 +64,12 @@
6564

6665
logger = logging.getLogger("elasticapm.metrics.cpu_linux")
6766

68-
CGroupFiles = namedtuple("CGroupFiles", ("limit", "usage", "stat"))
67+
68+
class CGroupFiles(object):
69+
def __init__(self, limit, usage, stat):
70+
self.limit = limit if os.access(limit, os.R_OK) else None
71+
self.usage = usage if os.access(usage, os.R_OK) else None
72+
self.stat = stat if os.access(stat, os.R_OK) else None
6973

7074

7175
class CPUMetricSet(MetricsSet):
@@ -229,11 +233,13 @@ def read_system_stats(self):
229233
stats["cpu_usage"] = stats["cpu_total"] - (f["idle"] + f["iowait"])
230234
break
231235
if self.cgroup_files:
232-
with open(self.cgroup_files.limit, "r") as memfile:
233-
stats["cgroup_mem_total"] = int(memfile.readline())
234-
with open(self.cgroup_files.usage, "r") as memfile:
235-
usage = int(memfile.readline())
236-
stats["cgroup_mem_used"] = usage
236+
if self.cgroup_files.limit:
237+
with open(self.cgroup_files.limit, "r") as memfile:
238+
stats["cgroup_mem_total"] = int(memfile.readline())
239+
if self.cgroup_files.usage:
240+
with open(self.cgroup_files.usage, "r") as memfile:
241+
usage = int(memfile.readline())
242+
stats["cgroup_mem_used"] = usage
237243
with open(self.memory_stats_file, "r") as memfile:
238244
for line in memfile:
239245
metric_name = line.split(":")[0]

tests/metrics/cpu_linux_tests.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,3 +396,54 @@ def test_mem_from_cgroup2_max_handling(elasticapm_client, tmpdir):
396396

397397
assert "system.process.cgroup.memory.mem.limit.bytes" not in data["samples"]
398398
assert "system.process.cgroup.memory.mem.usage.bytes" not in data["samples"]
399+
400+
401+
def test_mem_from_cgroup_files_dont_exist(elasticapm_client, tmpdir):
402+
# it appears that on Google App engine, there is a possibility of
403+
# memory.limit_in_bytes existing while memory.usage_in_bytes does not.
404+
# See https://github.com/elastic/apm-agent-python/issues/985
405+
proc_stat_self = os.path.join(tmpdir.strpath, "self-stat")
406+
proc_stat = os.path.join(tmpdir.strpath, "stat")
407+
proc_meminfo = os.path.join(tmpdir.strpath, "meminfo")
408+
cgroup_memory_limit = os.path.join(tmpdir.strpath, "memory", "memory.limit_in_bytes")
409+
# intentionally commented out
410+
# cgroup_memory_usage = os.path.join(tmpdir.strpath, "memory", "memory.usage_in_bytes")
411+
cgroup_memory_stat = os.path.join(tmpdir.strpath, "memory", "memory.stat")
412+
proc_self_cgroup = os.path.join(tmpdir.strpath, "cgroup")
413+
os.mkdir(os.path.join(tmpdir.strpath, "memory"))
414+
proc_self_mount = os.path.join(tmpdir.strpath, "mountinfo")
415+
416+
for path, content in (
417+
(proc_stat, TEMPLATE_PROC_STAT_DEBIAN.format(user=0, idle=0)),
418+
(proc_stat_self, TEMPLATE_PROC_STAT_SELF.format(utime=0, stime=0)),
419+
(proc_meminfo, TEMPLATE_PROC_MEMINFO),
420+
(cgroup_memory_limit, TEMPLATE_CGROUP_MEM_LIMIT_IN_BYTES_LIMITED),
421+
# intentionally commented out
422+
# (cgroup_memory_usage, TEMPLATE_CGROUP_MEM_USAGE_IN_BYTES),
423+
(cgroup_memory_stat, TEMPLATE_CGROUP_MEM_STAT),
424+
(proc_self_cgroup, "9:memory:/slice"),
425+
(
426+
proc_self_mount,
427+
"39 30 0:35 / "
428+
+ tmpdir.strpath
429+
+ "/memory rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,seclabel,memory\n",
430+
),
431+
):
432+
with open(path, mode="w") as f:
433+
f.write(content)
434+
metricset = CPUMetricSet(
435+
MetricsRegistry(elasticapm_client),
436+
sys_stats_file=proc_stat,
437+
process_stats_file=proc_stat_self,
438+
memory_stats_file=proc_meminfo,
439+
proc_self_cgroup=proc_self_cgroup,
440+
mount_info=proc_self_mount,
441+
)
442+
443+
assert metricset.cgroup_files.limit is not None
444+
assert metricset.cgroup_files.usage is None
445+
446+
data = next(metricset.collect())
447+
448+
assert "system.process.cgroup.memory.mem.limit.bytes" in data["samples"]
449+
assert "system.process.cgroup.memory.mem.usage.bytes" not in data["samples"]

0 commit comments

Comments
 (0)