Skip to content

report-log — Core Pipeline & vtrc-api Request-Log Format

บทนี้ (branch: master) ครอบคลุมสองด้าน: (1) วิธีที่ app.js ประมวลผล และ (2) reverse-engineer รูปแบบ log จาก producer จริงใน vtrc-api


ส่วน A — Flow การประมวลผลใน report-log

app.js IIFE (app.js:208-230)
  │  console.log(users.length)           ← พิมพ์ 9486
  │  dateStart = "2023-02-17"
  │  dateEnd   = "2023-02-18"            ← hard-code ช่วง 1 วันเท่านั้นใน commit ปัจจุบัน

while (dateStart !== dateEnd):
  │  filename = `request_${dateStart}.log`
  │  dateStart = moment(...).add(1, 'd')
  │  data = await readData(filename)
  │  listData.push(data)

(CSV export ที่ app.js:221-229 ถูก comment ปิดทั้งหมด)
  → ผลสรุปอยู่ใน memory เท่านั้น
  → stdout ได้ console.log(dataM)/console.log(dataW) ซึ่งพิมพ์ raw log lines รวม PII

readDatasetData

readData(fileName)                         (app.js:85-88)
  │  fs.readFile(fileName, 'utf-8')

setData(content)                           (app.js:90-148)
  │  split บรรทัดด้วย /(?:\r\n|\r|\n)/g
  │  for แต่ละ pattern ใน array[] (31 รายการ):
  │    dataM = lines ที่ include(mobile) && include(pattern)
  │    dataW = lines ที่ include(web) && include(pattern)
  │    ดึง userId ด้วย regex /"userId":"([^"]+)"/
  │    JMM = users ∩ keyUserM → type "JM"
  │    JWW = users ∩ keyUserW → type "JW"
  │    OMM / OWW = users ที่ไม่อยู่ใน key set → "OM" / "OW"
  │      ※ โค้ดปัจจุบัน filter จาก `users` ด้วย !includes
  │         ทำให้ OMM/OWW เป็นสมาชิก users.json ที่ไม่ปรากฏในวันนั้น
  │         ไม่ใช่ "user นอกจุฬาที่เรียก API" ตามชื่อคอมเมนต์เดิม
  │    push { menu, user, type, date } ต่อ user (ไม่ dedupe ต่อวัน)
  │    count++

return list

ประเภทผลลัพธ์ (ตามชื่อในโค้ด)

typeความหมายตามคอมเมนต์ในโค้ดCitation
JMจุฬา + Mobileapp.js:131-132
JWจุฬา + Webapp.js:134-135
OMOther + Mobileapp.js:137-138
OWOther + Webapp.js:140-141

setVerticalData — dead path

อ่าน ./test-final.csv แล้วเขียน test-final-last.csv (app.js:150-206) — ไม่ถูกเรียกจาก IIFE มีความเสี่ยง index (json[out * i]) เมื่อขนาดข้อมูลไม่ตรง (app.js:197-200)


ส่วน B — Reverse-engineer: ใครเขียน request_*.log?

Pipeline ใน vtrc-api

Apollo GraphQL request


GraphQLLogging.requestDidStart(requestContext)
  │  `vtrc-api/api/src/lib/logging.js:12-17`
  │  buildRequestContextObject → JSON
  │  ข้าม IntrospectionQuery

log.writeLog(`[INFO] ${JSON.stringify(logObj)}`, 'request')
  │  `logging.js:16`

wlog.writeLog(text, type='request')
  │  `vtrc-api/api/src/lib/wlog.js:9-19`
  │  path: {logDir}/../request_{yyyy}-{mm}-{dd}.log
  │  prefix: `{th-TH locale datetime} - {text}\n`

Response log แยกไฟล์ response_{date}.log (logging.js:20-33) — ไม่มีใน repo report-log (มีแค่ request)

ไม่มี morgan/winston ใน path นี้ — เป็น custom wlog + Apollo plugin

โครงสร้างบรรทัดที่ commit ใน report-log

{locale datetime} - [INFO] {JSON}\n

ตรงกับ format ที่ wlog.js:19 เขียน:

`${date.date.toLocaleString('th-th', { timeZone: TIME_ZONE_TEXT })} - ${text}\n`

โดย text = [INFO] ${JSON.stringify(requestLogObj)}

JSON schema จาก producer (logging.js:65-70)

{
  query: operationName  หรือ  query.replace(/\s\s+/g, ' '),
  variables: copiedVariables,   // อาจเป็น undefined/empty
  headers: replaceHeaders,
  from: { appId, userId }
}

Redaction ที่ producer ทำ (และช่องโหว่)

LayerKeys ที่ถูกแทนด้วย _REPLACED_Citation
variablesoldPassword, newPassword, confirmNewPassword, password, confirmPassword, pwd, passwdlogging.js:7,58
headersauthorization, apiKeylogging.js:8,59

ช่องโหว่ที่ยืนยันจากไฟล์ใน report-log:

  1. Header จริงใน log ใช้ชื่อ lowercase apikey — ไม่ตรงกับ apiKey ในรายการ redact → device key ยังโผล่เต็ม ในทุกบรรทัด
  2. Inline GraphQL arguments ใน query string (เช่น login(password:"..."), sendVerifyCode(citizenId:"...")) ไม่ผ่าน replaceVariableValue → plaintext เต็มในไฟล์ที่ commit

ส่วน C — เทียบ field: producer vs committed files

Fieldvtrc-api เขียนมีใน report-log files?Match?
line timestampใช่ (wlog)ใช่ใช่
[INFO] levelใช่ใช่ (ไม่พบ [ERROR] ใน committed set ที่ตรวจ)ใช่
queryใช่ใช่ใช่
variablesใช่ (เมื่อ client ส่ง)ใช่ในบางบรรทัดใช่
headersใช่ใช่ใช่
from.appIdใช่ใช่ใช่
from.userIdใช่ (หรือ null)ใช่ใช่
response_*.logเขียนแยกไม่มีใน repoN/A

ส่วน D — สิ่งที่ app.js อ่านจากแต่ละบรรทัด

สคริปต์ไม่ได้ parse JSON เต็มรูป — ใช้ substring + regex:

สิ่งที่ต้องการวิธีดึงCitation
แยก web/mobileitem.includes(web) / includes(mobile) บน raw lineapp.js:95-103
แยกเมนูitem.includes(pattern) จาก array[]app.js:95-96
userIdregex "userId":"([^"]+)"app.js:100-106

ไม่ได้ใช้ field from.appId, variables, หรือ parse header เป็น object — พอสำหรับรายงานนับเมนู แต่พิมพ์ raw lines ที่มี PII ออก stdout (app.js:108-109)


ส่วน E — Data flow กับระบบอื่น

vtrc-web / vtrc-mobile / backoffice
  │  GraphQL + apikey + Authorization

vtrc-api (GraphQLLogging → request_*.log บน disk ของ API host)
  │  คนคัดลอกไฟล์ออกด้วยมือ (ไม่มี automation ใน report-log)

report-log/request_*.log  (committed)
  │  node app.js

in-memory list + stdout  (CSV comment ปิด)

ไม่มีการเชื่อม network จาก report-log ออกไป — ไม่มี axios/fetch/http ใน app.js