JavaRush /Java blogi /Random-UZ /Java-ga kirish: nima, qanday, qaerda va nima bilan?

Java-ga kirish: nima, qanday, qaerda va nima bilan?

Guruhda nashr etilgan
Hammaga salom, JavaRush jamoasi! Bugun biz Java jurnali haqida gaplashamiz:
  1. Bu nima, nima uchun. Qanday hollarda foydalanish yaxshiroq, qanday hollarda emas?
  2. Java-da qanday ro'yxatga olish dasturlari mavjud va bu xilma-xillik bilan nima qilishimiz kerak?
  3. Jurnal darajalari. Keling, ilova nima ekanligini va uni qanday qilib to'g'ri sozlashni muhokama qilaylik.
  4. Jurnal tugunlari va ularni qanday qilib to'g'ri sozlash kerak, shunda hamma narsa biz xohlagan tarzda ishlaydi.
Ushbu material keng auditoriya uchun mo'ljallangan. Bu Java bilan endigina tanishayotganlar uchun ham, ishlayotganlar uchun ham tushunarli bo'ladi, lekin buni faqat logger.info(“log something”); Let's Go bilan tushunib yetganlar uchun!

Nega jurnalga kirish kerak?

Keling, ro'yxatga olish muammoni hal qiladigan haqiqiy holatlarni ko'rib chiqaylik. Mana mening ishimdan bir misol. Boshqa xizmatlar bilan integratsiyalashgan dastur nuqtalari mavjud. Men ushbu nuqtalarni ro'yxatga olishni "alibi" sifatida ishlataman : agar integratsiya ishlamasa, muammo qaysi tomondan kelib chiqqanligini aniqlash oson bo'ladi. Shuningdek, ma'lumotlar bazasida saqlangan muhim ma'lumotlarni jurnalga kiritish tavsiya etiladi. Masalan, administrator foydalanuvchisini yaratish. Aynan shu narsa jurnalga kirish yaxshi bo'lardi.

Java jurnali vositalari

Jurnal: nima, qanday, qaerda va nima bilan?  - 2Java-ga kirish uchun taniqli echimlar quyidagilarni o'z ichiga oladi:
  • log4j
  • JUL - java.util.logging
  • JCL - Jakarta commons logging
  • Qayta tiklash
  • SLF4J - java uchun oddiy logging jabhasi
Keling, ularning har birini tez ko'rib chiqaylik va materialning amaliy qismida biz Slf4j - log4j ulanishini asos qilib olamiz . Bu hozir g'alati tuyulishi mumkin, lekin tashvishlanmang: maqolaning oxiriga kelib hamma narsa aniq bo'ladi.

System.err.println

Dastlab, albatta, System.err.println (konsolga chiqishni yozib olish) mavjud edi. Hali ham disk raskadrovka paytida jurnalni tezda olish uchun ishlatiladi. Albatta, bu erda hech qanday sozlamalar haqida gapirishning hojati yo'q, shuning uchun uni eslaylik va davom etaylik.

Log4j

Bu allaqachon ishlab chiquvchilarning ehtiyojlaridan kelib chiqqan holda yaratilgan to'liq huquqli yechim edi. Bu foydalanish uchun juda qiziqarli vosita bo'lib chiqdi. Turli xil holatlar tufayli, bu yechim hech qachon JDK ga kirmadi, bu butun jamiyatni juda xafa qildi. log4j konfiguratsiya imkoniyatlariga ega edi, shuning uchun jurnalni paketda yoqish com.example.typeva kichik paketda o'chirish mumkin edi com.example.type.generic. Bu tizimga kiritilishi kerak bo'lgan narsalarni keraksiz narsalardan tezda ajratish imkonini berdi. Shuni ta'kidlash kerakki, log4j ning ikkita versiyasi mavjud: 1.2.x va 2.x.x, ular bir-biriga mos kelmaydi . log4j ilovasi kabi kontseptsiyani qo'shdi , ya'ni jurnallar yozib olinadigan va tartib - jurnalni formatlash vositasi. Bu sizga kerak bo'lgan narsalarni va qanday kerakligini yozib olish imkonini beradi. Ilova haqida biroz keyinroq gaplashamiz.

JUL - java.util.logging

Asosiy afzalliklardan biri bu yechim - JUL JDK (Java Development Kit) tarkibiga kiritilgan. Afsuski, uning rivojlanishi davomida mashhur log4j emas, balki IBM kompaniyasining yechimi uning rivojlanishiga ta'sir ko'rsatdi. Aslida, hozirda JUL bor, lekin hech kim undan foydalanmaydi. "So-so" dan: Iyul oyida logging darajalari Logback, Log4j, Slf4jdagidan farq qiladi va bu ular orasidagi tushunishni yomonlashtiradi. Logger yaratish ko'proq yoki kamroq o'xshash. Buning uchun siz import qilishingiz kerak:
java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
Sinf nomi jurnalning qayerdan kelayotganini bilish uchun maxsus berilgan. Java 8 dan boshlab, o'tish mumkin Supplier<String>. Bu avvalgidek har doim emas, balki haqiqatan ham zarur bo'lgan paytdagi satrni hisoblash va yaratishga yordam beradi. Faqatgina Java 8-ning chiqarilishi bilan ishlab chiquvchilar muhim muammolarni hal qilishdi, shundan so'ng JUL haqiqatan ham foydalanishga yaroqli bo'ldi. Ya'ni, argumentli usullar Supplier<String> msgSupplierquyida ko'rsatilgan:
public void info(Supplier<String> msgSupplier) {
   log(Level.INFO, msgSupplier);
}

JCL - Jakarta commons logging

Uzoq vaqt davomida daraxt kesishda sanoat standarti mavjud emasligi va ko'p odamlar o'zlarining shaxsiy loggerlarini yaratgan davr bo'lganligi sababli, ular JCL-ni - boshqalarga nisbatan qo'llaniladigan umumiy o'rashni chiqarishga qaror qilishdi. Nega? Loyihaga ba'zi bog'liqliklar qo'shilsa, ular loyihadagi loggerdan boshqa loggerdan foydalanishlari mumkin edi. Shu sababli, ular loyihaga o'tish davri bilan qo'shildi, bu esa barchasini birlashtirishga harakat qilishda haqiqiy muammolarni keltirib chiqardi. Afsuski, o'ram funktsional jihatdan juda yomon edi va hech qanday qo'shimchalar kiritmadi. Har kim o'z ishini qilish uchun JCL dan foydalansa, ehtimol qulay bo'lar edi. Lekin aslida bu shunday bo'lmadi, shuning uchun JCL dan foydalanish hozircha yaxshi fikr emas.

Qayta tiklash

Ochiq manba yo'li naqadar mashaqqatli... Logback uning davomchisini yaratish uchun log4j bilan bir xil dasturchi tomonidan yozilgan. Fikr log4j bilan bir xil edi. Logbackdagi farqlar quyidagilar edi:
  • yaxshilangan ishlash;
  • slf4j uchun mahalliy yordam qo'shildi;
  • Filtrlash opsiyasi kengaytirildi.
Odatiy bo'lib, tizimga kirish hech qanday sozlamalarni talab qilmaydi va DEBUG va undan yuqori darajadagi barcha jurnallarni yozib oladi. Agar konfiguratsiya kerak bo'lsa, u xml konfiguratsiyasi orqali amalga oshirilishi mumkin:
<configuration>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>app.log</file>
        <encoder>
            <pattern>%d{HH:mm:ss,SSS} %-5p [%c] - %m%n</pattern>
        </encoder>
    </appender>
    <logger name="org.hibernate.SQL" level="DEBUG" />
    <logger name="org.hibernate.type.descriptor.sql" level="TRACE" />
    <root level="info">
        <appender-ref ref="FILE" />
    </root>
</configuration>

SLF4J - java uchun oddiy logging jabhasi

Taxminan 2006 yilda log4j asoschilaridan biri loyihani tark etdi va slf4j - Java uchun Simple Logging Facade ni yaratdi - log4j, JUL, umumiy logginlar va logback atrofidagi o'rash. Ko'rib turganingizdek, taraqqiyot shu darajaga yetdiki, ular o'ramning tepasida o'ram yaratdilar... Bundan tashqari, u ikki qismga bo'lingan: ilovada ishlatiladigan API va ilova sifatida qo'shiladigan amalga oshirish. ro'yxatga olishning har bir turi uchun alohida bog'liqliklar. Masalan, slf4j-log4j12.jar, slf4j-jdk14.jar. To'g'ri amalga oshirishni ulash kifoya va bu: butun loyiha u bilan ishlaydi. Slf4j barcha yangi xususiyatlarni qo'llab-quvvatlaydi, masalan, jurnalni yozish uchun string formatlash. Ilgari bunday muammo bor edi. Aytaylik, log yozuvi mavjud:
log.debug("User " + user + " connected from " + request.getRemoteAddr());
Satrlarni birlashtirish tufayli ob'ektda useryashirin konversiya mavjud user.toString()va bu vaqt talab etadi, bu esa tizimni sekinlashtiradi. Va agar dasturni disk raskadrovka qilsak, hammasi yaxshi bo'ladi. Agar ushbu sinf uchun ro'yxatga olish darajasi INFO va undan yuqori bo'lsa, muammolar boshlanadi. Ya'ni, bu jurnal yozilmasligi kerak va satrlarni birlashtirish ham amalga oshirilmasligi kerak. Nazariy jihatdan, buni jurnal kutubxonasining o'zi hal qilishi kerak edi. Bundan tashqari, bu log4j ning birinchi versiyasining eng katta muammosi bo'lib chiqdi. Ular oddiy yechimni keltirmadilar, lekin buni shunday qilishni taklif qilishdi:
if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
Ya'ni, bitta jurnal chizig'i o'rniga 3(!) yozishni taklif qilishdi. Ro'yxatga olish koddagi o'zgarishlarni minimallashtirishi kerak va uchta qator umumiy yondashuvga aniq ziddir. slf4j-da JDK va API bilan moslik muammosi yo'q edi, shuning uchun darhol chiroyli yechim paydo bo'ldi:
log.debug("User {} connected from {}", user, request.getRemoteAddr());
bu erda {}usulda uzatiladigan argumentlar qo'shimchalarini bildiradi. Ya'ni, birinchi {}mos keladi user, ikkinchisi {}- request.getRemoteAddr(). Shu sababli, agar jurnal darajasi jurnalga yozishga ruxsat bergan bo'lsa, bu xabarni bitta xabarga birlashtirish mumkin. Shundan so'ng, SJF4J tezda mashhurlikka erishdi va hozirda eng yaxshi yechim hisoblanadi. Shuning uchun, biz to'plam misolidan foydalanib, jurnalga kirishni ko'rib chiqamiz slf4j-log4j12.

Nimani qayd qilish kerak

Albatta, siz hamma narsani qayd qilmasligingiz kerak. Ba'zida bu keraksiz va hatto xavfli. Misol uchun, agar siz kimningdir shaxsiy ma'lumotlarini garovga qo'ysangiz va u qandaydir tarzda oshkor bo'lsa, ayniqsa G'arbga yo'naltirilgan loyihalarda haqiqiy muammolar paydo bo'ladi. Ammo ro'yxatga olish majburiy bo'lgan narsa ham bor :
  1. Ilovaning boshlanishi/tugashi. Biz bilishimiz kerakki, dastur aslida biz kutgandek ishga tushirildi va kutilgandek tugadi.
  2. Xavfsizlik masalalari. Bu erda parolni taxmin qilish urinishlarini, muhim foydalanuvchilarning loginlarini va hokazolarni qayd qilish yaxshi bo'lar edi.
  3. Ba'zi dastur holatlari . Masalan, biznes jarayonida bir holatdan ikkinchi holatga o'tish.
  4. Tegishli ro'yxatga olish darajasi bilan disk raskadrovka uchun ba'zi ma'lumotlar .
  5. Ba'zi SQL skriptlari. Bu zarur bo'lgan haqiqiy holatlar mavjud. Shunga qaramay, darajalarni mohirona sozlash orqali ajoyib natijalarga erishish mumkin.
  6. Bajarilgan ish zarralari (Thread) to'g'ri ishlashi tekshirilgan hollarda jurnalga kiritilishi mumkin.

Ro'yxatga olishning mashhur xatolari

Ko'p nuanslar mavjud, ammo bu erda bir nechta keng tarqalgan xatolar mavjud:
  1. Ortiqcha ro'yxatga olish. Siz nazariy jihatdan muhim bo'lishi mumkin bo'lgan har bir qadamni qayd qilmasligingiz kerak. Qoida bor: jurnallar unumdorlikni 10% dan ko'p bo'lmagan yuklashi mumkin. Aks holda ishlash muammolari bo'ladi.
  2. Barcha ma'lumotlarni bitta faylga kiritish. Bu ma'lum bir nuqtada uni o'qish/yozishni juda qiyinlashtiradi, ba'zi tizimlarda fayl hajmi cheklovlari mavjudligini eslatib o'tmaslik kerak.
  3. Noto'g'ri ro'yxatga olish darajalaridan foydalanish. Har bir jurnal darajasi aniq chegaralarga ega va ularga rioya qilish kerak. Agar chegara noaniq bo'lsa, qaysi darajadan foydalanish haqida kelishib olishingiz mumkin.

Jurnal darajalari

x: Ko'rinadigan
O'LIM XATO OGOHLANTIRISH MA'LUMOT DEBUG IZ HAMMA
O‘CHIRISH
O'LIM x
XATO x x
OGOHLANTIRISH x x x
MA'LUMOT x x x x
DEBUG x x x x x
IZ x x x x x x
HAMMA x x x x x x x
Ro'yxatga olish darajalari qanday? Qandaydir tarzda jurnallarni tartiblash uchun ma'lum belgilar va farqlarni berish kerak edi. Shu maqsadda loging darajalari joriy etildi. Daraja ilovada o'rnatiladi. Agar yozuv belgilangan darajadan pastroq darajaga tegishli bo'lsa, u jurnalga kiritilmaydi. Misol uchun, bizda dasturni disk raskadrovka qilish uchun ishlatiladigan jurnallar mavjud. Oddiy ishlab chiqarish ishlarida (ilova mo'ljallangan maqsadda foydalanilganda), bunday jurnallar kerak emas. Shuning uchun jurnalga yozish darajasi disk raskadrovkadan yuqori bo'ladi. Misol sifatida log4j dan foydalangan holda darajalarni ko'rib chiqaylik. JULdan tashqari boshqa echimlar bir xil darajadan foydalanadi. Bu erda ular kamayish tartibida:
  • OFF: hech qanday jurnal yozilmaydi, barchasi e'tiborga olinmaydi;
  • FATAL: xato, shundan so'ng dastur endi ishlay olmaydi va to'xtatiladi, masalan, JVM xotirada xatolik;
  • XATO: hal qilinishi kerak bo'lgan muammolar mavjud bo'lganda xatolik darajasi. Xato butun dasturni to'xtatmaydi. Boshqa so'rovlar to'g'ri ishlashi mumkin;
  • OGOHLANTIRISH: Ogohlantirishni o'z ichiga olgan jurnallarni ko'rsatadi. Kutilmagan harakat yuz berdi, shunga qaramay tizim qarshilik ko'rsatdi va so'rovni bajardi;
  • INFO: ilovadagi muhim amallarni qayd qiluvchi jurnal. Bu xatolar emas, bu ogohlantirishlar emas, bu tizimning kutilgan harakatlari;
  • DEBUG: ilovani disk raskadrovka qilish uchun kerak bo'lgan jurnallar. Tizim undan kutilgan narsani to'liq bajarishini ta'minlash yoki tizimning harakatini tavsiflash uchun: "metod1 ishlay boshladi";
  • TRACE: disk raskadrovka uchun pastroq ustuvor jurnallar, eng past qayd darajasi;
  • ALL: tizimdagi barcha jurnallar yozib olinadigan daraja.
Ma'lum bo'lishicha, agar ilovaning biron bir joyida INFO jurnali darajasi yoqilgan bo'lsa, INFOdan tortib, FATALgacha bo'lgan barcha darajalar qayd qilinadi. Agar ro'yxatga olish darajasi O'LIM bo'lsa, faqat shu darajadagi jurnallar yozib olinadi.

Jurnallarni yozib olish va yuborish: Appender

Biz ushbu jarayonni log4j misolida ko'rib chiqamiz: u jurnallarni yozib olish/yuborish uchun keng imkoniyatlar beradi:
  • faylga yozish uchun - yechim DailyRollingFileAppender ;
  • dastur konsoliga ma'lumotlarni qabul qilish - ConsoleAppender ;
  • ma'lumotlar bazasiga jurnallarni yozish uchun - JDBCAppender ;
  • TCP/IP - TelnetAppender orqali uzatishni boshqarish ;
  • jurnallar ishlashga ta'sir qilmasligini ta'minlash uchun - AsyncAppender .
Bir nechta boshqa ilovalar mavjud: to'liq ro'yxatni bu erda topishingiz mumkin . Aytgancha, agar kerakli ilova mavjud bo'lmasa, bu muammo emas. Siz log4j ni qabul qiladigan Appender interfeysini qo'llash orqali o'z ilovangizni yozishingiz mumkin .

Jurnal tugunlari

Namoyish uchun biz slf4j interfeysidan va log4j dasturidan foydalanamiz. MainDemoJurnalni yaratish juda oddiy: ro'yxatga olish amalga oshiriladigan nomli sinfga quyidagilarni yozishingiz kerak :
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
Bu biz uchun logger yaratadi. Jurnalga kirish uchun siz yozuvlar qaysi darajada amalga oshirilishini ko'rsatadigan ko'plab usullardan foydalanishingiz mumkin. Masalan:
logger.trace("Method 1 started with argument={}", argument);
logger.debug("Database updated with script = {}", script);
logger.info("Application has started on port = {}", port);
logger.warn("Log4j didn't find log4j.properties. Please, provide them");
logger.error("Connection refused to host = {}", host);
Biz sinfdan o'tayotgan bo'lsak ham, oxirida paketlar bilan sinfning to'liq nomi yoziladi. Bu shunday amalga oshiriladi, shunda siz tizimga kirishni tugunlarga bo'lishingiz va har bir tugun uchun jurnal darajasi va ilovani sozlashingiz mumkin. Masalan, sinf nomi: com.github.romankh3.logginglecture.MainDemo- unda logger yaratilgan. Va shunday qilib uni jurnal tugunlariga bo'lish mumkin. Asosiy tugun null RootLogger hisoblanadi . Bu butun dasturning barcha jurnallarini qabul qiladigan tugun. Qolganlarini quyida ko'rsatilgandek tasvirlash mumkin: Jurnal: nima, qanday, qaerda va nima bilan?  - 4Qo'shimchalar o'z ishlarini maxsus jurnal tugunlarida sozlashadi. Endi, misol sifatida log4j.properties dan foydalanib , biz ularni qanday sozlashni ko'rib chiqamiz.

Log4j.properties ning bosqichma-bosqich konfiguratsiyasi

Endi biz hamma narsani bosqichma-bosqich o'rnatamiz va nima qilish mumkinligini ko'rib chiqamiz:
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
Bu satr biz org.apache.log4j.ConsoleAppender ilovasidan foydalanadigan CONSOLE ilovasini ro‘yxatdan o‘tkazayotganimizni aytadi. Ushbu ilova konsolga ma'lumotlarni yozadi. Keyinchalik, faylga yozadigan boshqa ilovani ro'yxatdan o'tkazamiz:
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
Shuni ta'kidlash kerakki, qo'shimchalar hali ham sozlanishi kerak. Qo'shimchalarni ro'yxatdan o'tkazganimizdan so'ng, biz tugunlarda qanday darajadagi jurnallar bo'lishini va qaysi qo'shimchalar ishlatilishini aniqlashimiz mumkin.

log4j.rootLogger=DEBUG, KONSOLE, FAYL

  • log4j.rootLogger barcha jurnallarni o'z ichiga olgan asosiy tugunni sozlashimizni anglatadi;
  • tenglik belgisidan keyin birinchi so'z jurnallar qaysi darajada va undan yuqori bo'lishini ko'rsatadi (bizning holatlarimizda bu DEBUG);
  • keyin verguldan keyin ishlatiladigan barcha qo'shimchalar ko'rsatiladi.
Muayyan jurnal tugunini sozlash uchun siz quyidagi yozuvdan foydalanishingiz kerak:
log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
Bu erda log4j.logger.u ma'lum bir tugunni sozlash uchun ishlatiladi, bizning holatlarimizda u com.github.romankh3.logginglecture. Va endi CONSOLE ilovasini o'rnatish haqida gapiraylik:
# CONSOLE appender customisation
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.threshold=DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] : %c:%L : %m%n
Bu erda biz ilovani qayta ishlash darajasini belgilashimiz mumkinligini ko'ramiz. Haqiqiy vaziyat: ma'lumot darajasiga ega bo'lgan xabar logging tuguni tomonidan qabul qilindi va unga tayinlangan qo'shimchaga uzatildi, ammo ogohlantirish darajasi va undan yuqori bo'lgan ilova ushbu jurnalni qabul qildi, lekin u bilan hech narsa qilmadi. Keyinchalik, xabarda qanday shablon bo'lishini hal qilishingiz kerak. Men misolda PatternLayout dan foydalanmoqdaman, lekin u erda ko'plab echimlar mavjud. Ular ushbu maqolada oshkor etilmaydi. FILE ilovasini o'rnatishga misol:
# File appender customisation
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=./target/logging/logging.log
log4j.appender.FILE.MaxFileSize=1MB
log4j.appender.FILE.threshold=DEBUG
log4j.appender.FILE.MaxBackupIndex=2
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[ %-5p] - %c:%L - %m%n
Bu erda ko'rinib turganidek, jurnallar qaysi faylga yozilishini sozlashingiz mumkin
log4j.appender.FILE.File=./target/logging/logging.log
Yozuv faylga o'tadi logging.log. Fayl hajmi bilan bog'liq muammolarni oldini olish uchun siz maksimalni belgilashingiz mumkin: bu holda, 1 MB. MaxBackupIndex - bunday fayllar qancha bo'lishini aytadi. Agar bu raqamdan ko'proq yaratilgan bo'lsa, birinchi fayl o'chiriladi. Ro'yxatga olish sozlangan haqiqiy misolni ko'rish uchun siz GitHub-dagi ochiq omborga o'tishingiz mumkin .

Keling, natijani birlashtiraylik

O'zingiz tasvirlangan hamma narsani qilishga harakat qiling:
  • Yuqoridagi misolga o'xshash o'z loyihangizni yarating.
  • Agar siz Maven-dan foydalanish bo'yicha ma'lumotga ega bo'lsangiz, biz undan foydalanamiz; agar yo'q bo'lsa, bu erda kutubxonani qanday ulashni tasvirlaydigan maqolaga havola .

Keling, xulosa qilaylik

  1. Biz Java-da qanday echimlar mavjudligi haqida gaplashdik.
  2. Deyarli barcha ma'lum jurnallar kutubxonalari bir kishining nazorati ostida yozilgan: D
  3. Biz nimani ro'yxatga olish kerakligini va nima bo'lmasligini bilib oldik.
  4. Biz ro'yxatga olish darajalarini aniqladik.
  5. Biz logging tugunlari bilan tanishdik.
  6. Biz qo'shimcha nima ekanligini va u nima uchun ekanligini ko'rib chiqdik.
  7. Biz log4j.proterties faylini bosqichma-bosqich sozladik.

Qo'shimcha materiallar

  1. JavaRush: jurnalga yozish. Stektras to'pini echib oling
  2. JavaRush: Logger ma'ruzasi
  3. Habr: Java jurnali. Salom Dunyo
  4. Habr: Java logging: dahshatli tush hikoyasi
  5. Youtube: Golovach kurslari. Jurnal yozish. 1-qism , 2-qism , 3-qism , 4-qism
  6. Log4j: ilova
  7. Log4j: tartib
Boshqa maqolalarimni ham ko'ring:
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION