ডিবাগিং ও লগিং

ডিবাগিং ও লগিং

ডিবাগিং

যখন আমরা একটা প্রোগ্রাম লিখি, তখন সেখানে সাধারণত দুই ধরনের এরর থাকে। এগুলো হলো সিনট্যাক্স এরর ও লজিকাল এরর।

সিনট্যাক্স এরর প্রোগ্রাম চালালে সহজেই ধরা পড়ে। তবে লজিকাল এরর ধরাটা একটু কষ্টসাধ্য। ইউনিট টেস্টিংয়ের মাধ্যমে লজিকাল এরর অনেকটাই ধরা পড়ে। তবে তারপরও লজিকাল এরর ধরাটা খুব কঠিন। অনেক সময় দেখব সবকিছু ঠিকঠাক রেখে প্রোগ্রাম লিখলাম কিন্তু আউটপুট যা আসার কথা তা আসছে না। এই যে সমস্যাটা, এটাকেই আমরা প্রোগ্রামিংয়ের ভাষায় বাগ বলি। আর এই বাগ মারার উপায় হলো ডিবাগিং। যে অস্ত্রপাতি দিয়ে বাগ মারা হয় তাকে বলা হয় ডিবাগার।

ভাগ্য ভালোই বলতে হবে, পাইথনে একটা অস্থির রকমের বিল্ট-ইন ডিবাগার আছে। এর নাম pdb (পিডিবি) বা পুরো নামে ডাকলে Python Debugger (পাইথন ডিবাগার)। আমরা তিন ভাবে এই ডিবাগারটা দিয়ে কাজ করতে পারি।

১. পিডিবি মূলত একটি মডিউল। তাই আমরা আমাদের প্রোগ্রামে একে আমদানি করে এবং তারপর আনুষঙ্গিক কিছু কাজ করে ডিবাগিং শুরু করতে পারি।

২. পাইথন IDLE (আইডিএলই)-এ ডিবাগিং করতে পারি।

৩. টার্মিনালে বা কমান্ডলাইনে ডিবাগিং করতে পারি।

শেষেরটাই সবচেয়ে স্মার্ট সলিউশন। তাই আমরা এখন এটাই শিখব। তবে তার আগে test.py ফাইলে লিখে নেব একটা ছোট্ট প্রোগ্রাম, শুধু ডিবাগিংয়ের জন্য।

def square(x):
    temp = x**2
    print(temp)
    return

def main():
    for i in range(1, 11):
        square(i)

if __name__ == "__main__":
    main()

আমরা এবার আমাদের টার্মিনালে কমান্ড দেব -

python3 -m pdb test.py

এই কমান্ডটি দিলেই টার্মিনালে ডিবাগার চালু হয়ে যাবে। অবশ্য এত বড় কমান্ড না দিয়ে আমরা চাইলে আরও ছোট একটা কমান্ডও দিতে পারি -

pdb3 test.py

ডিবাগার চালু হলে (Pdb) লেখার ডানপাশে বিভিন্ন কমান্ড দিয়ে আমরা ডিবাগিং করতে পারি।

এখন আমরা দরকারি কিছু ডিবাগিং কমান্ড শিখব:

কমান্ড কাজ
a বা args বর্তমান ফাংশনের সবগুলো আর্গুমেন্ট ও তাদের ভ্যালু প্রিন্ট করে।
b বা break lineno lineno-এর জায়গায় যে লাইন নম্বর দেওয়া হবে সেখানে এসে এক্সিকিউশন পজ হবে।
c বা continue এক্সিকিউশন চলবে যতক্ষণ না পরবর্তী ব্রেকপয়েন্ট পাওয়া যায়।
n বা next যতক্ষণ অবধি বর্তমান ফাংশনের পরবর্তী লাইন না পাওয়া যায় ততক্ষণ এক্সিকিউট হতে থাকে।
p expression expression এক্সিকিউট হওয়ার পর এর ভ্যালু প্রিন্ট করে।
q বা quit ডিবাগার বন্ধ করার জন্য।
restart ডিবাগার রিস্টার্ট হয়।
s বা step বর্তমান লাইন এক্সিকিউট করে পরবর্তী অকেশনের (নতুন ফাংশন বা বর্তমান ফাংশনের পরবর্তী লাইন) ঠিক প্রথম লাইনে থামবে।
w বা where এখন কোনো ও কত নম্বর লাইন এক্সিকিউট হচ্ছে তা দেখা যায়।
whatis এক্সপ্রেশন এক্সপ্রেশনের টাইপ প্রিন্ট করে।

আরও অনেক আছে। তবে আপাতত এগুলো জানলেই কাজ চলে যাবে আমাদের। আরও বেশি জানার জন্য অবশ্যই অফিশিয়াল ডকের বিকল্প নেই।

> /home/maateen/Desktop/test.py(1)<module>()
-> def square(x):
(Pdb) s
> /home/maateen/Desktop/test.py(6)<module>()
-> def main():
(Pdb) b 6
Breakpoint 1 at /home/maateen/Desktop/test.py:6
(Pdb) s
> /home/maateen/Desktop/test.py(10)<module>()
-> if__name__=="__main__":
(Pdb) n
> /home/maateen/Desktop/test.py(11)<module>()
-> main()
(Pdb) c
1
4
9
16
25
36
49
64
81
100
The program finished and will be restarted
> /home/maateen/Desktop/test.py(1)<module>()
-> def square(x):
(Pdb) q

লগিং

একটা প্রোগ্রাম যখন চলে, তখন অনেকগুলো ইভেন্ট ঘটে। এই ইভেন্টগুলো ট্রাক করে রেকর্ড রাখাই হলো লগিং। প্রোগ্রাম চলার সময় এটা বিভিন্ন ধরনের কাজ করে, এতে নানা ধরনের এরর ঘটতে পারে যা এক্সেপশনের মাধ্যমে হ্যান্ডেল করা হয়, এসব ঘটনা মেসেজ আকারে লগে লিখে রাখা হয়। যাতে প্রোগ্রাম ডিবাগ করতে সুবিধা হয়।

পাইথনের স্ট্যান্ডার্ড লাইব্রেরিতে খুবই শক্তিশালী একটা লগিং (logging) মডিউল আছে। সেটি ব্যবহার করে আমরা এখন একটি সিম্পল লগার (logger) তৈরি করব।

এ জন্য আমরা আমাদের test.py-তে কিছু কোড লিখব :

import logging

logging.basicConfig(filename='test.log', level=logging.INFO)

logging.debug('This is a debug message.')
logging.info('This is an informational message.')
logging.error('This is an error message.')

তৈরি করে ফেললাম একটা সিম্পল লগার। কিন্তু ঘটনা কি ঘটালাম আমরা? ব্যাখ্যা করা যাক।

logging.basicConfig(**kwargs) ফাংশনটা ডিফল্ট ফরম্যাটারের সাথে একটা স্ট্রিমহ্যান্ডলার অবজেক্ট তৈরি করে। তারপর সেটাকে রুট লগারে অ্যাড করে আমাদের লগিং সিস্টেমের বেসিক কনফিগারেশন তৈরি করে। মোদ্দা কথা, আমাদের লগিং সিস্টেমের ইনিশিয়াল স্ট্রাকচার তৈরি করে দেয় আর কি! যাহোক, ওপরের প্রোগ্রামটা চালালে আমরা দেখব test.py এর সাথে একই ফোল্ডারে test.log নামে আরও একটা ফাইল তৈরি হয়েছে। সেখানে info এবং error দুটি মেসেজ আছে। কিন্তু debug মেসেজটা নেই। কারণ আমরা আমাদের লগারের মেসেজ লেভেল INFO-তে সেট করেছি। তাই এটা শুধু info, warning, critical ও error মেসেজই সেভ করবে ফাইলে।

এটা চেষ্টা করা যাক :

import logging

logging.basicConfig(filename='test.log', level=logging.INFO)

a = 10
b = 0

try:
    temp = a/b
    print(temp)
except ZeroDivisionError as e:
    logging.exception(e)

এবার আমরা একটা রিয়েল লাইফ ইস্যুতে লগার ব্যবহার করলাম। আমরা ইতিমধ্যে জানি যে কোনো সংখ্যাকে শূন্য দিয়ে ভাগ করতে গেলে পাইথন ZeroDivisionError এক্সেপশন থ্রো করে। আমরা এই এক্সেপশনের পুরো এরর মেসেজটাই লগে সেভ করে রাখলাম logging.exception() ফাংশনের সাহায্যে। আরেকটা লক্ষ করার মতো ব্যাপার হলো, আমাদের আগের উদাহরণের সেই মেসেজ দুটিও কিন্তু রয়ে গেছে। কারণ হলো, ফাইলে সবকিছু অ্যাপেন্ড হচ্ছে। আমরা চাইলে ফাইলটাকে রাইট মোডেও ব্যবহার করতে পারি। এ জন্য logging.basicConfig(**kwargs) এর মধ্যে filemode='w' সেট করতে হবে।

আমরা এখন আরেকটা উদাহরণ দেখব। তবে তার আগে আমাদের সেই hijibiji.py-তে আমরা যোগ করার ও কোনো ইন্টিজার জোড় সংখ্যা কি না, সেটা টেস্ট করার ফাংশন লিখব:

def add(a, b):
    return a + b

def is_even(number):
    if (number % 2) == 0:
        return True
    else:
        return False

কোড লেখা হয়ে গেছে। এবার তাহলে এর মধ্যে লগিং পুরে দেওয়া যাক। এ জন্য আমরা test.py-তে কোড লিখব :

import logging
from hijibiji import add, is_even

logging.basicConfig(filename='test.log', level=logging.INFO)
logging.info('We are calling our add function.')
temp = add(12, 78)
print(temp)
logging.info('add function executed, task completed.')
logging.info('We are calling our is_even funcion.')
temp = is_even(2)
print(temp)
logging.info('is_even function executed, task completed.')

প্রোগ্রামটা চালালে দেখব ফাইলে আরও কিছু ইনফরমেশনাল মেসেজ যোগ হয়েছে। কিন্তু মেসেজগুলো রসকষহীন। এগুলাকে এবার একটু স্পাইচি (সুস্বাদু) করা যাক। এ জন্য আমরা test.py-তে কোড লিখব:

import logging
from hijibiji import add, is_even
logging.basicConfig(filename='test.log', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)

logging.info('We are calling our add function.')
temp = add(12, 78)
print(temp)
logging.info('add function executed, task completed.')
logging.info('We are calling our is_even funcion.')
temp = is_even(2)
print(temp)
logging.info('is_even function executed, task completed.')

এটা চালানোর পর লগফাইল চেক করলে দেখব, আগের চেয়ে মসলাদার হয়েছে মেসেজগুলো। লগিং নিয়ে আরও বেশি জানার জন্য অফিশিয়াল ডকুমেন্টেশন- https://docs.python.org/3/library/logging.html-এ ঘুরে আসতে পারেন।

comments powered by Disqus