JavaRush /وبلاگ جاوا /Random-FA /ورود به جاوا: چه، چگونه، کجا و با چه چیزی؟
Roman Beekeeper
مرحله

ورود به جاوا: چه، چگونه، کجا و با چه چیزی؟

در گروه منتشر شد
سلام به همه، انجمن JavaRush! امروز در مورد لاگ جاوا صحبت خواهیم کرد:
  1. این چیه، چرا هست. در چه مواردی بهتر است استفاده شود، در چه مواردی نیست؟
  2. چه نوع پیاده سازی لاگ در جاوا وجود دارد و با این تنوع چه کنیم؟
  3. سطوح ورود به سیستم. بیایید بحث کنیم که appender چیست و چگونه آن را به درستی پیکربندی کنیم.
  4. ثبت گره ها و نحوه پیکربندی صحیح آنها به طوری که همه چیز همانطور که می خواهیم کار کند.
این مطالب برای مخاطبان گسترده در نظر گرفته شده است. logger.info(“log something”); هم برای کسانی که به تازگی با جاوا آشنا شده اند و هم برای کسانی که در حال کار هستند، اما فقط با Let's Go متوجه شده اند، واضح خواهد بود !

چرا ورود به سیستم مورد نیاز است؟

بیایید به موارد واقعی نگاه کنیم که در آن ورود به سیستم مشکل را حل می کند. در اینجا یک نمونه از کار من است. نقاط کاربردی وجود دارد که با سرویس های دیگر ادغام می شوند. من از ثبت این نقاط به‌عنوان «علیبی» استفاده می‌کنم : اگر ادغام کار نکند، به راحتی می‌توان فهمید که مشکل از کدام طرف سرچشمه گرفته است. همچنین توصیه می شود اطلاعات مهمی را که در پایگاه داده ذخیره شده اند وارد کنید. به عنوان مثال، ایجاد یک کاربر مدیر. این دقیقاً همان چیزی است که ورود به سیستم خوب است.

ابزارهای ثبت جاوا

ورود به سیستم: چه، چگونه، کجا و با چه چیزی؟  - 2راه حل های شناخته شده برای ورود به جاوا عبارتند از:
  • log4j
  • JUL - java.util.logging
  • JCL - ورود به سیستم عوام جاکارتا
  • ورود به سیستم
  • SLF4J - نمای ورود به سیستم ساده برای جاوا
بیایید نگاهی گذرا به هر یک از آنها بیندازیم، و در بخش عملی مواد، اتصال Slf4j - log4j را به عنوان پایه در نظر خواهیم گرفت . این ممکن است در حال حاضر عجیب به نظر برسد، اما نگران نباشید: در پایان مقاله همه چیز مشخص خواهد شد.

System.err.println

البته در ابتدا System.err.println (خروجی ضبط در کنسول) وجود داشت. هنوز هم برای به دست آوردن سریع گزارش در هنگام اشکال زدایی استفاده می شود. البته در اینجا نیازی به صحبت در مورد تنظیمات نیست، پس بیایید آن را به خاطر بسپاریم و ادامه دهیم.

Log4j

این قبلاً یک راه حل تمام عیار بود که از نیازهای توسعه دهندگان ایجاد شده بود. معلوم شد که این ابزار واقعاً جالبی برای استفاده است. به دلیل شرایط مختلف، این راه حل هرگز وارد JDK نشد، که به شدت کل جامعه را ناراحت کرد. log4j دارای گزینه های پیکربندی بود به طوری که ورود به سیستم را می توان در یک بسته روشن com.example.typeو در یک بسته فرعی خاموش کرد com.example.type.generic. این امر امکان جداسازی سریع مواردی را که باید ثبت شود از موارد غیرضروری امکان پذیر می کرد. در اینجا ذکر این نکته ضروری است که دو نسخه log4j وجود دارد: 1.2.x و 2.x.x که با یکدیگر سازگار نیستند . log4j مفهومی مانند appender را اضافه کرد ، یعنی ابزاری که با آن لاگ‌ها ثبت می‌شوند و قالب‌بندی layout - log. این به شما امکان می دهد فقط آنچه را که نیاز دارید و چگونه به آن نیاز دارید ضبط کنید. کمی بعد در مورد appender بیشتر صحبت خواهیم کرد.

JUL - java.util.logging

یکی از مزایای کلیدی راه حل است - JUL در JDK (کیت توسعه جاوا) گنجانده شده است. متأسفانه، در طول توسعه آن، این log4j محبوب نبود که به عنوان پایه در نظر گرفته شد، بلکه راه حلی از IBM بود که بر توسعه آن تأثیر گذاشت. در واقع، در حال حاضر JUL وجود دارد، اما هیچ کس از آن استفاده نمی کند. از "so-so": در JUL سطوح ورود به سیستم با آنچه در Logback، Log4j، Slf4j است متفاوت است، و این درک بین آنها را بدتر می کند. ایجاد یک لاگر کم و بیش مشابه است. برای انجام این کار باید وارد کنید:
java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
نام کلاس به طور خاص به منظور دانستن اینکه ورود به سیستم از کجا آمده است منتقل می شود. از جاوا 8، امکان عبور وجود دارد Supplier<String>. این به شمارش و ایجاد یک رشته فقط در لحظه ای که واقعاً به آن نیاز است کمک می کند، و نه هر بار، همانطور که قبلا بود. تنها با انتشار جاوا 8، توسعه دهندگان مشکلات مهمی را حل کردند، پس از آن، JUL واقعا قابل استفاده شد. یعنی متدهایی با آرگومان Supplier<String> msgSupplierکه در زیر نشان داده شده است:
public void info(Supplier<String> msgSupplier) {
   log(Level.INFO, msgSupplier);
}

JCL - ورود به سیستم عوام جاکارتا

با توجه به این واقعیت که برای مدت طولانی هیچ استاندارد صنعتی در ورود به سیستم وجود نداشت و دوره ای بود که بسیاری از افراد لاگر سفارشی خود را ایجاد کردند، آنها تصمیم گرفتند JCL را منتشر کنند - یک لفاف معمولی که بر روی دیگران استفاده می شود. چرا؟ هنگامی که برخی از وابستگی ها به پروژه اضافه می شدند، می توانستند از لاگر متفاوتی نسبت به لاگر پروژه استفاده کنند. به همین دلیل، آنها به طور موقت به پروژه اضافه شدند، که در هنگام تلاش برای کنار هم قرار دادن همه آن، مشکلات واقعی ایجاد کرد. متأسفانه، لفاف از نظر عملکرد بسیار ضعیف بود و هیچ افزودنی معرفی نکرد. اگر همه از JCL برای انجام کار خود استفاده کنند، احتمالاً راحت خواهد بود. اما در واقعیت به این ترتیب کار نکرد، بنابراین استفاده از JCL در حال حاضر ایده خوبی نیست.

ورود به سیستم

مسیر منبع باز چقدر سخت است... Logback توسط همان توسعه دهنده log4j نوشته شده است تا جانشینی برای آن ایجاد کند. ایده همان log4j بود. تفاوت ها این بود که در logback:
  • عملکرد بهبود یافته؛
  • پشتیبانی بومی برای slf4j اضافه شده است.
  • گزینه فیلترینگ گسترش یافته است.
به طور پیش فرض، logback نیازی به تنظیمات ندارد و همه گزارش ها را از سطح DEBUG و بالاتر ثبت می کند. در صورت نیاز به پیکربندی، می توان آن را از طریق پیکربندی xml انجام داد:
<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 - نمای ورود به سیستم ساده برای جاوا

در حدود سال 2006، یکی از بنیانگذاران log4j پروژه را ترک کرد و slf4j - Simple Logging Facade برای جاوا - یک پوشش در اطراف log4j، JUL، Common-loggins و logback ایجاد کرد. همانطور که می بینید پیشرفت به حدی رسیده است که یک wrapper در بالای wrapper ایجاد کردند ... علاوه بر این به دو قسمت تقسیم می شود: API که در برنامه استفاده می شود و پیاده سازی که به صورت اضافه شده است. وابستگی های جداگانه برای هر نوع ورود به سیستم. مثلا، slf4j-log4j12.jar، slf4j-jdk14.jar. کافی است اجرای صحیح را وصل کنید و تمام: کل پروژه با آن کار خواهد کرد. Slf4j از تمام ویژگی های جدید مانند قالب بندی رشته برای ورود به سیستم پشتیبانی می کند. قبلا چنین مشکلی وجود داشت. فرض کنید یک ورودی گزارش وجود دارد:
log.debug("User " + user + " connected from " + request.getRemoteAddr());
یک تبدیل ضمنی در شی به دلیل الحاق رشته userوجود دارد user.toString()و این زمان می برد که باعث کند شدن سیستم می شود. و اگر برنامه را اشکال زدایی کنیم همه چیز خوب است. اگر سطح گزارش برای این کلاس INFO و بالاتر باشد، مشکلات شروع می شود. یعنی این لاگ نباید نوشته شود و الحاق رشته ها نیز نباید انجام شود. در تئوری، این باید توسط خود کتابخانه ورود به سیستم تصمیم می گرفت. علاوه بر این، این بزرگترین مشکل نسخه اول log4j بود. آنها یک راه حل معمولی ارائه نکردند، اما پیشنهاد کردند این کار را به این صورت انجام دهند:
if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
یعنی به جای یک لاگ لاگین، نوشتن 3(!) را پیشنهاد کردند. ورود به سیستم باید تغییرات در کد را به حداقل برساند و سه خط به وضوح با رویکرد کلی در تضاد بودند. slf4j هیچ مشکل سازگاری با JDK و API نداشت، بنابراین بلافاصله یک راه حل زیبا ظاهر شد:
log.debug("User {} connected from {}", user, request.getRemoteAddr());
جایی که {}نشان دهنده درج آرگومان هایی است که در متد ارسال می شوند. یعنی اولین {}مربوط به user، دوم {}- request.getRemoteAddr(). به همین دلیل، تنها در صورتی که سطح ورود به سیستم اجازه ورود به سیستم را بدهد، می توان این پیام را به یک پیام واحد متصل کرد. پس از این، SJF4J به سرعت محبوبیت یافت و در حال حاضر بهترین راه حل است. بنابراین، ما ورود به سیستم را با استفاده از مثال یک بسته در نظر خواهیم گرفت slf4j-log4j12.

آنچه باید ثبت شود

البته، شما نباید همه چیز را ثبت کنید. گاهی اوقات این غیر ضروری و حتی خطرناک است. به عنوان مثال، اگر داده‌های شخصی شخصی را تعهد کنید و به نحوی آشکار شود، مشکلات واقعی به‌ویژه در پروژه‌هایی که به سمت غرب است، به وجود می‌آید. اما چیزی نیز وجود دارد که ورود به سیستم الزامی است :
  1. شروع/پایان برنامه ما باید بدانیم که برنامه در واقع همانطور که انتظار داشتیم راه اندازی شد و همانطور که انتظار داشتیم به پایان رسید.
  2. سوالات امنیتی. در اینجا خوب است که تلاش‌های حدس زدن رمز عبور، ورود کاربران مهم و غیره را ثبت کنید.
  3. برخی از حالت های کاربردی به عنوان مثال، انتقال از یک حالت به حالت دیگر در یک فرآیند تجاری.
  4. برخی از اطلاعات برای اشکال زدایی ، با سطح مناسبی از ورود.
  5. برخی از اسکریپت های SQL موارد واقعی وجود دارد که این مورد نیاز است. باز هم با تنظیم ماهرانه سطوح می توان به نتایج عالی دست یافت.
  6. رشته های اجرا شده (Thread) را می توان در مواردی که عملکرد صحیح بررسی می شود ثبت نام کرد.

اشتباهات رایج در ثبت نام

تفاوت های ظریف زیادی وجود دارد، اما در اینجا چند اشتباه رایج وجود دارد:
  1. قطع بیش از حد. شما نباید هر مرحله ای را که از نظر تئوری مهم است ثبت نام کنید. یک قانون وجود دارد: سیاهههای مربوط می توانند عملکرد را بیش از 10٪ بارگذاری کنند. در غیر این صورت مشکلات عملکردی وجود خواهد داشت.
  2. ثبت همه داده ها در یک فایل این کار خواندن/نوشتن برای آن را در یک نقطه خاص بسیار دشوار می کند، ناگفته نماند که محدودیت اندازه فایل در سیستم های خاص وجود دارد.
  3. استفاده از سطوح ثبت نادرست هر سطح ورود به سیستم دارای مرزهای واضحی است و باید رعایت شود. اگر مرز مبهم است، می توانید توافق کنید که از کدام سطح استفاده کنید.

سطوح ورود به سیستم

x: قابل مشاهده است
کشنده خطا هشدار دهید اطلاعات اشکال زدایی پی گیری همه
خاموش
کشنده ایکس
خطا ایکس ایکس
هشدار دهید ایکس ایکس ایکس
اطلاعات ایکس ایکس ایکس ایکس
اشکال زدایی ایکس ایکس ایکس ایکس ایکس
پی گیری ایکس ایکس ایکس ایکس ایکس ایکس
همه ایکس ایکس ایکس ایکس ایکس ایکس ایکس
سطوح ورود به سیستم چیست؟ برای رتبه‌بندی سیاهه‌ها به نحوی لازم بود که نام‌گذاری‌ها و تمایزات خاصی داده شود. برای این منظور سطوح ورود به سیستم معرفی شد. سطح در برنامه تنظیم شده است. اگر یک ورودی متعلق به سطحی کمتر از سطح تعیین شده باشد، در گزارش وارد نمی شود. به عنوان مثال، ما گزارش هایی داریم که برای اشکال زدایی برنامه استفاده می شود. در کار تولید معمولی (زمانی که برنامه برای هدف مورد نظر خود استفاده می شود)، چنین سیاهه هایی مورد نیاز نیست. بنابراین، سطح ورود به سیستم بالاتر از اشکال زدایی خواهد بود. بیایید به سطوح با استفاده از log4j به عنوان مثال نگاه کنیم. راه حل های دیگر، به جز JUL، از همان سطوح استفاده می کنند. در اینجا آنها به ترتیب کاهشی هستند:
  • خاموش: هیچ گزارشی نوشته نشده است، همه نادیده گرفته خواهند شد.
  • FATAL: خطایی که پس از آن برنامه دیگر نمی تواند کار کند و متوقف می شود، به عنوان مثال، خطای JVM از حافظه خارج می شود.
  • ERROR: میزان خطا زمانی که مشکلاتی وجود دارد که باید حل شوند. خطا به طور کلی برنامه را متوقف نمی کند. سایر پرس و جوها ممکن است به درستی کار کنند.
  • WARN: گزارش هایی را نشان می دهد که حاوی هشدار هستند. یک اقدام غیرمنتظره رخ داد، با وجود این، سیستم مقاومت کرد و درخواست را تکمیل کرد.
  • INFO: گزارشی که اقدامات مهم را در برنامه ثبت می کند. اینها خطا نیست، اینها هشدار نیست، اینها اقدامات مورد انتظار سیستم است.
  • DEBUG: گزارش های مورد نیاز برای اشکال زدایی برنامه. برای اطمینان از اینکه سیستم دقیقاً همان چیزی را که از آن انتظار می رود انجام می دهد یا برای توصیف عملکرد سیستم: "روش 1 شروع به کار کرد"؛
  • TRACE: گزارش‌های با اولویت پایین‌تر برای اشکال‌زدایی، با پایین‌ترین سطح گزارش.
  • ALL: سطحی که در آن تمام لاگ های سیستم ثبت خواهند شد.
به نظر می رسد که اگر سطح ثبت اطلاعات در جایی در برنامه فعال باشد، همه سطوح، از INFO تا FATAL، ثبت می شوند. اگر سطح ورود به سیستم FATAL باشد، فقط لاگ هایی با این سطح ثبت می شوند.

ضبط و ارسال لاگ: Appender

ما این فرآیند را با استفاده از log4j به عنوان مثال در نظر خواهیم گرفت: فرصت‌های زیادی برای ضبط/ارسال گزارش‌ها فراهم می‌کند:
  • برای نوشتن روی یک فایل - راه حل DailyRollingFileAppender ;
  • برای دریافت داده ها در کنسول برنامه - ConsoleAppender ;
  • برای نوشتن سیاهههای مربوط به پایگاه داده - JDBCappender ;
  • برای کنترل انتقال از طریق TCP/IP - TelnetAppender .
  • برای اطمینان از اینکه ورود به سیستم بر عملکرد تأثیر نمی گذارد - AsyncAppender .
چندین پیاده سازی دیگر وجود دارد: لیست کامل را می توانید در اینجا بیابید . ضمناً، اگر ضمیمه مورد نیاز در دسترس نباشد، مشکلی نیست. می‌توانید با پیاده‌سازی رابط Appender ، که فقط log4j را می‌پذیرد ، ضمیمه خود را بنویسید .

گره های ورود به سیستم

برای نمایش از رابط slf4j و پیاده سازی از log4j استفاده خواهیم کرد. ایجاد یک لاگر بسیار ساده است: شما باید موارد زیر را در کلاسی به نام بنویسید MainDemoکه در آن لاگ انجام می شود:
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
این یک لاگر برای ما ایجاد می کند. برای ایجاد یک ورودی گزارش، می توانید از روش های زیادی استفاده کنید که نشان می دهد ورودی ها در چه سطحی انجام می شوند. مثلا:
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);
با وجود اینکه در حال گذراندن کلاس هستیم، در آخر نام کامل کلاس با بسته ها نوشته می شود. این کار به این دلیل انجام می شود که بعداً می توانید ورود به سیستم را به گره ها تقسیم کنید و یک سطح ورود و یک ضمیمه برای هر گره پیکربندی کنید. به عنوان مثال، نام کلاس: com.github.romankh3.logginglecture.MainDemo- یک لاگر در آن ایجاد شد. و به این ترتیب می توان آن را به گره های ورود به سیستم تقسیم کرد. گره اصلی RootLogger پوچ است . این گره ای است که تمام لاگ های کل برنامه را دریافت می کند. بقیه موارد را می توان مانند شکل زیر نشان داد: ورود به سیستم: چه، چگونه، کجا و با چه چیزی؟  - 4Appender ها کار خود را به طور خاص بر روی گره های ورود به سیستم پیکربندی می کنند. اکنون، با استفاده از log4j.properties به عنوان مثال ، نحوه پیکربندی آنها را بررسی خواهیم کرد.

پیکربندی گام به گام Log4j.properties

اکنون ما همه چیز را مرحله به مرحله تنظیم می کنیم و می بینیم که چه کاری می توان انجام داد:
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
این خط می گوید که ما یک ضمیمه CONSOLE را ثبت می کنیم که از پیاده سازی org.apache.log4j.ConsoleAppender استفاده می کند. این ضمیمه داده ها را روی کنسول می نویسد. بعد، بیایید یک ضمیمه دیگر را ثبت کنیم که در یک فایل می نویسد:
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
توجه به این نکته مهم است که ضمیمه ها همچنان نیاز به پیکربندی دارند. هنگامی که از قبل ضمیمه‌های ثبت‌شده داشته باشیم، می‌توانیم تعیین کنیم که چه سطحی از ثبت در گره‌ها وجود دارد و از کدام ضمیمه‌ها استفاده می‌شود.

log4j.rootLogger=اشکال زدایی، کنسول، فایل

  • log4j.rootLogger به این معنی است که ما گره اصلی را که شامل تمام گزارش‌ها است، پیکربندی می‌کنیم.
  • پس از علامت مساوی، اولین کلمه نشان می دهد که گزارش ها در چه سطح و بالاتری ثبت می شوند (در مورد ما، این DEBUG است).
  • سپس بعد از کاما همه ضمیمه هایی که استفاده خواهند شد نشان داده می شوند.
برای پیکربندی یک گره ورود به سیستم خاص، باید از ورودی زیر استفاده کنید:
log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
جایی که log4j.logger.از آن برای پیکربندی یک گره خاص استفاده می شود، در مورد ما چنین است com.github.romankh3.logginglecture. و اکنون اجازه دهید در مورد تنظیم ضمیمه CONSOLE صحبت کنیم:
# 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
در اینجا می بینیم که می توانیم سطحی را که appender از آن پردازش می کند، تنظیم کنیم. وضعیت واقعی: یک پیام با سطح اطلاعات توسط گره ورود به سیستم دریافت شد و به ضمیمه کننده ای که به آن اختصاص داده شده بود منتقل شد، اما ضمیمه کننده با سطح هشدار و بالاتر، این گزارش را پذیرفت، اما هیچ کاری با آن انجام نداد. بعد، باید تصمیم بگیرید که چه الگوی در پیام باشد. من در مثال از PatternLayout استفاده می کنم، اما راه حل های زیادی وجود دارد. آنها در این مقاله افشا نمی شوند. نمونه ای از راه اندازی ضمیمه FILE:
# 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
در اینجا می توانید پیکربندی کنید که لاگ ها در کدام فایل نوشته شوند، همانطور که از آن قابل مشاهده است
log4j.appender.FILE.File=./target/logging/logging.log
ضبط به فایل می رود logging.log. برای جلوگیری از مشکلات با اندازه فایل، می توانید حداکثر را تنظیم کنید: در این مورد، 1 مگابایت. MaxBackupIndex - می گوید که چه تعداد از این فایل ها وجود خواهد داشت. اگر بیشتر از این عدد ایجاد شود، اولین فایل حذف خواهد شد. برای مشاهده یک مثال واقعی که در آن لاگ پیکربندی شده است، می توانید به مخزن باز در GitHub بروید.

بیایید نتیجه را تجمیع کنیم

سعی کنید همه آنچه را که خودتان توضیح داده اید انجام دهید:
  • پروژه خود را شبیه به مثال بالا ایجاد کنید.
  • اگر دانشی در مورد استفاده از Maven دارید، ما از آن استفاده خواهیم کرد، اگر نه، در اینجا پیوندی به مقاله ای وجود دارد که نحوه اتصال کتابخانه را توضیح می دهد.

بیایید آن را جمع بندی کنیم

  1. ما در مورد راه حل هایی که در جاوا وجود دارد صحبت کردیم.
  2. تقریباً تمام کتابخانه های ثبت شده شناخته شده تحت کنترل یک نفر نوشته شده اند :D
  3. ما یاد گرفتیم که چه چیزی باید ثبت شود و چه چیزی نه.
  4. ما سطوح ورود به سیستم را مشخص کردیم.
  5. با گره های ورود به سیستم آشنا شدیم.
  6. ما بررسی کردیم که ضمیمه چیست و برای چیست.
  7. ما فایل log4j.proterties را مرحله به مرحله پیکربندی کردیم.

مواد اضافی

  1. JavaRush: ورود به سیستم. یک توپ stetrace را باز کنید
  2. JavaRush: سخنرانی Logger
  3. Habr: لاگ جاوا. سلام دنیا
  4. Habr: جاوا logging: داستان یک کابوس
  5. یوتیوب: دوره های Golovach. ورود به سیستم. قسمت 1 , قسمت 2 , قسمت 3 , قسمت 4
  6. Log4j: ضمیمه
  7. Log4j: طرح بندی
مقالات دیگر من را نیز ببینید:
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION