sapling/eden/integration/service_log_test.py

69 lines
2.3 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2.
import pathlib
from .lib.service_test_case import (
ManagedFakeEdenFSMixin,
ServiceTestCaseBase,
service_test,
)
from .start_test import run_eden_start_with_real_daemon
class ServiceLogTestBase(ServiceTestCaseBase):
"""Test how the EdenFS service stores its logs."""
def setUp(self) -> None:
super().setUp()
self.eden_dir = self.make_temp_dir("eden")
@property
def log_file_path(self) -> pathlib.Path:
return self.eden_dir / "logs" / "edenfs.log"
Make systemd service log to file Summary: When run inside the systemd service (fb-edenfs@.service), edenfs' logs are written to `/var/log/messages` (on Facebook dev servers). This is undesirable, since those logs have a bunch of noise. Make systemd-managed edenfs log to `~/local/.eden/logs/edenfs.log` instead, matching the behavior of custom-managed edenfs. --- I considered using systemd's StandardOutput= and StandardError= directives [1], but they have limitations: * **StandardOutput=file:%f/logs/edenfs.log**: When the `logs` directory is missing, systemd does not create it. In this case, systemd fails when it opens the log file, so systemd refuses to start the service. * **StandardOutput=journal** [2]: journald and journalctl are broken for user services. Logging to journald only works with persistent journal storage [3][4], but Facebook uses volatile journal storage. * **StandardOutput=syslog** [5]: rsyslog seems designed for system administrators, not users. I didn't investigate much, but I suspect it's impossible to make rsyslog write to a user-controlled path such as `~/local/.eden/logs/edenfs.log`. * **LogsDirectory=%f/logs and StandardOutput=file:%L/edenfs.log** [6][7]: LogsDirectory= does exactly what we need, except it only supports paths relative to `/var/log` or `~/.config/log/`. `LogsDirectory=%f/logs` does not work, and systemd will ignore such a directive. * **StandardOutput=file:%f/logs/edenfs.log and a `mkdir` service**: If we create a service which just creates the `logs` directory, and make fb-edenfs@.service depend upon that service, systemd can successfully open the log file [8]. In theory, using StandardOutput= would cause errors like "could not set resource limits" to be logged to `edenfs.log`. In practice, systemd does not respect the service's logging configuration when reporting such errors [9]. Therefore, this solution is no better than the manual redirect. [1] https://www.freedesktop.org/software/systemd/man/systemd.exec.html#StandardOutput= [2] https://www.freedesktop.org/software/systemd/man/systemd-journald.service.html# [3] https://www.freedesktop.org/software/systemd/man/journald.conf.html#SplitMode= [4] https://lists.freedesktop.org/archives/systemd-devel/2016-October/037554.html [5] https://www.rsyslog.com/ [6] https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RuntimeDirectory= [7] https://www.freedesktop.org/software/systemd/man/systemd.unit.html#Specifiers [8] ```name=fb-edenfs-logs@.service [Service] Environment=EDENFS_CONFIG_DIR=%f ExecStart=/bin/sh -c ' \ set -e; \ set -u; \ \ /bin/mkdir -p -- "$${EDENFS_CONFIG_DIR}/logs""; \ ' ``` ```name=fb-edenfs@.service [Unit] After=fb-edenfs-logs@%i.service Requires=fb-edenfs-logs@%i.service ``` [9] https://github.com/systemd/systemd/blob/fd0ec39d3848029abd3a439e84c5728331de2128/src/basic/log.c#L560-L639 Reviewed By: simpkins Differential Revision: D13422459 fbshipit-source-id: 57c575a6f377812caa2a79168778576c6ccff33e
2018-12-18 02:08:05 +03:00
@service_test
class ServiceLogFakeEdenFSTest(ServiceLogTestBase):
def test_fake_edenfs_writes_logs_to_file_in_eden_dir(self) -> None:
self.assertFalse(
self.log_file_path.exists(),
f"{self.log_file_path} should not exist before starting fake_edenfs",
)
with self.spawn_fake_edenfs(self.eden_dir):
self.assertTrue(
self.log_file_path.exists(),
f"fake_edenfs should create {self.log_file_path}",
)
self.assertIn("Starting fake edenfs daemon", self.log_file_path.read_text())
Make systemd service log to file Summary: When run inside the systemd service (fb-edenfs@.service), edenfs' logs are written to `/var/log/messages` (on Facebook dev servers). This is undesirable, since those logs have a bunch of noise. Make systemd-managed edenfs log to `~/local/.eden/logs/edenfs.log` instead, matching the behavior of custom-managed edenfs. --- I considered using systemd's StandardOutput= and StandardError= directives [1], but they have limitations: * **StandardOutput=file:%f/logs/edenfs.log**: When the `logs` directory is missing, systemd does not create it. In this case, systemd fails when it opens the log file, so systemd refuses to start the service. * **StandardOutput=journal** [2]: journald and journalctl are broken for user services. Logging to journald only works with persistent journal storage [3][4], but Facebook uses volatile journal storage. * **StandardOutput=syslog** [5]: rsyslog seems designed for system administrators, not users. I didn't investigate much, but I suspect it's impossible to make rsyslog write to a user-controlled path such as `~/local/.eden/logs/edenfs.log`. * **LogsDirectory=%f/logs and StandardOutput=file:%L/edenfs.log** [6][7]: LogsDirectory= does exactly what we need, except it only supports paths relative to `/var/log` or `~/.config/log/`. `LogsDirectory=%f/logs` does not work, and systemd will ignore such a directive. * **StandardOutput=file:%f/logs/edenfs.log and a `mkdir` service**: If we create a service which just creates the `logs` directory, and make fb-edenfs@.service depend upon that service, systemd can successfully open the log file [8]. In theory, using StandardOutput= would cause errors like "could not set resource limits" to be logged to `edenfs.log`. In practice, systemd does not respect the service's logging configuration when reporting such errors [9]. Therefore, this solution is no better than the manual redirect. [1] https://www.freedesktop.org/software/systemd/man/systemd.exec.html#StandardOutput= [2] https://www.freedesktop.org/software/systemd/man/systemd-journald.service.html# [3] https://www.freedesktop.org/software/systemd/man/journald.conf.html#SplitMode= [4] https://lists.freedesktop.org/archives/systemd-devel/2016-October/037554.html [5] https://www.rsyslog.com/ [6] https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RuntimeDirectory= [7] https://www.freedesktop.org/software/systemd/man/systemd.unit.html#Specifiers [8] ```name=fb-edenfs-logs@.service [Service] Environment=EDENFS_CONFIG_DIR=%f ExecStart=/bin/sh -c ' \ set -e; \ set -u; \ \ /bin/mkdir -p -- "$${EDENFS_CONFIG_DIR}/logs""; \ ' ``` ```name=fb-edenfs@.service [Unit] After=fb-edenfs-logs@%i.service Requires=fb-edenfs-logs@%i.service ``` [9] https://github.com/systemd/systemd/blob/fd0ec39d3848029abd3a439e84c5728331de2128/src/basic/log.c#L560-L639 Reviewed By: simpkins Differential Revision: D13422459 fbshipit-source-id: 57c575a6f377812caa2a79168778576c6ccff33e
2018-12-18 02:08:05 +03:00
def test_fake_edenfs_appends_to_existing_log_file(self) -> None:
self.log_file_path.parent.mkdir(exist_ok=True, parents=True)
Make systemd service log to file Summary: When run inside the systemd service (fb-edenfs@.service), edenfs' logs are written to `/var/log/messages` (on Facebook dev servers). This is undesirable, since those logs have a bunch of noise. Make systemd-managed edenfs log to `~/local/.eden/logs/edenfs.log` instead, matching the behavior of custom-managed edenfs. --- I considered using systemd's StandardOutput= and StandardError= directives [1], but they have limitations: * **StandardOutput=file:%f/logs/edenfs.log**: When the `logs` directory is missing, systemd does not create it. In this case, systemd fails when it opens the log file, so systemd refuses to start the service. * **StandardOutput=journal** [2]: journald and journalctl are broken for user services. Logging to journald only works with persistent journal storage [3][4], but Facebook uses volatile journal storage. * **StandardOutput=syslog** [5]: rsyslog seems designed for system administrators, not users. I didn't investigate much, but I suspect it's impossible to make rsyslog write to a user-controlled path such as `~/local/.eden/logs/edenfs.log`. * **LogsDirectory=%f/logs and StandardOutput=file:%L/edenfs.log** [6][7]: LogsDirectory= does exactly what we need, except it only supports paths relative to `/var/log` or `~/.config/log/`. `LogsDirectory=%f/logs` does not work, and systemd will ignore such a directive. * **StandardOutput=file:%f/logs/edenfs.log and a `mkdir` service**: If we create a service which just creates the `logs` directory, and make fb-edenfs@.service depend upon that service, systemd can successfully open the log file [8]. In theory, using StandardOutput= would cause errors like "could not set resource limits" to be logged to `edenfs.log`. In practice, systemd does not respect the service's logging configuration when reporting such errors [9]. Therefore, this solution is no better than the manual redirect. [1] https://www.freedesktop.org/software/systemd/man/systemd.exec.html#StandardOutput= [2] https://www.freedesktop.org/software/systemd/man/systemd-journald.service.html# [3] https://www.freedesktop.org/software/systemd/man/journald.conf.html#SplitMode= [4] https://lists.freedesktop.org/archives/systemd-devel/2016-October/037554.html [5] https://www.rsyslog.com/ [6] https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RuntimeDirectory= [7] https://www.freedesktop.org/software/systemd/man/systemd.unit.html#Specifiers [8] ```name=fb-edenfs-logs@.service [Service] Environment=EDENFS_CONFIG_DIR=%f ExecStart=/bin/sh -c ' \ set -e; \ set -u; \ \ /bin/mkdir -p -- "$${EDENFS_CONFIG_DIR}/logs""; \ ' ``` ```name=fb-edenfs@.service [Unit] After=fb-edenfs-logs@%i.service Requires=fb-edenfs-logs@%i.service ``` [9] https://github.com/systemd/systemd/blob/fd0ec39d3848029abd3a439e84c5728331de2128/src/basic/log.c#L560-L639 Reviewed By: simpkins Differential Revision: D13422459 fbshipit-source-id: 57c575a6f377812caa2a79168778576c6ccff33e
2018-12-18 02:08:05 +03:00
self.log_file_path.write_text("test log messages\n")
with self.spawn_fake_edenfs(self.eden_dir):
pass
self.assertIn("test log messages", self.log_file_path.read_text())
class ServiceLogRealEdenFSTest(ManagedFakeEdenFSMixin, ServiceLogTestBase):
def test_real_edenfs_writes_logs_to_file_in_eden_dir(self) -> None:
self.assertFalse(
self.log_file_path.exists(),
f"{self.log_file_path} should not exist before starting edenfs",
)
self.exit_stack.enter_context(
run_eden_start_with_real_daemon(
eden_dir=self.eden_dir,
etc_eden_dir=self.etc_eden_dir,
home_dir=self.home_dir,
systemd=False,
)
)
self.assertTrue(
self.log_file_path.exists(), f"edenfs should create {self.log_file_path}"
)
self.assertIn("Starting edenfs", self.log_file_path.read_text())