Skip to content

Commit d0406f0

Browse files
committed
Improve logging
1 parent 825ad49 commit d0406f0

File tree

4 files changed

+78
-7
lines changed

4 files changed

+78
-7
lines changed

src/common/core/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def using_database_replica(
157157
chosen_replica = attempted_replica
158158
break
159159
except OperationalError:
160-
logger.warning(f"Replica '{attempted_replica}' is not available.")
160+
logger.exception(f"Replica '{attempted_replica}' is not available.")
161161
continue
162162

163163
if settings.REPLICA_READ_STRATEGY == ReplicaReadStrategy.DISTRIBUTED:
@@ -168,7 +168,7 @@ def using_database_replica(
168168
chosen_replica = attempted_replica
169169
break
170170
except OperationalError:
171-
logger.warning(f"Replica '{attempted_replica}' is not available.")
171+
logger.exception(f"Replica '{attempted_replica}' is not available.")
172172
local_replicas.remove(attempted_replica)
173173
continue
174174

tests/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from typing import Callable
2+
3+
GetLogsFixture = Callable[[str], list[tuple[str, str]]]

tests/conftest.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,21 @@
55
import prometheus_client
66
import pytest
77

8+
from tests import GetLogsFixture
9+
810
pytest_plugins = "flagsmith-test-tools"
911

1012

13+
@pytest.fixture()
14+
def get_logs(caplog: pytest.LogCaptureFixture) -> GetLogsFixture:
15+
caplog.set_level("DEBUG")
16+
17+
def _logs(module: str) -> list[tuple[str, str]]:
18+
return [(r.levelname, r.message) for r in caplog.records if r.name == module]
19+
20+
return _logs
21+
22+
1123
@pytest.fixture()
1224
def prometheus_multiproc_dir(
1325
tmp_path_factory: pytest.TempPathFactory,

tests/unit/common/core/test_utils.py

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
is_saas,
2121
using_database_replica,
2222
)
23+
from tests import GetLogsFixture
2324

2425
pytestmark = pytest.mark.django_db
2526

@@ -206,6 +207,7 @@ def test_get_version__invalid_file_contents__returns_unknown(
206207
@pytest.mark.django_db(databases="__all__")
207208
def test_using_database_replica__no_replicas__points_to_default(
208209
django_assert_num_queries: DjangoAssertNumQueries,
210+
get_logs: GetLogsFixture,
209211
mocker: MockerFixture,
210212
) -> None:
211213
# Given
@@ -216,6 +218,10 @@ def test_using_database_replica__no_replicas__points_to_default(
216218
with django_assert_num_queries(1, using="default"):
217219
using_database_replica(manager).first()
218220

221+
assert get_logs("common.core.utils") == [
222+
("INFO", "No replicas set up."),
223+
]
224+
219225

220226
@pytest.mark.django_db(databases="__all__")
221227
def test_using_database_replica__distributed__picks_databases_randomly(
@@ -245,6 +251,7 @@ def test_using_database_replica__distributed__picks_databases_randomly(
245251
def test_using_database_replica__distributed__skips_unhealthy_replica(
246252
bad_replica: MockType,
247253
django_assert_num_queries: DjangoAssertNumQueries,
254+
get_logs: GetLogsFixture,
248255
mocker: MockerFixture,
249256
settings: SettingsWrapper,
250257
) -> None:
@@ -268,11 +275,16 @@ def test_using_database_replica__distributed__skips_unhealthy_replica(
268275
for _ in range(20):
269276
using_database_replica(manager).first()
270277

278+
assert set(get_logs("common.core.utils")) == {
279+
("ERROR", "Replica 'replica_1' is not available."),
280+
}
281+
271282

272283
@pytest.mark.django_db(databases="__all__")
273284
def test_using_database_replica__distributed__falls_back_to_cross_region_replica(
274285
bad_replica: MockType,
275286
django_assert_max_num_queries: DjangoAssertNumQueries,
287+
get_logs: GetLogsFixture,
276288
mocker: MockerFixture,
277289
settings: SettingsWrapper,
278290
) -> None:
@@ -303,6 +315,24 @@ def test_using_database_replica__distributed__falls_back_to_cross_region_replica
303315
using_database_replica(manager).first()
304316
assert captured.final_queries
305317

318+
logs = get_logs("common.core.utils")
319+
assert (
320+
{
321+
("ERROR", "Replica 'replica_1' is not available."),
322+
("ERROR", "Replica 'replica_2' is not available."),
323+
("ERROR", "Replica 'replica_3' is not available."),
324+
}
325+
== set(logs[0:3])
326+
== set(logs[4:7])
327+
# ..And so on
328+
)
329+
assert (
330+
("WARNING", "Falling back to cross-region replicas, if any.")
331+
== logs[3]
332+
== logs[7]
333+
# ...And so on
334+
)
335+
306336

307337
@pytest.mark.django_db(databases="__all__")
308338
def test_using_database_replica__sequential__picks_databases_sequentially(
@@ -328,6 +358,7 @@ def test_using_database_replica__sequential__picks_databases_sequentially(
328358
def test_using_database_replica__sequential__skips_unhealthy_replica(
329359
bad_replica: MockType,
330360
django_assert_num_queries: DjangoAssertNumQueries,
361+
get_logs: GetLogsFixture,
331362
mocker: MockerFixture,
332363
settings: SettingsWrapper,
333364
) -> None:
@@ -354,11 +385,16 @@ def test_using_database_replica__sequential__skips_unhealthy_replica(
354385
with django_assert_num_queries(1, using="replica_1"):
355386
using_database_replica(manager).first()
356387

388+
assert get_logs("common.core.utils") == [
389+
("ERROR", "Replica 'replica_2' is not available."),
390+
]
391+
357392

358393
@pytest.mark.django_db(databases="__all__")
359394
def test_using_database_replica__sequential__falls_back_to_cross_region_replica(
360395
bad_replica: MockType,
361396
django_assert_num_queries: DjangoAssertNumQueries,
397+
get_logs: GetLogsFixture,
362398
mocker: MockerFixture,
363399
settings: SettingsWrapper,
364400
) -> None:
@@ -387,13 +423,20 @@ def test_using_database_replica__sequential__falls_back_to_cross_region_replica(
387423
with django_assert_num_queries(1, using="cross_region_replica_1"):
388424
using_database_replica(manager).first()
389425

426+
assert get_logs("common.core.utils") == 3 * [
427+
("ERROR", "Replica 'replica_1' is not available."),
428+
("ERROR", "Replica 'replica_2' is not available."),
429+
("ERROR", "Replica 'replica_3' is not available."),
430+
("WARNING", "Falling back to cross-region replicas, if any."),
431+
]
432+
390433

391434
@pytest.mark.django_db(databases="__all__")
392435
@pytest.mark.parametrize("strategy", ["distributed", "sequential"])
393436
def test_using_database_replica__all_replicas_unavailable__falls_back_to_default_database(
394437
django_assert_num_queries: DjangoAssertNumQueries,
395438
bad_replica: MockType,
396-
caplog: pytest.LogCaptureFixture,
439+
get_logs: GetLogsFixture,
397440
mocker: MockerFixture,
398441
settings: SettingsWrapper,
399442
strategy: str,
@@ -418,8 +461,21 @@ def test_using_database_replica__all_replicas_unavailable__falls_back_to_default
418461
# When / Then
419462
with django_assert_num_queries(1, using="default"):
420463
using_database_replica(manager).first()
421-
log = [r for r in caplog.records if r.name == "common.core.utils"][-1]
422-
assert (log.levelname, log.message) == (
423-
"WARNING",
424-
"No replicas available.",
464+
465+
logs = get_logs("common.core.utils")
466+
log_iterator = {"distributed": set, "sequential": list}[strategy]
467+
assert log_iterator(logs[0:3]) == log_iterator(
468+
[
469+
("ERROR", "Replica 'replica_1' is not available."),
470+
("ERROR", "Replica 'replica_2' is not available."),
471+
("ERROR", "Replica 'replica_3' is not available."),
472+
]
473+
)
474+
assert logs[3] == ("WARNING", "Falling back to cross-region replicas, if any.")
475+
assert log_iterator(logs[4:6]) == log_iterator(
476+
[
477+
("ERROR", "Replica 'cross_region_replica_1' is not available."),
478+
("ERROR", "Replica 'cross_region_replica_2' is not available."),
479+
]
425480
)
481+
assert logs[6] == ("WARNING", "No replicas available.")

0 commit comments

Comments
 (0)