Python LoggerAdapter merge_extra: รวม context ประจำกับข้อมูลต่อ log call
service มักมี context ประจำ request เช่น tenant กับ trace แล้วแต่ละ log call ยังต้องเพิ่ม event-specific field ครับ ตั้งแต่ Python 3.13 LoggerAdapter(..., merge_extra=True) รวม extra ของ adapter กับ extra ที่ส่งตอน log แทนการทิ้งข้อมูลฝั่ง call
WHY: ตรายางประจำใบงานกับโน้ตเฉพาะเหตุการณ์
Adapter context เหมือนตรา tenant ที่ต้องอยู่ทุกใบ ส่วน extra ต่อ call เหมือนโน้ตว่าเหตุการณ์นี้ทำ job ไหน หากไม่ merge เราอาจได้เพียงตราหรือเพียงโน้ต การเปิด merge_extra ทำให้ record มีทั้งสองชุด แต่ key ชนกันยังต้องมี policy ที่ทีมเข้าใจ
import logging
import unittest
class Capture(logging.Handler):
def __init__(self):
super().__init__()
self.records = []
def emit(self, record):
self.records.append(record)
class AdapterTests(unittest.TestCase):
def test_adapter_and_call_context_are_merged(self):
logger = logging.getLogger("showkhun.merge-extra")
logger.handlers.clear()
logger.propagate = False
logger.setLevel(logging.INFO)
capture = Capture()
logger.addHandler(capture)
adapter = logging.LoggerAdapter(
logger,
{"tenant": "forest"},
merge_extra=True,
)
adapter.info("job completed", extra={"job_id": "job-7"})
record = capture.records[0]
self.assertEqual(record.tenant, "forest")
self.assertEqual(record.job_id, "job-7")
if __name__ == "__main__":
unittest.main()
Context ไม่ควรกลายเป็นถังรวมข้อมูลลับ
กำหนด allowlist field เช่น trace_id, tenant_id, job_id และห้าม password, token, cookie หรือ payload ลูกค้าเข้า extra แม้ formatter ปัจจุบันไม่แสดง field เพราะ handler ใหม่อาจ serialize ทั้ง record ภายหลัง
ถ้า key ต่อ call ชนกับ adapter context ให้ทดสอบ precedence ตามเวอร์ชันที่รองรับและตั้งชื่อ namespace ให้ชัด เช่น request_id กับ job_id อย่าปล่อยให้ caller เปลี่ยน tenant ที่เป็น security context ผ่าน log field และควรย้าย propagation context ข้าม task/thread ด้วยกลไกที่เหมาะ เช่น contextvars
Recap
- เปิด
merge_extra=Trueเมื่อต้องการทั้ง context ประจำและข้อมูลต่อ call - ตรวจ
LogRecordจริงใน test ไม่ดูเพียงข้อความ formatter - กำหนด key และ precedence เป็น contract
- redact secret ก่อนสร้าง record ไม่ใช่รอปลายทาง