Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 107c060

Browse files
authored
Ensure that errors during startup are written to the logs and the console. (#10191)
* Defer stdio redirection until we are about to start the reactor * Catch and handle exceptions during startup
1 parent 7c536d0 commit 107c060

File tree

5 files changed

+55
-16
lines changed

5 files changed

+55
-16
lines changed

changelog.d/10191.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ensure that errors during startup are written to the logs and the console.

synapse/app/_base.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
from cryptography.utils import CryptographyDeprecationWarning
2727
from typing_extensions import NoReturn
2828

29+
import twisted
2930
from twisted.internet import defer, error, reactor
31+
from twisted.logger import LoggingFile, LogLevel
3032
from twisted.protocols.tls import TLSMemoryBIOFactory
3133

3234
import synapse
@@ -139,14 +141,38 @@ def run():
139141

140142
def quit_with_error(error_string: str) -> NoReturn:
141143
message_lines = error_string.split("\n")
142-
line_length = max(len(line) for line in message_lines if len(line) < 80) + 2
144+
line_length = min(max(len(line) for line in message_lines), 80) + 2
143145
sys.stderr.write("*" * line_length + "\n")
144146
for line in message_lines:
145147
sys.stderr.write(" %s\n" % (line.rstrip(),))
146148
sys.stderr.write("*" * line_length + "\n")
147149
sys.exit(1)
148150

149151

152+
def handle_startup_exception(e: Exception) -> NoReturn:
153+
# Exceptions that occur between setting up the logging and forking or starting
154+
# the reactor are written to the logs, followed by a summary to stderr.
155+
logger.exception("Exception during startup")
156+
quit_with_error(
157+
f"Error during initialisation:\n {e}\nThere may be more information in the logs."
158+
)
159+
160+
161+
def redirect_stdio_to_logs() -> None:
162+
streams = [("stdout", LogLevel.info), ("stderr", LogLevel.error)]
163+
164+
for (stream, level) in streams:
165+
oldStream = getattr(sys, stream)
166+
loggingFile = LoggingFile(
167+
logger=twisted.logger.Logger(namespace=stream),
168+
level=level,
169+
encoding=getattr(oldStream, "encoding", None),
170+
)
171+
setattr(sys, stream, loggingFile)
172+
173+
print("Redirected stdout/stderr to logs")
174+
175+
150176
def register_start(cb: Callable[..., Awaitable], *args, **kwargs) -> None:
151177
"""Register a callback with the reactor, to be called once it is running
152178

synapse/app/generic_worker.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@
3232
SERVER_KEY_V2_PREFIX,
3333
)
3434
from synapse.app import _base
35-
from synapse.app._base import max_request_body_size, register_start
35+
from synapse.app._base import (
36+
handle_startup_exception,
37+
max_request_body_size,
38+
redirect_stdio_to_logs,
39+
register_start,
40+
)
3641
from synapse.config._base import ConfigError
3742
from synapse.config.homeserver import HomeServerConfig
3843
from synapse.config.logger import setup_logging
@@ -469,14 +474,21 @@ def start(config_options):
469474

470475
setup_logging(hs, config, use_worker_options=True)
471476

472-
hs.setup()
477+
try:
478+
hs.setup()
473479

474-
# Ensure the replication streamer is always started in case we write to any
475-
# streams. Will no-op if no streams can be written to by this worker.
476-
hs.get_replication_streamer()
480+
# Ensure the replication streamer is always started in case we write to any
481+
# streams. Will no-op if no streams can be written to by this worker.
482+
hs.get_replication_streamer()
483+
except Exception as e:
484+
handle_startup_exception(e)
477485

478486
register_start(_base.start, hs)
479487

488+
# redirect stdio to the logs, if configured.
489+
if not hs.config.no_redirect_stdio:
490+
redirect_stdio_to_logs()
491+
480492
_base.start_worker_reactor("synapse-generic-worker", config)
481493

482494

synapse/app/homeserver.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@
3737
)
3838
from synapse.app import _base
3939
from synapse.app._base import (
40+
handle_startup_exception,
4041
listen_ssl,
4142
listen_tcp,
4243
max_request_body_size,
43-
quit_with_error,
44+
redirect_stdio_to_logs,
4445
register_start,
4546
)
4647
from synapse.config._base import ConfigError
@@ -69,8 +70,6 @@
6970
from synapse.rest.well_known import WellKnownResource
7071
from synapse.server import HomeServer
7172
from synapse.storage import DataStore
72-
from synapse.storage.engines import IncorrectDatabaseSetup
73-
from synapse.storage.prepare_database import UpgradeDatabaseException
7473
from synapse.util.httpresourcetree import create_resource_tree
7574
from synapse.util.module_loader import load_module
7675
from synapse.util.versionstring import get_version_string
@@ -362,10 +361,8 @@ def setup(config_options):
362361

363362
try:
364363
hs.setup()
365-
except IncorrectDatabaseSetup as e:
366-
quit_with_error(str(e))
367-
except UpgradeDatabaseException as e:
368-
quit_with_error("Failed to upgrade database: %s" % (e,))
364+
except Exception as e:
365+
handle_startup_exception(e)
369366

370367
async def start():
371368
# Load the OIDC provider metadatas, if OIDC is enabled.
@@ -456,6 +453,11 @@ def main():
456453
# check base requirements
457454
check_requirements()
458455
hs = setup(sys.argv[1:])
456+
457+
# redirect stdio to the logs, if configured.
458+
if not hs.config.no_redirect_stdio:
459+
redirect_stdio_to_logs()
460+
459461
run(hs)
460462

461463

synapse/config/logger.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,7 @@ def _log(event: dict) -> None:
259259
finally:
260260
threadlocal.active = False
261261

262-
logBeginner.beginLoggingTo([_log], redirectStandardIO=not config.no_redirect_stdio)
263-
if not config.no_redirect_stdio:
264-
print("Redirected stdout/stderr to logs")
262+
logBeginner.beginLoggingTo([_log], redirectStandardIO=False)
265263

266264

267265
def _load_logging_config(log_config_path: str) -> None:

0 commit comments

Comments
 (0)