Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b6dbfca
Remove `MetadataFilter`
MadLittleMods Aug 21, 2025
792774b
Clarify why no `parent_context` on request context
MadLittleMods Aug 22, 2025
18aad07
Start thinking about how to keep track of `server_name`
MadLittleMods Aug 22, 2025
f1117a1
Split loading config vs setting up the homeserver
MadLittleMods Aug 25, 2025
03f617b
Force keyword args on `LoggingContext`
MadLittleMods Aug 25, 2025
a97a847
`LoggingContext.name` is not optional
MadLittleMods Aug 25, 2025
66aa39f
Require `LoggingContext.server_name`
MadLittleMods Aug 25, 2025
3a5bab7
Fill in `LoggingContext.server_name`
MadLittleMods Aug 25, 2025
7bc92a0
Add to request context
MadLittleMods Aug 25, 2025
1a50efa
Merge branch 'develop' into madlittlemods/per-tenant-logging-server-name
MadLittleMods Sep 22, 2025
5881e6b
Fill in `server_name` in `tests/util/test_logcontext.py`
MadLittleMods Sep 22, 2025
c2faea5
Inherit `server_name` when we daemonize
MadLittleMods Sep 22, 2025
af3bf8e
Fill in `server_name` in logcontext used in tests
MadLittleMods Sep 22, 2025
c77ffe6
Add `server_name` to `log_shutdown` logcontext
MadLittleMods Sep 22, 2025
7aae4f3
Fill in `server_name` on `Clock`
MadLittleMods Sep 22, 2025
fe0a88d
Fill in bulk cases of `Clock.server_name`
MadLittleMods Sep 22, 2025
ee4f13a
Always use homeserver clock for `Linearizer`
MadLittleMods Sep 23, 2025
7e2a9ab
`server_name` required on `LruCache`
MadLittleMods Sep 23, 2025
98198ba
Add changelog
MadLittleMods Sep 23, 2025
831f619
Merge branch 'develop' into madlittlemods/per-tenant-logging-server-name
MadLittleMods Sep 24, 2025
d6ddf2e
Fill in new `server_name` spots
MadLittleMods Sep 24, 2025
947bd47
Add `server_name` to expected structured logging fields
MadLittleMods Sep 24, 2025
02e4e5d
Remove from docs for simplicity
MadLittleMods Sep 24, 2025
09b9756
Merge branch 'develop' into madlittlemods/per-tenant-logging-server-name
MadLittleMods Sep 24, 2025
44fa84f
Restore `LoggingContextFilter.__init__` constructor
MadLittleMods Sep 25, 2025
7ecb32c
Merge branch 'develop' into madlittlemods/per-tenant-logging-server-name
MadLittleMods Sep 25, 2025
e61366d
Docstring for `request`
MadLittleMods Sep 26, 2025
3f7c93a
Fill in clock docstring
MadLittleMods Sep 26, 2025
f2f6795
Merge branch 'develop' into madlittlemods/per-tenant-logging-server-name
MadLittleMods Sep 26, 2025
04ed0d8
Merge branch 'develop' into madlittlemods/per-tenant-logging-server-name
MadLittleMods Sep 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/18868.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix `server_name` in logging context for multiple Synapse instances in one process.
Copy link
Contributor Author

@MadLittleMods MadLittleMods Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Progressed outside of this PR: We have removed enough sentinel logcontext usage in Synapse for this to be useful (tracked by #18905).

With this PR, we will be able to distinguish which server sent the logs wherever we're not using sentinel logcontext. And if we find any more sentinel logcontext usage, we can be fix it up piecemeal like normal.

2 changes: 1 addition & 1 deletion synapse/app/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ def run_sighup(*args: Any, **kwargs: Any) -> None:
hs.get_pusherpool().start()

def log_shutdown() -> None:
with LoggingContext("log_shutdown"):
with LoggingContext(name="log_shutdown", server_name=server_name):
logger.info("Shutting down...")

# Log when we start the shut down process.
Expand Down
4 changes: 2 additions & 2 deletions synapse/app/admin_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ def start(config: HomeServerConfig, args: argparse.Namespace) -> None:
# command.

async def run() -> None:
with LoggingContext(name="command"):
with LoggingContext(name="command", server_name=config.server.server_name):
await _base.start(ss)
await args.func(ss, args)

Expand All @@ -342,5 +342,5 @@ async def run() -> None:

if __name__ == "__main__":
homeserver_config, args = load_config(sys.argv[1:])
with LoggingContext(name="main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
start(homeserver_config, args)
2 changes: 1 addition & 1 deletion synapse/app/appservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

def main() -> None:
homeserver_config = load_config(sys.argv[1:])
with LoggingContext(name="main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
start(homeserver_config)


Expand Down
2 changes: 1 addition & 1 deletion synapse/app/client_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

def main() -> None:
homeserver_config = load_config(sys.argv[1:])
with LoggingContext(name="main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
start(homeserver_config)


Expand Down
2 changes: 1 addition & 1 deletion synapse/app/event_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

def main() -> None:
homeserver_config = load_config(sys.argv[1:])
with LoggingContext(name="main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
start(homeserver_config)


Expand Down
2 changes: 1 addition & 1 deletion synapse/app/federation_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

def main() -> None:
homeserver_config = load_config(sys.argv[1:])
with LoggingContext(name="main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
start(homeserver_config)


Expand Down
2 changes: 1 addition & 1 deletion synapse/app/federation_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

def main() -> None:
homeserver_config = load_config(sys.argv[1:])
with LoggingContext(name="main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
start(homeserver_config)


Expand Down
2 changes: 1 addition & 1 deletion synapse/app/frontend_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

def main() -> None:
homeserver_config = load_config(sys.argv[1:])
with LoggingContext(name="main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
start(homeserver_config)


Expand Down
2 changes: 1 addition & 1 deletion synapse/app/generic_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ async def start() -> None:

def main() -> None:
homeserver_config = load_config(sys.argv[1:])
with LoggingContext(name="main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
start(homeserver_config)


Expand Down
2 changes: 1 addition & 1 deletion synapse/app/homeserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ def run(hs: HomeServer) -> None:
def main() -> None:
homeserver_config = load_or_generate_config(sys.argv[1:])

with LoggingContext("main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
# check base requirements
check_requirements()
hs = setup(homeserver_config)
Expand Down
2 changes: 1 addition & 1 deletion synapse/app/media_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

def main() -> None:
homeserver_config = load_config(sys.argv[1:])
with LoggingContext(name="main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
start(homeserver_config)


Expand Down
2 changes: 1 addition & 1 deletion synapse/app/pusher.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

def main() -> None:
homeserver_config = load_config(sys.argv[1:])
with LoggingContext(name="main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
start(homeserver_config)


Expand Down
2 changes: 1 addition & 1 deletion synapse/app/synchrotron.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

def main() -> None:
homeserver_config = load_config(sys.argv[1:])
with LoggingContext(name="main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
start(homeserver_config)


Expand Down
2 changes: 1 addition & 1 deletion synapse/app/user_dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

def main() -> None:
homeserver_config = load_config(sys.argv[1:])
with LoggingContext(name="main"):
with LoggingContext(name="main", server_name=homeserver_config.server.server_name):
start(homeserver_config)


Expand Down
6 changes: 3 additions & 3 deletions synapse/config/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ def add_arguments_to_parser(cls, config_parser: argparse.ArgumentParser) -> None

@classmethod
def load_config_with_parser(
cls: Type[TRootConfig], parser: argparse.ArgumentParser, argv: List[str]
cls: Type[TRootConfig], parser: argparse.ArgumentParser, argv_options: List[str]
) -> Tuple[TRootConfig, argparse.Namespace]:
"""Parse the commandline and config files with the given parser

Expand All @@ -611,14 +611,14 @@ def load_config_with_parser(

Args:
parser
argv
argv_options: The options passed to Synapse. Usually `sys.argv[1:]`.

Returns:
Returns the parsed config object and the parsed argparse.Namespace
object from parser.parse_args(..)`
"""

config_args = parser.parse_args(argv)
config_args = parser.parse_args(argv_options)

config_files = find_config_files(search_paths=config_args.config_path)
obj = cls(config_files)
Expand Down
3 changes: 0 additions & 3 deletions synapse/config/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
)

from synapse.logging.context import LoggingContextFilter
from synapse.logging.filter import MetadataFilter
from synapse.synapse_rust import reset_logging_config
from synapse.types import JsonDict

Expand Down Expand Up @@ -213,13 +212,11 @@ def _setup_stdlib_logging(
# writes.

log_context_filter = LoggingContextFilter()
log_metadata_filter = MetadataFilter({"server_name": config.server.server_name})
old_factory = logging.getLogRecordFactory()

def factory(*args: Any, **kwargs: Any) -> logging.LogRecord:
record = old_factory(*args, **kwargs)
log_context_filter.filter(record)
log_metadata_filter.filter(record)
return record

logging.setLogRecordFactory(factory)
Expand Down
2 changes: 1 addition & 1 deletion synapse/federation/federation_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def __init__(self, hs: "HomeServer"):
# with FederationHandlerRegistry.
hs.get_directory_handler()

self._server_linearizer = Linearizer("fed_server")
self._server_linearizer = Linearizer(name="fed_server", clock=hs.get_clock())

# origins that we are currently processing a transaction from.
# a dict from origin to txn id.
Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/appservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def __init__(self, hs: "HomeServer"):
self.is_processing = False

self._ephemeral_events_linearizer = Linearizer(
name="appservice_ephemeral_events"
name="appservice_ephemeral_events", clock=hs.get_clock()
)

def notify_interested_services(self, max_token: RoomStreamToken) -> None:
Expand Down
8 changes: 6 additions & 2 deletions synapse/handlers/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -1450,8 +1450,12 @@ def __init__(self, hs: "HomeServer", device_handler: DeviceWriterHandler):
self.clock = hs.get_clock() # nb must be called this for @measure_func
self.device_handler = device_handler

self._remote_edu_linearizer = Linearizer(name="remote_device_list")
self._resync_linearizer = Linearizer(name="remote_device_resync")
self._remote_edu_linearizer = Linearizer(
name="remote_device_list", clock=self.clock
)
self._resync_linearizer = Linearizer(
name="remote_device_resync", clock=self.clock
)

# user_id -> list of updates waiting to be handled.
self._pending_updates: Dict[
Expand Down
7 changes: 4 additions & 3 deletions synapse/handlers/e2e_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ def __init__(self, hs: "HomeServer"):

# Limit the number of in-flight requests from a single device.
self._query_devices_linearizer = Linearizer(
name="query_devices",
max_count=10,
name="query_devices", max_count=10, clock=hs.get_clock()
)

self._query_appservices_for_otks = (
Expand Down Expand Up @@ -1765,7 +1764,9 @@ def __init__(self, hs: "HomeServer"):
assert isinstance(device_handler, DeviceWriterHandler)
self._device_handler = device_handler

self._remote_edu_linearizer = Linearizer(name="remote_signing_key")
self._remote_edu_linearizer = Linearizer(
name="remote_signing_key", clock=self.clock
)

# user_id -> list of updates waiting to be handled.
self._pending_updates: Dict[str, List[Tuple[JsonDict, JsonDict]]] = {}
Expand Down
5 changes: 3 additions & 2 deletions synapse/handlers/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def __init__(self, hs: "HomeServer"):
self._notifier = hs.get_notifier()
self._worker_locks = hs.get_worker_locks_handler()

self._room_backfill = Linearizer("room_backfill")
self._room_backfill = Linearizer(name="room_backfill", clock=self.clock)

self._third_party_event_rules = (
hs.get_module_api_callbacks().third_party_event_rules
Expand All @@ -180,7 +180,8 @@ def __init__(self, hs: "HomeServer"):
# When the lock is held for a given room, no other concurrent code may
# partial state or un-partial state the room.
self._is_partial_state_room_linearizer = Linearizer(
name="_is_partial_state_room_linearizer"
name="_is_partial_state_room_linearizer",
clock=self.clock,
)

# if this is the main process, fire off a background process to resume
Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/federation_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def __init__(self, hs: "HomeServer"):
# federation event staging area.
self.room_queues: Dict[str, List[Tuple[EventBase, str]]] = {}

self._room_pdu_linearizer = Linearizer("fed_room_pdu")
self._room_pdu_linearizer = Linearizer(name="fed_room_pdu", clock=self._clock)

async def on_receive_pdu(self, origin: str, pdu: EventBase) -> None:
"""Process a PDU received via a federation /send/ transaction
Expand Down
4 changes: 3 additions & 1 deletion synapse/handlers/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,9 @@ def __init__(self, hs: "HomeServer"):

# We limit concurrent event creation for a room to 1. This prevents state resolution
# from occurring when sending bursts of events to a local room
self.limiter = Linearizer(max_count=1, name="room_event_creation_limit")
self.limiter = Linearizer(
max_count=1, name="room_event_creation_limit", clock=self.clock
)

self._bulk_push_rule_evaluator = hs.get_bulk_push_rule_evaluator()

Expand Down
4 changes: 3 additions & 1 deletion synapse/handlers/presence.py
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,9 @@ def __init__(self, hs: "HomeServer"):
] = {}
self.external_process_last_updated_ms: Dict[str, int] = {}

self.external_sync_linearizer = Linearizer(name="external_sync_linearizer")
self.external_sync_linearizer = Linearizer(
name="external_sync_linearizer", clock=self.clock
)

if self._track_presence:
# Start a LoopingCall in 30s that fires every 5s.
Expand Down
4 changes: 3 additions & 1 deletion synapse/handlers/read_marker.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ class ReadMarkerHandler:
def __init__(self, hs: "HomeServer"):
self.store = hs.get_datastores().main
self.account_data_handler = hs.get_account_data_handler()
self.read_marker_linearizer = Linearizer(name="read_marker")
self.read_marker_linearizer = Linearizer(
name="read_marker", clock=hs.get_clock()
)

async def received_client_read_marker(
self, room_id: str, user_id: str, event_id: str
Expand Down
8 changes: 6 additions & 2 deletions synapse/handlers/room_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,12 @@ def __init__(self, hs: "HomeServer"):
if self.hs.config.server.include_profile_data_on_invite:
self._membership_types_to_include_profile_data_in.add(Membership.INVITE)

self.member_linearizer: Linearizer = Linearizer(name="member")
self.member_as_limiter = Linearizer(max_count=10, name="member_as_limiter")
self.member_linearizer: Linearizer = Linearizer(
name="member", clock=hs.get_clock()
)
self.member_as_limiter = Linearizer(
max_count=10, name="member_as_limiter", clock=hs.get_clock()
)

self.clock = hs.get_clock()
self._spam_checker_module_callbacks = hs.get_module_api_callbacks().spam_checker
Expand Down
5 changes: 4 additions & 1 deletion synapse/handlers/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,10 @@ def get_lazy_loaded_members_cache(
)
if cache is None:
logger.debug("creating LruCache for %r", cache_key)
cache = LruCache(max_size=LAZY_LOADED_MEMBERS_CACHE_MAX_SIZE)
cache = LruCache(
max_size=LAZY_LOADED_MEMBERS_CACHE_MAX_SIZE,
server_name=self.server_name,
)
self.lazy_loaded_members_cache[cache_key] = cache
else:
logger.debug("found LruCache for %r", cache_key)
Expand Down
2 changes: 1 addition & 1 deletion synapse/http/federation/matrix_federation_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def __init__(
# addresses, to prevent DNS rebinding.
reactor = BlocklistingReactorWrapper(reactor, ip_allowlist, ip_blocklist)

self._clock = Clock(reactor)
self._clock = Clock(reactor, server_name=server_name)
self._pool = HTTPConnectionPool(reactor)
self._pool.retryAutomatically = False
self._pool.maxPersistentPerHost = 5
Expand Down
2 changes: 1 addition & 1 deletion synapse/http/federation/well_known_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def __init__(

self.server_name = server_name
self._reactor = reactor
self._clock = Clock(reactor)
self._clock = Clock(reactor, server_name=server_name)

if well_known_cache is None:
well_known_cache = TTLCache(
Expand Down
4 changes: 3 additions & 1 deletion synapse/http/matrixfederationclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,9 @@ def __init__(
use_proxy=True,
)

self.remote_download_linearizer = Linearizer("remote_download_linearizer", 6)
self.remote_download_linearizer = Linearizer(
name="remote_download_linearizer", max_count=6, clock=self.clock
)

def wake_destination(self, destination: str) -> None:
"""Called when the remote server may have come back online."""
Expand Down
24 changes: 22 additions & 2 deletions synapse/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,8 +411,19 @@ def __init__(
# Clock is optional as this class is exposed to the module API.
clock: Optional[Clock] = None,
):
"""
Args:
canonical_json: TODO
extract_context: TODO
clock: This is expected to be passed in by any Synapse code.
Only optional for the Module API.
"""

if clock is None:
clock = Clock(cast(ISynapseThreadlessReactor, reactor))
clock = Clock(
cast(ISynapseThreadlessReactor, reactor),
server_name="synapse_module_running_from_unknown_server",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to track which server this is running on? Such information would be useful if a particular module is misbehaving.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we could do a pattern like the following today because we expose ModuleApi.server_name(), I'd prefer not to make a further mess.

Modified DirectServeJsonResource
class DirectServeJsonResource(_AsyncResource):
    """A resource that will call `self._async_on_<METHOD>` on new requests,
    formatting responses and errors as JSON.
    """

    def __init__(
        self,
        canonical_json: bool = False,
        extract_context: bool = False,
        # Clock is optional as this class is exposed to the module API.
        clock: Optional[Clock] = None,
        # This is only necessary for Module API users who don't pass in a `clock`.
        server_name: Optional[str] = None,
    ):
        """
        Args:
            canonical_json: TODO
            extract_context: TODO
            clock: This is expected to be passed in by any Synapse code.
                Only optional for the Module API.
            server_name: The homeserver name (this should be `ModuleApi.server_name()`).
                Only used for the Module API if `clock` is not passed in.
        """

        if clock is None:
            clock = Clock(
                cast(ISynapseThreadlessReactor, reactor),
                server_name=server_name
                if server_name is not None
                else "synapse_module_running_from_unknown_server",
            )
        else:
            assert server_name is None, (
                "No need to pass in `server_name` if clock is set"
            )

        super().__init__(clock, extract_context)
        self.canonical_json = canonical_json

Usage:

class SomeResource(DirectServeJsonResource):
    def __init__(
        self,
        module_api: ModuleApi,
    ):
        super().__init__(server_name=module_api.server_name())


class SomeSynapseModule:
    """
    Synapse module that TODO
    """

    def __init__(
        self, config: MyModuleConfig, module_api: ModuleApi
    ):
        # Keep a reference to the config and Module API
        self._module_api = module_api

        # "Modules **must** register their web resources in their `__init__` method."
        # (https://github.com/element-hq/synapse/blob/081f6ad50fa0ea87c348778e8be40517da25c698/docs/modules/writing_a_module.md#L69)
        self._module_api.register_web_resource(
            SomeResource(
                self._module_api
            ),
        )

The ideal solution would be using the hs.get_clock() and passing in the Clock directly. See #18868 (comment) for why the homeserver clock is ideal. We currently don't expose Clock to module API consumers and maybe we shouldn't as it's best for them to continue using the dedicated ModuleApi equivalents.

Overall, this requires much more thought. And perhaps handing out less raw interfaces like DirectServeJsonResource in the module API as it makes doing the right thing like this practically impossible without making breaking changes.

)

super().__init__(clock, extract_context)
self.canonical_json = canonical_json
Expand Down Expand Up @@ -590,8 +601,17 @@ def __init__(
# Clock is optional as this class is exposed to the module API.
clock: Optional[Clock] = None,
):
"""
Args:
extract_context: TODO
clock: This is expected to be passed in by any Synapse code.
Only optional for the Module API.
"""
if clock is None:
clock = Clock(cast(ISynapseThreadlessReactor, reactor))
clock = Clock(
cast(ISynapseThreadlessReactor, reactor),
server_name="synapse_module_running_from_unknown_server",
)

super().__init__(clock, extract_context)

Expand Down
Loading
Loading