Przejdź do głównej zawartości

Wzorce logowania

Twoje API zwraca losowe błędy 500 na produkcji. „Logi” to nieustrukturyzowane ciągi z console.log, nie potrafisz prześledzić pojedynczego żądania przez trzy usługi, a twój inżynier dyżurny grepuje czysty tekst o 3 nad ranem. Rozwiązaniem nie jest pisanie frameworka do logowania ręcznie — chodzi o to, by twoje narzędzie AI zamieniło to, co już masz, w strukturalne, skorelowane i zredagowane logi w jednym przebiegu.

Ten przepis pokazuje prompty i pętlę przeglądu. Ręcznie pisana klasa loggera to coś, co AI usuwa, a nie coś, co wdrażasz.

  • Prompt, który zamienia każdy console.log w repozytorium na strukturalne logowanie winston (lub pino) z formatterem JSON, defaultMeta i warstwą redakcji
  • Prompt, który dodaje middleware korelujące żądania, dzięki czemu jedno requestId podąża za żądaniem przez każde wywołanie downstream za pomocą AsyncLocalStorage
  • Prompt, który audytuje twoją bazę kodu pod kątem instrukcji logujących sekrety i dane osobowe, zanim trafią na dysk
  • Tryby awarii, które sprawiają, że logowanie wygenerowane przez AI wygląda dobrze podczas przeglądu, a psuje się na produkcji

Błędem jest proszenie AI o „dodanie logowania”. Dostajesz porozrzucane wywołania logger.info bez żadnego schematu. Zamiast tego przeprowadź trzy oddzielne przebiegi: wybierz bibliotekę, uczyń logi strukturalnymi i zredagowanymi, a potem skoreluj je. Oceń diff po każdym przebiegu.

Krok 1: Wybierz utrzymywaną bibliotekę, a nie autorską klasę

Dział zatytułowany „Krok 1: Wybierz utrzymywaną bibliotekę, a nie autorską klasę”

Nie pozwól, by AI samodzielnie sklecił SamplingLogger czy MultiTransportLogger. Zarówno winston, jak i pino już rozwiązują kwestie transportów, poziomów, formatowania i redakcji — i są utrzymywane. pino to szybszy domyślny wybór (serializuje poza gorącą ścieżką); winston jest bardziej elastyczny, jeśli potrzebujesz wielu transportów. Wskaż ten wybór w swoim prompcie, żeby AI nie wymyślało koła na nowo.

Krok 2: Zamień console.log na strukturalne, zredagowane logowanie

Dział zatytułowany „Krok 2: Zamień console.log na strukturalne, zredagowane logowanie”

To mechaniczny refaktor wielu plików — dokładnie to, w czym dobry jest tryb agentowy i przebiegi headless. Prompt jest w zasadzie identyczny dla wszystkich trzech narzędzi; różni się tylko sposób uruchomienia.

Otwórz tryb agentowy (Cmd/Ctrl+I), wskaż mu src/ i wklej poniższy prompt do strukturalnego logowania. Cursor edytuje każde miejsce wywołania w plikach w jednym przebiegu; przejrzyj każdą zmianę w widoku diff i użyj punktu kontrolnego przed akceptacją, abyś mógł wycofać cały refaktor, jeśli schemat jest błędny. Trzymaj otwarty src/lib/logger.ts jako plik kotwiczący, aby agent scentralizował konfigurację tam, zamiast definiować logger osobno w każdym module.

Oto sam prompt. Jest celowo stanowczy — niejasny prompt „dodaj logowanie” to właśnie to, co stworzyło ten nieprzeszukiwalny bałagan, od którego zaczynałeś.

Gdy diff zostanie naniesiony, oceń go pod kątem trzech pytań, zanim go zaakceptujesz:

  1. Czy komunikat to stała, przeszukiwalna wartość? logger.info('User login') da się zgrepować; logger.info(`User ${id} logged in`) — nie. Odrzucaj interpolowane komunikaty — części dynamiczne należą do obiektu metadanych.
  2. Czy redakcja trafiła do wspólnego formattera, a nie do instrukcji delete przy każdym wywołaniu? Jeśli AI rozsiało ręczne linie delete obj.password, odeślij je z powrotem: redakcja musi być formatem stosowanym do każdego logu, inaczej zostanie zapomniana przy kolejnej linii logu, którą ktoś doda.
  3. Czy poziomy mają sens? Obsłużony błąd walidacji to warn, nieobsłużone 500 to error. Jeśli wszystko stało się info, twoje alertowanie jest teraz bezużyteczne.

Krok 3: Dodaj korelację żądań, która przetrwa granice asynchroniczne

Dział zatytułowany „Krok 3: Dodaj korelację żądań, która przetrwa granice asynchroniczne”

requestId, które istnieje wyłącznie na req, jest bezwartościowe w momencie, gdy wywołujesz funkcję usługi, która nie przyjmuje req. Solidnym wzorcem jest AsyncLocalStorage: zapisz kontekst żądania raz w middleware, a potem dowolny logger w dowolnym miejscu stosu wywołań odczyta go bez przeciągania req przez każdą sygnaturę. Zmuś AI, by użyło go jawnie — zostawione samo sobie będzie przekazywać req.logger dookoła i zgubi identyfikator przy pierwszym setTimeout lub odłączonym promisie.

Klauzula „udowodnij to” ma znaczenie: zmusza AI do zademonstrowania propagacji przez granicę asynchroniczną, zamiast twierdzenia, że to działa. Jeśli nie potrafi pokazać identyfikatora pojawiającego się w funkcji, która nigdy nie otrzymała req, to implementacja jest tym kruchym wariantem.

Krok 4: Audytuj pod kątem wycieku sekretów i danych osobowych

Dział zatytułowany „Krok 4: Audytuj pod kątem wycieku sekretów i danych osobowych”

Redakcja po nazwie klucza jest konieczna, ale niewystarczająca — adresy e-mail i IP to dane osobowe w rozumieniu RODO i nie wyłapie ich matcher na password/token. Jako ostatni krok przeprowadź dedykowany przebieg audytowy.

Oto tryby awarii, które przechodzą przegląd kodu i wychodzą na jaw o 3 nad ranem.

  • AI loguje pełne ciała żądań „do debugowania”. Pojedyncze logger.info('request', { body: req.body }) zrzuci hasła, tokeny i numery kart do twojego magazynu logów, gdy tylko ktoś trafi na trasę rejestracji. Redakcja po kluczu cię nie uratuje, jeśli całe ciało jest zagnieżdżone pod jednym niewinnie wyglądającym kluczem. Zabroń logowania surowych ciał w prompcie i zweryfikuj, że tabela audytu z Kroku 4 jest pusta, zanim wdrożysz.
  • Kontekst logger.child() jest tracony przez granice asynchroniczne. Logger potomny powiązany w middleware nie podąża automatycznie do setTimeout, odłączonego .then() ani workera kolejki. To dokładnie dlatego Krok 3 używa AsyncLocalStorage zamiast req.logger — jeśli widzisz identyfikator korelacji obecny w logu żądania, ale brakujący w logu usługi downstream, to trafiłeś na ten problem, a rozwiązaniem jest odczyt kontekstu z magazynu, a nie z przekazanego loggera potomnego.
  • Wszystko stało się info, więc alertowanie to szum. Jeśli refaktor spłaszczył wszystkie poziomy, twój alert „loguj przy błędach” albo nigdy się nie odpala, albo odpala przy każdym żądaniu. Uruchom ponownie pytanie o przegląd poziomów z Kroku 2 i każ AI przeklasyfikować: warn dla obsłużonych/4xx, error dla nieobsłużonych/5xx, debug dla diagnostyki o wysokiej częstotliwości.
  • Synchroniczne logowanie na gorącej ścieżce dodaje opóźnienia. Zapis do wolnego transportu (plik na zasobie sieciowym, zdalny odbiornik HTTP) w linii z żądaniem blokuje je. Użyj pino (asynchronicznego z założenia) lub buforowanego/asynchronicznego transportu dla winston i nigdy nie rób await na zapisie logu w handlerze żądania.
  • MCP utrzymywanej biblioteki odpowiedziałoby za ciebie na pytanie o konfigurację. Jeśli AI zgaduje API winston lub pino i myli się w subtelny sposób, podłącz Context7 MCP (@upstash/context7-mcp) i dodaj do promptu „use Context7 for the winston/pino docs” — wciąga aktualną dokumentację biblioteki do kontekstu, dzięki czemu wygenerowana konfiguracja pasuje do zainstalowanej wersji, a nie do zmyślonej.