Skip to content

feat(rollbar): opcjonalne wyciszenie błędów SMTP per instalacja#678

Open
mpasternak wants to merge 2 commits into
devfrom
fix/rollbar-wycisz-bledy-poczty
Open

feat(rollbar): opcjonalne wyciszenie błędów SMTP per instalacja#678
mpasternak wants to merge 2 commits into
devfrom
fix/rollbar-wycisz-bledy-poczty

Conversation

@mpasternak

Copy link
Copy Markdown
Member

Problem

Na bpp.umlub.pl serwer SMTP uczelni odrzuca poświadczenia:

smtplib.SMTPAuthenticationError: (535, b'5.7.3 Authentication unsuccessful')

To awaria po stronie administratorów poczty klienta — nie naprawimy jej kodem.
W Rollbarze: 60 wystąpień i rośnie
(#1554,
#379).

Rozwiązanie

Wyciszenie opt-in per instalacja, przez
DJANGO_BPP_ROLLBAR_IGNORE_SMTP_ERRORS (domyślnie wyłączone).

Domyślne wyłączenie jest tu istotne: na pozostałych instalacjach niedziałająca
poczta to realny problem — nie idą powiadomienia o zgłoszeniach publikacji do
redaktorów i nikt tego nie zauważa, dopóki ktoś nie zapyta. Globalne
wyciszenie zamieniłoby jeden hałaśliwy problem na jeden cichy.

Mechanizm: exception_level_filters pyrollbara z poziomem "ignored"
(rollbar._is_ignored). Wyciszamy całą rodzinę smtplib.SMTPException, bo
awaria „poczta nie działa" ma wiele wcieleń, nie tylko 535 — ale nic poza nią
(ConnectionRefusedError z Redisa czy PBN nadal leci).

Budowanie listy filtrów wyodrębnione do django_bpp/rollbar_filters.py jako
czysta funkcja — inaczej nie da się tego przetestować bez przeładowywania
ustawień Django.

Jak włączyć na UM Lublin

W konfiguracji deploymentu tej instalacji (bpp-deploy), w env appservera
i celery workera (wyjątek leci z workera — djcelery_email):

DJANGO_BPP_ROLLBAR_IGNORE_SMTP_ERRORS=1

Bez tego merge PR-a niczego nie zmienia.

Testy

src/django_bpp/tests/test_rollbar_filters.py — 6 testów:

  • domyślnie błędy SMTP raportowane,
  • flaga wycisza 535 i całą rodzinę (SMTPConnectError, SMTPServerDisconnected),
  • flaga nie wycisza ConnectionRefusedError ani ValueError,
  • settings.ROLLBAR faktycznie zawiera filtry (sama funkcja nic nie da, jeśli
    nikt jej nie zawoła) i domyślnie są puste,
  • kontrakt z pyrollbarem: rollbar._is_ignored honoruje nasz format krotek —
    bez tego literówka "ignore" zamiast "ignored" przeszłaby niezauważona.

Testy napisane po implementacji (mój błąd procesowy), więc zweryfikowałem je
mutacją kodu produkcyjnego — wszystkie trzy mutacje są łapane:

mutacja wynik
flaga ignorowana (zawsze pusta lista) 3 failed
"ignore" zamiast "ignored" 3 failed
Exception zamiast smtplib.SMTPException 2 failed

Dodatkowo sprawdzone end-to-end: z DJANGO_BPP_ROLLBAR_IGNORE_SMTP_ERRORS=1
settings.ROLLBAR["exception_level_filters"] to
[(<class 'smtplib.SMTPException'>, 'ignored')].

Uwaga

To nie naprawia poczty — powiadomienia o zgłoszeniach publikacji nadal nie
docierają do redaktorów UM Lublin. To wyłącznie decyzja, żeby nie raportować
awarii, której nie kontrolujemy. Sprawa SMTP wymaga zgłoszenia po stronie
klienta.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NcAqeqyqBzNEkkVnhpHDaH

mpasternak and others added 2 commits July 25, 2026 00:14
Na bpp.umlub.pl serwer SMTP uczelni odrzuca poświadczenia (535 5.7.3
Authentication unsuccessful) — awaria po stronie administratorów poczty
klienta, której nie naprawimy kodem. Efekt w Rollbarze: 60 wystąpień
(#1554, #379), rosnące.

Wyciszenie jest OPT-IN per instalacja przez
DJANGO_BPP_ROLLBAR_IGNORE_SMTP_ERRORS (domyślnie wyłączone), bo na
pozostałych instalacjach niedziałająca poczta to realny problem — nie idą
powiadomienia o zgłoszeniach publikacji do redaktorów i nikt tego nie
zauważa, dopóki ktoś nie zapyta.

Mechanizm: exception_level_filters pyrollbara z poziomem "ignored"
(rollbar._is_ignored). Wyciszamy CAŁĄ rodzinę smtplib.SMTPException, bo
awaria "poczta nie działa" ma wiele wcieleń, nie tylko 535 — ale nic poza
nią (ConnectionRefusedError z Redisa czy PBN nadal leci).

Budowanie listy filtrów wyodrębnione do django_bpp/rollbar_filters.py jako
czysta funkcja — inaczej nie da się tego przetestować bez przeładowywania
ustawień Django.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NcAqeqyqBzNEkkVnhpHDaH
Self-review obalił przesłankę, na której zbudowałem ten PR.

1. Twierdziłem, że "każde wystąpienie zakłada osobny item". NIEPRAWDA —
   sprawdzone w Rollbarze: item #379 ma total_occurrences=59, item #1554
   ma 1. Czyli 60 wystąpień to DWA itemy, nie 60; grupowanie działa
   poprawnie. Drugi item odszczepił się tylko dlatego, że między
   wystąpieniami poszła podbitka pyrollbara 1.3.0 → 1.4.0, zmieniając hash.

   To jednak nadal jest argument za wyciszeniem w kodzie, tylko INNY:
   "mute" w interfejsie Rollbara jest per item i przepada przy zmianie
   hasha (jak wyżej), a wyciszenie w repo jest trwałe i widoczne w gicie —
   da się je znaleźć i cofnąć, gdy klient naprawi pocztę. Uzasadnienie
   przepisane na prawdziwe.

2. Zakres zawężony z całej rodziny smtplib.SMTPException do samego
   SMTPAuthenticationError. Poprzedni zakres wyciszał SMTPRecipientsRefused
   (zły adres w NASZEJ bazie) i SMTPSenderRefused (nasza konfiguracja
   DEFAULT_FROM_EMAIL) — jedno i drugie jest do naprawienia po naszej
   stronie. Przy okazji: "cała rodzina błędów poczty" i tak było
   nieosiągalne, bo odmowa połączenia TCP leci jako ConnectionRefusedError,
   który nie jest podklasą SMTPException.

3. Test "kontraktu z pyrollbarem" celował w rollbar._is_ignored — funkcję,
   która w pyrollbar 1.4.0 NIE JEST NIGDZIE WYWOŁYWANA (jedyne wystąpienie
   to jej definicja). Realne tłumienie idzie przez _filtered_level →
   events.on_exception_info → filters.basic.filter_by_level. Testy jadą
   teraz prawdziwą ścieżką, z przechwyceniem send_payload. Zweryfikowane
   mutacją: wyłączenie flagi, rozszerzenie do SMTPException i literówka
   w poziomie — każde łapane.

4. Zmienna dopisana do .env.example (przełącznik per instalacja, którego
   nie widać w .env.example, jest dla wdrażającego nieodkrywalny) oraz
   ostrzeżenie w docstringu, że przy DJANGO_BPP_HOSTNAMES działa na
   wszystkie uczelnie w procesie.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NcAqeqyqBzNEkkVnhpHDaH
@mpasternak

Copy link
Copy Markdown
Member Author

Poprawki po self-review — moje uzasadnienie było fałszywe

Review podważył przesłankę tego PR-a i miał rację. Naniesione w 61f6813fc.

1. „Każde wystąpienie zakłada osobny item" — NIEPRAWDA

Sprawdziłem w Rollbarze: item #379 ma total_occurrences: 59, item #1554
ma 1. Czyli 60 wystąpień to dwa itemy, nie 60 — grupowanie działa
poprawnie. Drugi item odszczepił się wyłącznie dlatego, że między wystąpieniami
poszła podbitka pyrollbara 1.3.0 → 1.4.0 i zmieniła hash. Przeniosłem tu
wniosek z przypadku PermissionDenied (gdzie item faktycznie powstawał per
wystąpienie) na sytuację, w której on nie obowiązuje.

To jednak nadal jest argument za wyciszeniem w kodzie — tylko inny:
„mute" w interfejsie Rollbara jest per item i przepada przy zmianie hasha
(dokładnie jak wyżej), a wyciszenie w repo jest trwałe i widoczne w gicie
da się je znaleźć i cofnąć, gdy klient naprawi pocztę. Uzasadnienie w docstringu
i newsfragmencie przepisane na to.

2. Zakres zawężony do SMTPAuthenticationError

Poprzednie „cała rodzina smtplib.SMTPException" wyciszało też:

  • SMTPRecipientsRefused — zły adres e-mail w naszej bazie,
  • SMTPSenderRefusednasza konfiguracja DEFAULT_FROM_EMAIL.

Jedno i drugie jest do naprawienia po naszej stronie i musi być widoczne także
na instalacji z wyciszeniem. Przy okazji: deklarowany cel „cała rodzina błędów
poczty" i tak był nieosiągalny — odmowa połączenia TCP leci jako
ConnectionRefusedError/OSError, które nie są podklasami SMTPException.

Env-var przemianowany na DJANGO_BPP_ROLLBAR_IGNORE_SMTP_AUTH_ERRORS.

3. Test „kontraktu" testował martwy kod

rollbar._is_ignored nie jest w pyrollbar 1.4.0 nigdzie wywoływane
jedyne wystąpienie w pakiecie to jego własna definicja. Realne tłumienie idzie
przez _filtered_levelevents.on_exception_info(level=...)
filters.basic.filter_by_level. Mój test dawał więc fałszywą pewność dokładnie
tam, gdzie docstring obiecywał ją dawać.

Testy jadą teraz prawdziwą ścieżką raportowania, z przechwyceniem
send_payload. Zweryfikowane mutacją — każda łapana:

mutacja wynik
flaga ignorowana (pusta lista) 1 failed
rozszerzenie do SMTPException 1 failed
"ignore" zamiast "ignored" 1 failed

Stara wersja testów nie wykrywała mutacji „za szeroki zakres".

4. Drobiazgi

  • Zmienna dopisana do .env.example — przełącznik per instalacja, którego tam
    nie widać, jest dla wdrażającego nieodkrywalny.
  • Ostrzeżenie w docstringu: przy DJANGO_BPP_HOSTNAMES (multi-tenant w jednym
    procesie) ustawienie działa na wszystkie uczelnie tego procesu. Dla
    bpp.umlub.pl to nie problem — payload pokazuje tryb single-host i dedykowany
    kontener z własnym workerem.

Świadoma decyzja, mimo zastrzeżenia review

Review argumentuje, żeby PR-a nie mergować, bo wyciszenie usuwa jedyny sygnał,
że redakcja UM Lublin nie dostaje powiadomień o zgłoszeniach publikacji.
To prawda i jest to realna szkoda — payload pokazuje wprost pięć adresów, do
których nie dochodzi poczta.

Zostawiam PR, bo to była świadoma decyzja właściciela produktu: awaria jest
znana, leży po stronie informatyków klienta i jest zgłoszona. Ale odnotowuję
wprost: to nie naprawia poczty i dopóki klient nie odblokuje SMTP-a,
zgłoszenia publikacji będą lądować w bazie niezauważone. Warto wrócić do tej
zmiennej po naprawie i ją usunąć.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant