1. Loq mesajının strukturu
Gəlin təsəvvür edək ki, loqlar — proqramınızın sadəcə «şüur axını» deyil, elə bir dəyərli jurnaldır ki, bir ay və ya bir il sonra siz və ya həmkarınız «Burada nə baş verib axı?» sualının cavabını tapa bilsin. Bunun mümkün olması üçün hər loq mesajı strukturlu olmalıdır. Adətən (və bu, kitabxınaların çoxunda standartdır) hər mesaj aşağıdakıları ehtiva edir:
- Hadisənin vaxtı — nə vaxt baş verdiyi.
- Səviyyə — bunun nə dərəcədə vacib olduğu (INFO, ERROR və s.).
- Logger-in adı — adətən bu, sinifin və ya komponentin adıdır.
- Mesaj mətni — dəqiq nə baş verdiyi.
- Stack trace (səhv varsa) — harada və niyə baş verdiyini anlamaq üçün.
Log4j/SLF4J nümunəsində yaxşı formatlanmış loq sətiri:
2024-06-16 18:42:07,123 INFO com.example.MainApp - İstifadəçi sistemə daxil oldu: username=vasya
Əgər səhv baş veribsə:
2024-06-16 18:42:10,456 ERROR com.example.LoginService - İstifadəçinin avtorizasiyasında səhv: vasya
java.lang.IllegalArgumentException: Yanlış şifrə
at com.example.LoginService.checkPassword(LoginService.java:42)
...
Bu nə üçün vacibdir?
Tətbiq uzun müddət işləyəndə loqlar qiqabaytlarla yer tuta bilər. Mesajlar strukturlu deyilsə, problemi tapmaq «ventilyatorun səsindən melodiyanı tapmaq» səviyyəsində bir tapmacaya çevrilir.
2. Mesajların formatlanması
Niyə belə etmək olmaz:
logger.info("İstifadəçi " + username + " sistemə daxil oldu");
Hər şey sadə görünür, amma burada bir məqam var: loq səviyyəsi indi ERROR olsa belə, mötərizənin içindəki sətir yenə də yığılacaq (birləşdirmə işləyəcək), bu isə resursların artıq sərfidir. Saniyədə minlərlə sətirin loqlandığı böyük sistemlərdə bu, real gecikmələrə səbəb ola bilər.
Düzgün yanaşma: şablonlar və parametrlər
Müasir kitabxanalar (məsələn, SLF4J və Log4j 2) parametrli şablonları dəstəkləyir:
logger.info("İstifadəçi {} sistemə daxil oldu", username);
Burada sətir yalnız loq səviyyəsi bu mesajı çıxarmağa imkan verirsə formalaşdırılacaq. Məsələn, WARN qoyulubsa, sətir ümumiyyətlə hesablanmayacaq — resurs və əsəb qənaəti.
Bonus: bir neçə parametr ötürsəniz, onlar sırayla yerləşdiriləcək:
logger.info("İstifadəçi {} {} əməliyyatını {} obyektində icra etdi", username, action, objectId);
İstisnaların loqlaşdırılması (stack trace)
İstisnanı tutursunuzsa, stack trace-i əllə mesaja əlavə etməyin:
// ETMƏYİN:
logger.error("Xəta: " + ex.getMessage() + "\n" + Arrays.toString(ex.getStackTrace()));
Düzgün:
logger.error("Sorğunun emalı zamanı səhv", ex);
SLF4J və Log4j stack trace-i loqa özləri səliqəli şəkildə əlavə edəcək.
Nümunə: yanaşmaların müqayisəsi
// Pis (birləşdirmə həmişə icra olunur)
logger.debug("Obyekt: " + expensiveToString(obj));
// Yaxşı (tənbəl formalaşdırma)
logger.debug("Obyekt: {}", obj);
3. Loq səviyyələrinin seçimi
Əgər loqlarda hər şey ERROR səviyyəsindədirsə, bu artıq loq deyil, «qırmızı xəbərdarlıq lampasıdır». Hər şey DEBUG-dirsə, detallarda boğulacaqsınız. Gəlin hansı səviyyəni nə vaxt istifadə etməyi aydınlaşdıraq.
| Səviyyə | Nə üçün istifadə olunur | Mesaj nümunəsi |
|---|---|---|
|
Sistemin səhv işləməsinə və ya ümumiyyətlə işləməməsinə səbəb olan kritik nasazlıqlar | «Məlumat bazasına qoşulma xətası» |
|
Vacib xəbərdarlıqlar, kritik deyil, amma diqqət tələb edir | «İstifadəçini tapmaq mümkün olmadı, guest istifadə olunur» |
|
Tətbiqin normal işini əks etdirən adi hadisələr | «İstifadəçi qeydiyyatdan keçdi: vasya» |
|
Sazlama üçün ətraflı məlumat, production-da lazım deyil | «checkPassword metodu parametrlərlə çağrıldı ...» |
|
Ən detallı məlumat, adətən dərin diaqnostika üçün | «Emal dövrünün başlanğıcı: i=0» |
Tipik mesaj nümunələri
- ERROR — fayl yazıla bilmədi, emal olunmamış istisna tutuldu, servis əlçatan deyil.
- WARN — köhnəlmiş (deprecated) API, şübhəli istifadəçi davranışı, cəhd limiti aşılıb.
- INFO — istifadəçi daxil oldu/çıxdı, sifarişin emalı başa çatdı, tətbiqin startı.
- DEBUG — sorğu parametrləri, dəyişənlərin dəyərləri, hesablamaların aralıq nəticələri.
- TRACE — metodlara giriş/çıxış, daxili döngələr, alqoritmlərin işinin təfərrüatları.
Məsləhət:
Production mühitində adətən yalnız INFO və yuxarı səviyyələr açıq olur, bəzən WARN və ERROR. DEBUG və TRACE — yalnız mürəkkəb bug-ların axtarışı zamanı.
4. Best practices (loqlaşdırma üçün ən yaxşı təcrübələr)
Həssas məlumatları loqa yazmayın
Parollar, tokenlər, kredit kartı nömrələri — bunların heç birinin loqlarda yeri yoxdur. «Loq faylı — yalnız mənim üçündür» kimi görünsə belə, GDPR-ı və loqu təsadüfən ümumi çata göndərən həmkarınızı xatırlayın.
// Pis:
logger.info("İstifadəçi {} parol {} ilə daxil oldu", username, password);
// Yaxşı:
logger.info("İstifadəçi {} sistemə daxil oldu", username);
ERROR səviyyəsini sui-istifadə etməyin
Hər şeyi logger.error ilə yazırsınızsa, həqiqətən fəlakət olanda bunu heç kim görməyəcək — hamı «qırmızı lampalara» öyrəşəcək. ERROR səviyyəsini yalnız tətbiq həqiqətən işi davam etdirə bilməyəndə və ya biznes məntiqi pozulanda istifadə edin.
İstisnaları tam stack ilə loqlaşdırın
Təkcə ex.getMessage() yazmayın, yoxsa səhvin dəqiq harada baş verdiyini heç vaxt bilməyəcəksiniz. İstisnanı logger-ə ikinci parametr kimi ötürün.
logger.error("Sorğunun emalı zamanı səhv", ex);
Unikal identifikatorlardan istifadə edin (hadisələrin korrelyasiyası)
Böyük sistemlərdə hər bir sorğuya, istifadəçiyə və ya əməliyyata unikal identifikator vermək faydalıdır. Bu, sistemin müxtəlif hissələrindən gələn hadisələri «birləşdirməyə» kömək edəcək.
logger.info("Sifarişin emalına başlanıldı: orderId={}", orderId);
logger.info("Sifariş uğurla emal olundu: orderId={}", orderId);
Hər şeyi loqa yazmayın
Loqlar həddindən artıq çoxdursa — onlar faydasız olur. Hər sətiri loqlaşdırmayın, əks halda lazım olan məlumatı tapmaq mümkünsüzləşəcək.
Mesajları aydın formatlayın
Mesajları elə yazın ki, onu təkcə kodun müəllifi deyil, loqları altı aydan sonra oxuyacaq şəxs də başa düşsün. Abreviaturalardan, qeyri-aşkar qısaltmalardan və «öz aramızda zarafatlardan» qaçın.
5. Təcrübə: loqun formatını və səviyyələri qurmaq
Log4j2-də formatın nümunə konfiqurasiyası (log4j2.xml)
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Bu nə deməkdir?
- %d{...} — hadisənin vaxtı.
- %-5level — səviyyə (ERROR, INFO və s.).
- %logger{36} — logger-in adı (adətən sinif).
- %msg — mesajın özü.
Müxtəlif loq səviyyələri ilə kod nümunəsi (SLF4J)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogDemo {
private static final Logger logger = LoggerFactory.getLogger(LogDemo.class);
public static void main(String[] args) {
logger.info("Tətbiq işə salındı");
logger.debug("x dəyişəninin dəyəri: {}", 42);
try {
throw new IllegalArgumentException("Ay-ay-ay!");
// ...
} catch (Exception ex) {
logger.error("İşə salınma zamanı xəta baş verdi", ex);
}
}
}
Səviyyələr arasındakı fərqin nümayişi
Əgər logger konfiqurasiyasında INFO səviyyəsi qoyulubsa, DEBUG və daha aşağı səviyyəli mesajlar çıxarılmayacaq. Konfiqdə səviyyəni debug olaraq dəyişməyə çalışın — detalları görəcəksiniz.
6. Tipik səhvlər
Səhv №1: Loqlarda sətirlərin birləşdirilməsi. Tez-tez yeni başlayanlar belə yazır:
logger.debug("İstifadəçi: " + user.getName() + ", rol: " + user.getRole());
Nəticədə DEBUG səviyyəsi söndürülü olsa belə bu sətirlər yığılacaq və əlavə yüklənməyə səbəb olacaq. Parametrlərdən istifadə edin!
Səhv №2: İstisnanın stack olmadan loqlaşdırılması. Yalnız mesaj yazılır:
logger.error("Xəta: " + ex.getMessage());
Nəticədə loqlarda səhvin dəqiq harada baş verdiyi barədə məlumat olmur. Exception-u ikinci parametr kimi ötürün!
Səhv №3: Hər şeyi ERROR səviyyəsində loqlaşdırmaq. Hamısı qırmızıdırsa — heç nə qırmızı deyil. Səviyyələri təyinatı üzrə istifadə edin, əks halda vacib xətalar «xırdalıqlar» arasında itəcək.
Səhv №4: Həssas məlumatların loqlaşdırılması. Heç vaxt loqlara parolları, tokenləri, kart nömrələrini yazmayın. Heç kim görməyəcək kimi görünsə də, həyat sürprizləri sevir.
Səhv №5: Anlaşılmaz mesajlar. Əgər loqdakı mesaj «ERR42: fail» kimidirsə, bir aydan sonra özünüz də bunun nə demək olduğunu xatırlamayacaqsınız. Aydın və ətraflı yazın.
Səhv №6: Unikal identifikatorların olmaması. Mürəkkəb sistemlərdə orderId, userId və digər identifikatorlar olmadan hadisələri «birləşdirə» və konkret istifadəçi və ya sifarişlə nə baş verdiyini anlaya bilməyəcəksiniz.
GO TO FULL VERSION