จัดการ Error ของ Django อย่างมีประสิทธิภาพด้วย Sentry

หน้า overview ของ Sentry project
ที่พรอนโต้เราต้องดูแล internal service หลายตัวมากในการรองรับลูกค้าหลายพันคน ซึ่งพอถึงจุดๆ ที่ service เราถูกปล่อยไปให้ลูกค้าใช้แล้วนั้น แน่นอนว่าจะเริ่มมีปัญหาตามมา มากบ้างน้อยบ้าง ปนๆ กันไป เนื่องจาก service ที่เราดูแลส่วนใหญ่ จะสร้างจาก Django web framework เพราะฉะนั้นเราเลยอาศัยกลไก Error Reporting ของตัว Django เองซึ่งหน้าตาก็ไม่ค่อยน่ารักเท่าไรมาตลอด
จนกระทั่งเมื่อประมาณปีที่แล้ว เราได้ทดลองเอา Sentry.io มาใช้จัดการ Error ที่เกิดขึ้นในระบบ แล้วค้นพบว่ามันช่วยแก้ปัญหาการจัดการ Error ได้ดีทีเดียว ทั้งเก็บสถิติ Error ที่เกิดขึ้น หน้า Issue ที่มี Traceback ที่ค่อนข้างจะอ่านง่ายกว่า Error Reporting ของ Django แถมเก็บ Variable ใน state ขณะที่เกิด error ไวด้วย อีกทั้งยังสามารถแจ้งเตือนผ่าน Email หรือ Integrate เป็น Chat bot ใน Slack ได้อีกด้วยครับ

ติดตั้ง Sentry ให้กับ Django project

ก่อนอื่นเลยลง packages ก่อนผ่าน pip โดย

pip install raven

หลังจากนั้นให้เราเพิ่ม raven เข้าไปใน INSTALLED_APPS ซึ่งอยู่ใน settings ของ Django project ครับ

INSTALLED_APPS = INSTALLED_APPS + [
    'raven.contrib.django.raven_compat'
]

ต่อไปเราจะทำการเพิ่มตัว DSN (Data Source Name) ของ Sentry ซึ่งเอาไว้ใช้ในการบอกว่าจะให้ส่ง Error จาก service เราไปไว้ที่ไหนใน Sentry โดย Sentry จะเตรียมไว้ให้เราหลังจากเราสมัครสมาชิกและสร้างโปรเจ็คแล้ว โดยสามารถไปหาได้ที่ Project settings → Data → Client Keys (DSN) ครับ เมื่อได้ DSN มาแล้วให้เพิ่ม settings อีกตามนี้เลยครับ (แต่ใน Production Environment แนะนำให้เก็บไว้ใน Environment Variable นะครับ)

RAVEN_CONFIG = {
    'dsn': 'https://*******@sentry.io/123456'
}

หลังจากเพิ่ม Config แล้วให้ลองเทสดูครับว่า Sentry ส่ง Error report ออกมาได้มั้ยโดยใช้ management command ตามนี้ครับ

python manage.py raven test

ถ้าไม่มี Error อะไรเกิดขึ้นก็น่าจะได้ Output ประมาณนี้ครับ

Client configuration:
  base_url       : https://sentry.io
  project        : 123456
  public_key     : abc1234acacab123
  secret_key     : def12312dedef12
Sending a test message... Event ID was 'dae24622d81c4ae59e7e2de38e8e187f'

จัดการ Error Report ของ Microservices

จากที่ผ่านมาแค่นั้นก็น่าจะพอสำหรับ การใช้ Sentry จัดการ Error Report แต่โปรเจ็คที่พรอนโต้อย่าง SimpleSat ตัว Backend เราประกอบจาก Microservices หลายตัวครับ ซึ่งถ้า Config แค่พื้นฐานไป การจะรู้ว่า Error ที่เกิดขึ้นนั้น เกิดขึ้นที่ service ไหนหรือ environment ไหนนั้นต้องดูจาก Traceback ของโค้ดอย่างเดียว เราเลยใช้วิธี Override ตัว Raven’s DjangoClient ให้ส่ง tags ไปเพิ่มนอกจาก tags พื้นฐาน แล้วเพิ่มตัวแปร SENTRY_CLIENT ให้ชี้มาที่ Class ที่เรา Override แทนหน้าตาประมาณข้างล่างครับ

from raven.contrib.django.client import DjangoClient as RavenDjangoClient

# override base Sentry Django client to sending service information
class SentryDjangoClient(RavenDjangoClient):
    def build_msg(self, *args, **kwargs):
        data = super(RavenDjangoClient, self).build_msg(*args, **kwargs)
        data['tags']['SIMPLESAT_SERVICE'] = 'CUSTOMER'
        data['tags']['SIMPLESAT_ENV'] = 'PRODUCTION'
        return data

SENTRY_CLIENT = 'service_customer.settings.production.SentryDjangoClient'

ด้วยวิธีนี้ ทำให้นอกจากเราจะรวม Error Report ของทุก Microservices มาไว้ที่เดียวกันแล้ว เรายังสามารถรู้ได้ทันทีเลยว่า Error นี้เกิดที่ Service ไหนและ Environment ไหน ทำให้ลดเวลาที่ใช้ในการไล่ปัญหาลงได้เยอะมากครับ

ตัวอย่างหน้า Issue ของ Sentry
สุดท้ายแล้ว จริงๆ ตัว Sentry นี่สามารถ Config อะไรได้มากกว่านี้อีกใน Python ไม่ว่าจะเป็น Middleware เพื่อรับ request ได้หรือจะไป Integrate กับ Django log แม้กระทั่งต่อกับ Celery ก็ได้ แต่ผมขอหยุดไว้เท่านี้ หวังว่าทุกคนคงมีความสุขกับการจัดการ Error มากขึ้นนะครับ


Comments