سایت ۵۰۰ میدهد. نه صفحه سفید است نه پیام واضحی؛ فقط یک Internal Server Error خشک و بیتوضیح. اولین کاری که میکنید این است که بروید سراغ لاگ خطای سرور. و اینجاست که اکثر آدمها ده دقیقه اول را هدر میدهند، چون نمیدانند کدام فایل را باز کنند و دنبال چه چیزی بگردند.
این مقاله دقیقاً همان مسیر را میرود: کجا لاگها هستند، چطور یک درخواست مشخص را داخل هزاران خط پیدا کنید، و چرا error_log و access_log دو چیز کاملاً متفاوتاند که نباید قاطیشان کنید.
لاگ خطای سرور کجاست و چرا پیدایش نمیکنید
در هاست اشتراکی لینوکسی، لاگها معمولاً در مسیر خانگی حساب شما هستند، نه در /var/log. اگر با SSH وارد شدهاید، اول این را بزنید:
ls -la ~/logs/ ~/public_html/error_log 2>/dev/null
در cPanel و DirectAdmin، فایل اصلی معمولاً ~/logs/error_log یا ~/public_html/error_log است. اگر هیچکدام نبود، از خود وبسرور بپرسید:
apachectl -S 2>/dev/null | head -20
php -i | grep -i error_log
خط آخر خروجی php -i مسیر واقعی error_log در سطح PHP را نشان میدهد. این عدد را یادداشت کنید؛ نیمی از سردرگمیها از این است که لاگ آپاچی و لاگ PHP در دو فایل جدا نوشته میشوند و شما فقط یکی را نگاه میکنید.
روی سرور اختصاصی یا VPS، مسیرها متفاوتاند. آپاچی روی دبیان و اوبونتو به /var/log/apache2/error.log مینویسد، روی CentOS و AlmaLinux به /var/log/httpd/error_log. اگر Nginx جلوی PHP-FPM نشسته باشد، خطاهای PHP در /var/log/php-fpm/www-error.log میروند و لاگ Nginx فقط خطاهای لایه وب را نگه میدارد. یعنی یک خطای ۵۰۰ ممکن است در سه فایل مختلف ردپا بگذارد.
تفاوت error_log و access_log در یک نگاه
این جدول را یک بار برای همیشه حفظ کنید، چون بیشتر وقتها آدمها ساعتها access_log را میخوانند و دنبال خطا میگردند:
| ویژگی | error_log | access_log |
|---|---|---|
| چه چیزی ثبت میشود | خطاها، هشدارها، کرشها | هر درخواست HTTP، موفق یا ناموفق |
| ساختار خط | متن آزاد + سطح خطا | فرمت ترکیبی: IP، زمان، متد، مسیر، کد وضعیت، حجم |
| کد وضعیت دارد؟ | خیر | بله، مثل 200، 404، 500 |
| برای چه کاری | فهمیدن علت خرابی | فهمیدن اینکه چه اتفاقی افتاد و چقدر |
یک خط نمونه از access_log:
185.55.12.9 - - [14/Feb/2025:09:41:22 +0330] "POST /cart/checkout HTTP/1.1" 500 312 "-" "Mozilla/5.0"
این خط میگوید یک کاربر در ساعت ۰۹:۴۱ روی مسیر /cart/checkout خطای ۵۰۰ گرفته. اما نمیگوید چرا. برای «چرا» باید همان لحظه را در error_log بگردید. اینجا اشتباه میکنند: کد ۵۰۰ را در access_log میبینند، فکر میکنند لاگ را پیدا کردهاند، و بعد ده بار صفحه را رفرش میکنند تا شاید پیام خطا ظاهر شود. پیام خطا آنجا نیست. در فایل دیگری است.
چطور یک درخواست خاص را در لاگ خطای سرور پیدا کنیم
فرض کنید همان خطای /cart/checkout را دارید و میخواهید بدانید پشتش چه بوده. اول بازه زمانی را از access_log بردارید، بعد در error_log همان بازه را بگردید:
grep "14/Feb/2025:09:4" ~/logs/access_log | grep " 500 "
grep -A 5 "09:41:2" ~/logs/error_log
فلگ -A 5 پنج خط بعد از هر تطابق را هم نشان میدهد، چون خطای PHP معمولاً چند خطی است و stack trace زیر خط اول میآید. اگر فقط خط اول را ببینید، معمولاً چیزی دستگیرتان نمیشود.
وقتی فایل لاگ بزرگ است، grep روی کل فایل کند میشود. اگر لاگ چرخیده (rotated) و فایلهای error_log.1 و error_log.2.gz دارید، مستقیم روی نسخه فشرده بگردید:
zgrep -i "fatal error" ~/logs/error_log.2.gz
برای دیدن زنده لاگ، وقتی نمیدانید خطا کِی رخ میدهد:
tail -f ~/logs/error_log | grep -v "favicon"
آن grep -v را دستکم نگیرید. در سایتهای پربازدید، خطوط مربوط به favicon.ico و درخواستهای رباتها میتوانند بیش از نیمی از خروجی را پر کنند و شما را از خطای واقعی دور کنند.
خواندن سطح خطا: چیزی که واقعاً مهم است
در لاگ PHP، هر خط با یک سطح شروع میشود. اینها را باید از هم تفکیک کنید:
- Fatal error: اسکریپت همانجا متوقف شده. این تقریباً همیشه باعث ۵۰۰ میشود و باید اول از همه دنبالش باشید.
- Parse error: خطای نگارشی در کد. معمولاً بعد از یک ویرایش یا آپلود ناقص فایل ظاهر میشود.
- Warning: اسکریپت ادامه داده، ولی چیزی سر جایش نبوده. مثل
include(): Failed opening. - Notice و Deprecated: در نسخههای جدید PHP پرتعداد میشوند و بهتنهایی سایت را نمیخوابانند.
یک نمونه واقعی که زیاد میبینم:
[14-Feb-2025 09:41:22 UTC] PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) in /home/user/public_html/wp-includes/wp-db.php on line 2056
عدد 134217728 همان ۱۲۸ مگابایت است. یعنی memory_limit روی ۱۲۸M بوده و اسکریپت جا نداشته. راهحل سریع، بالا بردن این مقدار در php.ini یا .user.ini است:
memory_limit = 256M
اما اینجا یک تله هست که باید صادقانه بگویم: بالا بردن memory_limit مشکل را پنهان میکند، حل نمیکند. اگر یک افزونه در هر درخواست ۲۰۰ مگابایت میخورد، با ۲۵۶M فقط کمی دیرتر میخوابد و روی سرور پربازدید، مصرف حافظه کل سرور را بالا میبرد. اگر خطای memory در یک مسیر خاص تکرار میشود، مشکل آن افزونه یا کوئری است، نه عدد memory_limit.
خطاهایی که در لاگ نیستند و باید جای دیگری بگردید
گاهی سایت ۵۰۰ میدهد ولی error_log کاملاً خالی است. این حالت چند علت مشخص دارد:
- خطا در سطح وبسرور رخ داده، نه PHP. مثلاً
.htaccessخراب است. در این حالت لاگ آپاچی را ببینید، نه لاگ PHP. - خطا در سطح DNS یا اتصال است و اصلاً به سرور شما نرسیده. برای این حالت، بررسی DNS و شبکه نقطه شروع درستی است.
- لاگنویسی خاموش است. اگر
display_errorsوlog_errorsهر دو خاموش باشند، هیچجا چیزی نوشته نمیشود.
برای حالت سوم، این دو خط را در php.ini یا .user.ini بگذارید:
log_errors = On
error_log = /home/user/logs/php_error.log
و بعد وبسرور را ریاستارت کنید. روی PHP-FPM، تغییر php.ini بدون ریاستارت سرویس اعمال نمیشود و همین باعث میشود فکر کنید تنظیم کار نکرده.
وقتی خطا از خود کد نیست
بعضی وقتها لاگ پر است از خطا ولی سایت سالم کار میکند. مثلاً صدها خط Deprecated: Creation of dynamic property در PHP 8.2. اینها را نباید نادیده گرفت، ولی اولویت اول هم نیستند. اولویت را روی Fatal error و کدهای ۵۰۰ بگذارید.
اگر سایت کند است ولی خطایی در لاگ نیست، مسئله جای دیگری است. در آن حالت مسیر درست، عیبیابی عملکرد است نه خطا؛ راهنمای چرا سایت کند است؟ راهنمای کامل عیبیابی سایت کند دقیقاً همین مسیر را پوشش میدهد و از TTFB و زمان کوئری شروع میکند.
یک نکته عملی: قبل از هر تغییر روی سرور زنده، یک محیط جدا بسازید. راهنمای ساخت محیط استیجینگ برای سایت نشان میدهد چطور بدون دست زدن به سایت اصلی، خطا را بازتولید کنید. این کار در بلندمدت بیشترین وقت را برایتان ذخیره میکند.
ابزارهایی که کار را کوتاه میکنند
برای شمارش سریع خطاها به تفکیک نوع:
grep -o "PHP Fatal error" ~/logs/error_log | wc -l
awk '{print $5}' ~/logs/access_log | sort | uniq -c | sort -rn | head
خط دوم کدهای وضعیت را میشمارد و نشان میدهد چند درصد درخواستها ۵۰۰ بودهاند. اگر عدد ۵۰۰ نسبت به کل درخواستها بالا رفت، مسئله دیگر یک کاربر تنها نیست.
اگر روی هاست اشتراکی هستید و دسترسی SSH ندارید، معمولاً از پنل میتوانید لاگها را دانلود کنید. در هاست لینوکس این فایلها از همان پنل قابل مشاهده و دانلود هستند و نیازی به دسترسی روت نیست. برای کارهای سبکتر مثل بررسی هدرها و ریدایرکتها، ابزارهای رایگان وبمستر سریعتر از باز کردن ترمینال جواب میدهند.
و اگر حجم لاگها از چند گیگابایت رد شد و grep روی سرور اشتراکی کند شد، وقتش رسیده به سرور اختصاصی فکر کنید؛ نه بهخاطر قدرت پردازش، بلکه بهخاطر کنترل روی چرخش لاگ و نگهداری طولانیمدتشان.
پرسشهای پرتکرار
لاگ خطای سرور در هاست اشتراکی کجاست؟
در بیشتر پنلهای لینوکسی، فایل error_log داخل پوشه logs در ریشه حساب شما قرار دارد، یعنی مسیری مثل ~/logs/error_log. اگر آنجا نبود، مسیر دقیق را با php -i | grep error_log پیدا کنید، چون لاگ PHP و لاگ وبسرور دو فایل جدا هستند.
چرا سایت خطای ۵۰۰ میدهد ولی error_log خالی است؟
معمولاً چون خطا در سطح وبسرور رخ داده، نه PHP؛ مثلاً یک خط نامعتبر در .htaccess. علت دیگر این است که log_errors خاموش است و هیچجا نوشته نمیشود. در هر دو حالت باید لاگ آپاچی یا Nginx را جداگانه بررسی کنید.
چطور بفهمم کدام درخواست باعث خطا شده؟
اول زمان و مسیر درخواست را از access_log بردارید، بعد همان بازه زمانی را در error_log جستوجو کنید. دستور grep -A 5 روی error_log کمک میکند چند خط بعد از خطا را هم ببینید، چون stack trace معمولاً چند خطی است.
بالا بردن memory_limit مشکل را حل میکند؟
فقط علامت را جابهجا میکند. اگر یک افزونه یا کوئری در هر درخواست حافظه زیادی میخورد، بالا بردن memory_limit باعث میشود دیرتر بخوابد، نه اینکه درست شود. ریشه را در همان افزونه یا کوئری پیدا کنید.
قدم بعدی ساده است: همین حالا یک بار tail -f روی error_log بگذارید و سایت را در مرورگر باز کنید. اگر خطایی در حال وقوع باشد، همان لحظه جلوی چشمتان ظاهر میشود. اگر ظاهر نشد، یعنی خطا جای دیگری است و باید سراغ لاگ وبسرور بروید.
دیدگاهها ۰
هنوز دیدگاهی ثبت نشده؛ اولین نفر باشید!