كيفية الحصول على stack trace الحالي في Java: دليل شامل للمطور
\n\nتخيل الموقف: أنت تكتب خادمًا متعدد الخيوط معقدًا بلغة Java، وفجأة - خلل. التطبيق يتعطل، ولكن سجلات الأخطاء فارغة، ولا يمكن إعادة إنتاج الخطأ محليًا. ألم مألوف؟ في مثل هذه اللحظات، تصبح القدرة على "النظر" داخل مكدس الاستدعاءات الحالي (stack trace) قوة خارقة. هذا ليس مجرد تصحيح أخطاء - إنه فن فهم من أين جاء تدفق التنفيذ ولماذا لم يسير الكود كما هو مخطط.
\n\nالسؤال "How can I get the current stack trace in Java?" هو أحد أكثر الأسئلة شيوعًا على Stack Overflow (حصل على آلاف المشاهدات ولا يزال يجذب النجوم). وهذا ليس من قبيل الصدفة: كل مطور Java، من المبتدئ إلى المهندس المعماري، سيواجه عاجلاً أم آجلاً الحاجة إلى الحصول على مكدس الاستدعاءات برمجيًا. في هذه المقالة، سنستعرض جميع الطرق: من الكلاسيكية (عبر الاستثناء) إلى الحديثة (عبر StackWalker من Java 9). ستتعلم ليس فقط كيفية الحصول على التتبع، ولكن أيضًا كيفية تطبيقه لتحليل الأداء والتسجيل وإيجاد الاختناقات.
ما هو stack trace ولماذا نحتاج إلى الحصول عليه "أثناء التشغيل"؟
\n\nقبل الغوص في الكود، دعنا نتذكر الأساسيات. Stack trace (تتبع المكدس) هو قائمة بجميع استدعاءات الدوال التي أدت إلى نقطة التنفيذ الحالية. عندما ترى رسالة خطأ حمراء في وحدة التحكم - هذا هو stack trace. ولكن ماذا لو لم يكن هناك خطأ، وتريد فقط فهم السياق الذي تم فيه استدعاء دالة؟ أو تحتاج إلى تسجيل مسار الاستدعاء الكامل لأغراض التدقيق؟
\n\nالسيناريوهات النموذجية التي تحتاج فيها إلى ذلك:
\n- \n
- التسجيل - إضافة سياق الاستدعاء إلى ملف السجل. \n
- تحليل الأداء - فهم أي الدوال تستدعي بعضها البعض. \n
- تصحيح أخطاء تعدد الخيوط - معرفة أي خيط ينفذ الكود. \n
- الأغراض التعليمية - تصور عمل العودية أو سلاسل الاستدعاءات. \n
الآن دعنا ننتقل إلى التطبيق العملي. سأعرض لك أربع طرق عملية - من الأبسط إلى الأكثر مرونة.
\n\nالطريقة 1: Thread.dumpStack() - سريعة وقذرة
\n\nأبسط طريقة لعرض المكدس الحالي في وحدة التحكم هي استدعاء الدالة الثابتة Thread.dumpStack(). إنها لا تعيد بيانات، بل تطبع التتبع مباشرة في System.err.
public class SimpleDump {\n public static void main(String[] args) {\n firstMethod();\n }\n\n static void firstMethod() {\n secondMethod();\n }\n\n static void secondMethod() {\n // نطبع المكدس هنا مباشرة\n Thread.dumpStack();\n }\n}\n\nالنتيجة في وحدة التحكم:
\njava.lang.Exception: Stack trace\n at java.base/java.lang.Thread.dumpStack(Thread.java:1383)\n at SimpleDump.secondMethod(SimpleDump.java:12)\n at SimpleDump.firstMethod(SimpleDump.java:8)\n at SimpleDump.main(SimpleDump.java:4)\n\nالإيجابيات: الحد الأدنى من الكود، لا حاجة لإنشاء كائنات.
\nالسلبيات: لا يمكنك معالجة المكدس برمجيًا (فقط الإخراج إلى الشاشة). غير مناسب للتسجيل أو التحليل.
الطريقة 2: Throwable.getStackTrace() - كلاسيكية هذا النوع
\n\nهذه الطريقة مألوفة لكل من تعامل مع الاستثناءات. تقوم بإنشاء كائن جديد من Throwable (أو Exception) وتستدعي getStackTrace(). تعيد مصفوفة من StackTraceElement[] يمكن تحليلها.
public class ClassicWay {\n public static void main(String[] args) {\n methodA();\n }\n\n static void methodA() {\n methodB();\n }\n\n static void methodB() {\n // الحصول على المكدس كمصفوفة من العناصر\n StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();\n // أو عبر new Throwable().getStackTrace()\n // StackTraceElement[] stackTrace = new Throwable().getStackTrace();\n\n