Skip to content

Commit 2c4057b

Browse files
Prevent duplicate logging setup when running multiple Synapse instances (#19067)
Be mindful that it's possible to run Synapse multiple times in the same Python process. So we only need to do some part of the logging setup once. - We only need to setup the global log record factory and context filter once - We only need to redirect Twisted logging once ### Background As part of Element's plan to support a light form of vhosting (virtual host) (multiple instances of Synapse in the same Python process), we're currently diving into the details and implications of running multiple instances of Synapse in the same Python process. "Per-tenant logging" tracked internally by element-hq/synapse-small-hosts#48
1 parent f54ddbc commit 2c4057b

File tree

3 files changed

+60
-33
lines changed

3 files changed

+60
-33
lines changed

changelog.d/19067.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Prevent duplicate logging setup when running multiple Synapse instances.

synapse/config/logger.py

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -198,12 +198,27 @@ def generate_files(self, config: dict[str, Any], config_dir_path: str) -> None:
198198
log_config_file.write(DEFAULT_LOG_CONFIG.substitute(log_file=log_file))
199199

200200

201-
def _setup_stdlib_logging(
202-
config: "HomeServerConfig", log_config_path: Optional[str], logBeginner: LogBeginner
203-
) -> None:
201+
_already_performed_one_time_logging_setup: bool = False
202+
"""
203+
Marks whether we've already successfully ran `one_time_logging_setup()`.
204+
"""
205+
206+
207+
def one_time_logging_setup(*, logBeginner: LogBeginner = globalLogBeginner) -> None:
204208
"""
205-
Set up Python standard library logging.
209+
Perform one-time logging configuration for the Python process.
210+
211+
For example, we don't need to have multiple log record factories. Once we've
212+
configured it once, we don't need to do it again.
213+
214+
This matters because multiple Synapse instances can be run in the same Python
215+
process (c.f. Synapse Pro for small hosts)
206216
"""
217+
global _already_performed_one_time_logging_setup
218+
219+
# We only need to set things up once.
220+
if _already_performed_one_time_logging_setup:
221+
return
207222

208223
# We add a log record factory that runs all messages through the
209224
# LoggingContextFilter so that we get the context *at the time we log*
@@ -221,26 +236,6 @@ def factory(*args: Any, **kwargs: Any) -> logging.LogRecord:
221236

222237
logging.setLogRecordFactory(factory)
223238

224-
# Configure the logger with the initial configuration.
225-
if log_config_path is None:
226-
log_format = (
227-
"%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s"
228-
" - %(message)s"
229-
)
230-
231-
logger = logging.getLogger("")
232-
logger.setLevel(logging.INFO)
233-
logging.getLogger("synapse.storage.SQL").setLevel(logging.INFO)
234-
235-
formatter = logging.Formatter(log_format)
236-
237-
handler = logging.StreamHandler()
238-
handler.setFormatter(formatter)
239-
logger.addHandler(handler)
240-
else:
241-
# Load the logging configuration.
242-
_load_logging_config(log_config_path)
243-
244239
# Route Twisted's native logging through to the standard library logging
245240
# system.
246241
observer = STDLibLogObserver()
@@ -281,6 +276,36 @@ def _log(event: dict) -> None:
281276

282277
logBeginner.beginLoggingTo([_log], redirectStandardIO=False)
283278

279+
_already_performed_one_time_logging_setup = True
280+
281+
282+
def _setup_stdlib_logging(
283+
config: "HomeServerConfig", log_config_path: Optional[str]
284+
) -> None:
285+
"""
286+
Set up Python standard library logging.
287+
"""
288+
289+
# Configure the logger with the initial configuration.
290+
if log_config_path is None:
291+
log_format = (
292+
"%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s"
293+
" - %(message)s"
294+
)
295+
296+
logger = logging.getLogger("")
297+
logger.setLevel(logging.INFO)
298+
logging.getLogger("synapse.storage.SQL").setLevel(logging.INFO)
299+
300+
formatter = logging.Formatter(log_format)
301+
302+
handler = logging.StreamHandler()
303+
handler.setFormatter(formatter)
304+
logger.addHandler(handler)
305+
else:
306+
# Load the logging configuration.
307+
_load_logging_config(log_config_path)
308+
284309

285310
def _load_logging_config(log_config_path: str) -> None:
286311
"""
@@ -318,19 +343,14 @@ def setup_logging(
318343
hs: "HomeServer",
319344
config: "HomeServerConfig",
320345
use_worker_options: bool = False,
321-
logBeginner: LogBeginner = globalLogBeginner,
322346
) -> None:
323347
"""
324348
Set up the logging subsystem.
325349
326350
Args:
327351
config: configuration data
328-
329352
use_worker_options: True to use the 'worker_log_config' option
330353
instead of 'log_config'.
331-
332-
logBeginner: The Twisted logBeginner to use.
333-
334354
"""
335355
from twisted.internet import reactor
336356

@@ -341,11 +361,17 @@ def setup_logging(
341361
)
342362

343363
# Perform one-time logging configuration.
344-
_setup_stdlib_logging(config, log_config_path, logBeginner=logBeginner)
364+
one_time_logging_setup()
365+
366+
# Configure logging.
367+
_setup_stdlib_logging(config, log_config_path)
345368
# Add a SIGHUP handler to reload the logging configuration, if one is available.
346369
from synapse.app import _base as appbase
347370

348-
appbase.register_sighup(hs, _reload_logging_config, log_config_path)
371+
# We only need to reload the config if there is a log config file path provided to
372+
# reload from.
373+
if log_config_path:
374+
appbase.register_sighup(hs, _reload_logging_config, log_config_path)
349375

350376
# Log immediately so we can grep backwards.
351377
logger.warning("***** STARTING SERVER *****")

synmark/suites/logging.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from twisted.logger import LogBeginner, LogPublisher
3434
from twisted.protocols.basic import LineOnlyReceiver
3535

36-
from synapse.config.logger import _setup_stdlib_logging
36+
from synapse.config.logger import _setup_stdlib_logging, one_time_logging_setup
3737
from synapse.logging import RemoteHandler
3838
from synapse.synapse_rust import reset_logging_config
3939
from synapse.types import ISynapseReactor
@@ -115,10 +115,10 @@ class _logging:
115115
}
116116

117117
logger = logging.getLogger("synapse")
118+
one_time_logging_setup(logBeginner=beginner)
118119
_setup_stdlib_logging(
119120
hs_config, # type: ignore[arg-type]
120121
None,
121-
logBeginner=beginner,
122122
)
123123

124124
# Force a new logging config without having to load it from a file.

0 commit comments

Comments
 (0)