ในชุด 2 ส่วนนี้ ส่วนแรกครอบคลุมการ setup AppSignal ใน Rails app เพื่อ insights เยอะมาก out-of-the-box AppSignal track errors, monitor performance, report metrics dependencies อัตโนมัติ
แต่หลายกรณีแต่ละ app 行為แตกต่าง ต้องการมากกว่า generic monitoring
ในโพสต์นี้ จะแนะนำการเพิ่ม custom instrumentation และ monitoring ให้ Rails app เพื่อ insights ลึกขึ้น
Custom Instrumentation และ Monitoring
ต้องการมากกว่า AppSignal instrument out-of-the-box? AppSignal gem ให้เพิ่ม custom instrumentation ได้
Instrumenting ส่วนของโค้ด
สมมติอยากเพิ่ม feature: user เยี่ยม /posts ดู posts ทั้งหมด ควร filter posts ที่ title เริ่มต้นด้วยตัวอักษรเฉพาะ (หรือซับซ้อนกว่านี้ 🪄)
Search functionality นี้ implement ไว้ใน Post model ด้วย Post.where_title_starts_with อัปเดต PostsController#index ใช้เมธอดใหม่ถ้ามี query parameter:
# app/controllers/posts_controller.rb def index starts_with = params[:starts_with] @posts = if starts_with.present? Post.where_title_starts_with(starts_with) else Post.all end end ส่วนสำคัญของ app อยากรู้ performance และการเปลี่ยนแปลง AppSignal มีหลายวิธี
ขั้นแรก instrument เนื้อหา Post.where_title_starts_with ใช้ instrumentation blocks wrap code blocks:
# app/models/post.rb def self.where_title_starts_with(letter) Appsignal.instrument('Post.where_title_starts_with', "Fetch posts that start with letter") do Analytics.track_post_title_search(letter.downcase) select('*, pg_sleep(0.01)').where("title ILIKE :letter", letter: "#{letter.downcase}%").load end end ขั้นที่สอง instrument Analytics.track_post_title_search เพราะ app/services/analytics.rb ทำ heavy processing ใช้ method instrumentation:
# app/services/analytics.rb require 'appsignal/integrations/object' class Analytics def self.track_post_title_search(letter, sleep = sleep(1)) # Some heavy processing sleep 1 end appsignal_instrument_class_method :track_post_title_search end Insights
หลังเซฟโค้ด สักครู่ ดูข้อมูลใหม่บน AppSignal dashboard (อาจต้อง restart docker containers) ทดสอบ feature: https://localhost:3000/posts?starts_with=f
ขึ้นอยู่กับจำนวน posts ใน database /posts endpoint อาจช้าลงมาก
เปิด performance issues (Performance → Issue list) ดู PostsController#index ล่างหน้าจะมี 'Event Timeline' แสดงเวลาที่ใช้รันโค้ดเฉพาะ:
Timeline นี้มีทุก performance events แต่ที่นี่เห็น custom instrumentation events แสดงว่า Post.where_title_starts_with ใช้เวลา 8.84 วินาที โดย Analytics.track_post_title_search ใช้ 2.01 วินาที ส่วนที่เหลือเป็น active record query คลิก event ย่อยได้รายละเอียดเพิ่ม เช่น sql.active_record
Instrumentation helpers ให้ breakdown รายละเอียด ทำให้เข้าใจโค้ดที่อาจส่งผลต่อ performance เรียนรู้เพิ่มใน instrumentation guide
Exception Handling
นอกจาก monitor performance ต้องรู้ว่า app ไม่ทำงานตามคาดหรือผิดที่ไหน AppSignal report unhandled exceptions แล้ว แต่มีมากกว่านั้น
เริ่มด้วยการลบโค้ดที่ทำ error intermittent ดู backtrace บน dashboard ใน app/controllers/pages_controller.rb ลบ if statement:
class PagesController < ApplicationController def home CreateRandomPostsJob.perform_later end end Error rate บน dashboard จะลดลงมาก
ตอนนี้ user ดู post ที่ไม่มีอยู่ app จะ crash (เช่น https://localhost:3000/posts/doesnotexist) อยากแสดงข้อความแทน เพิ่ม rescue ใน PostsController อัปเดต #set_post:
# app/controllers/posts_controller.rb class PostsController < ApplicationController . . . private def set_post @post = Post.find(params[:id]) rescue ActiveRecord::RecordNotFound => e render json: { error: "Oops. That post isn't here" } , status: :not_found end . . end เพราะ handle exception เอง จะไม่ report ไป AppSignal อัตโนมัติ ยัง track ได้ด้วย Appsignal.set_error
วิธีง่าย: ส่ง error เป็น argument Appsignal.set_error(e) ใช้ Appsignal.tag_request เพิ่ม context:
def set_post Appsignal.tag_request(user_id: 'user-from-params', post_id: params[:id]) @post = Post.find(params[:id]) rescue ActiveRecord::RecordNotFound => e Appsignal.set_error(e) render json: { error: "Oops. That post isn't here" }, status: :not_found end ทดสอบ https://localhost:3000/posts/doesnotexist ควรได้ JSON response แทน crash
Insights
ลองดู post ที่ไม่มีอยู่ Error จะถูก report ไป AppSignal บน dashboard (Errors → Issue list) หา error ใหม่ (ActiveRecord::RecordNotFound)
Error detail ให้ context มีประโยชน์: HTTP method, parameters, session data และ custom tags ที่เพิ่มไว้ สามารถ filter errors ด้วย tag ได้
เพราะ tag request ข้อมูลเพิ่มไปที่ errors และ instrumented events อื่นๆ ดู post ปกติ (https://localhost:3000/posts/1) tags ก็อยู่ใน performance measurement (Performance → Issue list → PostsController#show) อ่านเพิ่มใน guides
ความสามารถเพิ่ม custom metadata เปิดโอกาส diagnose issues ใน production ตัวอย่างดี: เพิ่ม Kubernetes metadata ให้ errors
Metrics
ตอนมี custom instrumentation และ error monitoring แล้ว อาจสังเกต spikes ใน posts searches ทุกครั้งที่ user search Analytics#track_post_title_search ถูกเรียก ทำ calculations และ API call ไป third-party ที่มี rate limits อยาก track ว่าเรียกบ่อยแค่ไหน
AppSignal ให้ track custom metrics ตลอด app
ขั้นแรก track ความถี่เรียก analytics service ด้วย counter และ tags:
#app/services/analytics.rb require 'appsignal/integrations/object' class Analytics def self.track_post_title_search(letter, sleep = sleep(1)) Appsignal.increment_counter("track_post_search", 1, { letter: letter }) # Some heavy processing sleep 1 end appsignal_instrument_class_method :track_post_title_search end ขั้นที่สอง track จำนวน posts ที่คืนใน PostsController#index เพราะเป็น core behavior และเพิ่มขึ้นเรื่อยๆ:
#app/controllers/posts_controller.rb class PostsController < ApplicationController . . def index . . Appsignal.set_gauge("posts_index", @posts.size, starts_with: params[:starts_with]) end end Fake traffic script ยังรันอยู่ สร้างข้อมูลบ้าง แต่เพิ่ม variety ค้นหา f, l, v ด้วย
Insights
ดู custom metrics ต้องสร้าง dashboard พร้อม custom graphs บน AppSignal ทำผ่าน UI ได้ แต่เราจะ import ตัวอย่างนี้ Under Dashboard → Add dashboard → import JSON:
{ "title": "Post Search", "description": "Sample dashboard about posts search activity", "visuals": [ { "title": "Analytics", "line_label": "%name% %letter%", "display": "LINE", "format": "number", "draw_null_as_zero": true, "metrics": [ { "name": "track_post_search", "fields": [ { "field": "COUNTER" } ], "tags": [ { "key": "letter", "value": "*" } ] } ], "type": "timeseries" }, { "title": "Search", "line_label": "%name% %starts_with%", "display": "LINE", "format": "number", "draw_null_as_zero": true, "metrics": [ { "name": "posts_index", "fields": [ { "field": "GAUGE" } ], "tags": [ { "key": "starts_with", "value": "*" } ] } ], "type": "timeseries" } ] } ภายในไม่กี่นาที จะเห็นข้อมูลบนกราฟ Hover ดู legend ของ metrics ใน timeframe
สังเกต: แสดง lines แยกตาม tag values ปัจจุบัน fake traffic ค้นหาแค่ 'e' แต่เราค้นหา manual อื่นๆ จึงเห็น line ใหม่แต่ละตัว
คิดว่าเพียงพอ? AppSignal มี custom instrumentation อีกเยอะที่ไม่ได้ครอบคลุม หนึ่งอย่างคุ้มค่า: breadcrumbs — track actions list ใน app จะ report ใน error ให้ข้อมูลเฉพาะเจาะจงและเรียงลำดับว่าอะไรนำไปสู่ error
อ่านเพิ่มใน guides
สรุป: Custom Instrumentation และ Monitoring สำหรับ Ruby Apps ด้วย AppSignal
Part 1 ครอบคลุม basic setup และการใช้ AppSignal สำหรับ Ruby apps
Part นี้ เราเอา app ที่มี monitoring ดี out-of-the-box ทำให้ดีขึ้นด้วย AppSignal gem
Custom instrumentation, error tracking, performance monitoring ให้ insights ลึก เกี่ยวกับพฤติกรรม app ให้มาก out-of-the-box แต่ยังควบคุมได้เมื่อต้องการ
ปล่อยให้โค้ดรันเสรีในธรรมชาติ ตราบใดที่คุณติดตามมัน Happy coding!
P.S. อยากอ่าน Ruby Magic posts ทันทีที่เผยแพร่? สมัครรับ Ruby Magic newsletter ไม่พลาดโพสต์เด็ดๆ!