SWR баг — Logging стандарт
Хамрах хүрээ: төвлөрсөн цуглуулагч руу лог илгээдэг бүх сервис · Формат: stdout руу JSON · Талбарын нэршил: OpenTelemetry semantic conventions · Цуглуулга: Loki / ELK / Datadog
Энэ бол гаралтад тавих шаардлага болохоос logging library-ийн заавар биш. Ямар хэл, ямар library ашиглахыг баг өөрөө сонгоно (Go
log/slog, Rusttracing, Nodepino— ялгаагүй). Гаралт нь §3-ын формат, §4-ийн талбартай таарч байвал стандарт хангагдсан. Кодын жишээ энд байхгүй нь санаатай: library-ийн API өөрчлөгддөг, гаралтад тавих шаардлага өөрчлөгддөггүй.
1. Үндсэн зарчим
- Лог бол машин уншдаг өгөгдөл, хүнд зориулсан өгүүлбэр биш. Түүнийг
grep-ээр биш, query-гээр уншина. - Нэг үйл явдал = нэг JSON мөр. Олон мөрт текст, stack trace нь тусдаа талбар болно.
messageтогтмол, утга нь талбар. Ингэснээр групплэх, тоолох, дохиолол тавих боломжтой.- Нэг хүсэлт = нэг лог мөр (§7). Алхам бүрийг INFO-гоор бичих нь өртгийг нэмж, уншихад хэцүү болгоно.
- Нууц мэдээлэл хэзээ ч логонд орохгүй (§10). Гаргаснаа буцааж авах арга байхгүй.
- Лог бол хамгийн үнэтэй, хамгийн бүтэц муутай сигнал. Тоолж болох зүйлийг metric, гүйцэтгэлийн замыг trace, үлдсэнийг нь лог болго.
Хэмжсэн үндэслэл (2026 оны 09 дүгээр сарын 21): twelve-factor (
12factor.net/logs), OpenTelemetry logs data model ба semantic conventions, W3C Trace Context, Stripe-ийн canonical log lines, Kubernetes-ийн logging conventions, OWASP Logging Cheat Sheet, Grafana Loki-гийн label best practices — эдгээр анхдагч эх сурвалжийг шалгаж бичсэн (§15).
2. Хамрах хүрээ
| Хамаарна | Хамаарахгүй |
|---|---|
| stdout руу лог гаргаж, цуглуулагч (Loki/ELK/Datadog) руу орж байгаа бүх сервис | Metric ба trace — өөр сигнал, өөр стандарт |
| Batch job, worker, cron, CLI хэрэгсэл | Төхөөрөмж дээрх клиент (Android/logcat) — нэршлийг нь дагана, харин pipeline-ын дүрэм (§3, §12) хамаарахгүй |
| Дотоод сервис хоорондын дуудлага | Audit log — албан ёсны эх сурвалж нь БД дэх audit хүснэгт байна. Лог бол зөвхөн хуулбар (§11) |
3. Гаралтын формат
- stdout руу бич. Файл руу бичих, log rotation хийх, лог илгээх код бичих шаардлагагүй — эдгээрийг цуглуулагч гүйцэтгэнэ.
- Мөр бүр нэг бүтэн JSON объект, UTF-8,
\n-ээр төгсөнө. Хагас мөр, олон мөрт JSON байж болохгүй. - Талбарын түлхүүр нэг түвшинд (flat) байна; цэгтэй нэрийг шууд түлхүүр болгоно:
"service.name": "payment-api". Үүрлэсэн объект ашиглавал цуглуулагч дээр нэг түвшинд задардаг байх ёстой. - Тоог тоогоор,
true/false-ийг boolean-оор бич."duration_ms": "42"гэж тэмдэгт мөр болговол дундаж, percentile тооцох боломжгүй болно. - Өнгө (ANSI), баннер,
progress barproduction-д байхгүй.
{
"@timestamp": "2026-09-21T04:19:27.337Z",
"log.level": "INFO",
"message": "Handled HTTP request",
"service.name": "payment-api",
"service.version": "a1b2c3d",
"deployment.environment.name": "production",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"http.request.method": "POST",
"url.path": "/v1/charges",
"http.route": "/v1/charges",
"http.response.status_code": 201,
"duration_ms": 42,
"user.id": "usr_18f3",
"payment.order_id": "ord_92kd"
}
4. Талбарууд
4.1 Заавал
| Талбар | Утга |
|---|---|
@timestamp | RFC3339 / ISO-8601, UTC, миллисекундтэй |
log.level | DEBUG / INFO / WARN / ERROR (том үсгээр) |
message | Тогтмол, богино тайлбар — хувьсагч оруулахгүй (§5) |
service.name | Сервисийн нэр, ж: payment-api. Бүх орчинд ижил бичигдэнэ |
service.version | Build хувилбар эсвэл git SHA |
deployment.environment.name | dev / staging / production |
4.2 Нөхцөлт (боломжтой үед заавал)
| Талбар | Хэзээ |
|---|---|
trace_id, span_id | Хүсэлт/ажлын контекст доторх бүх лог (§9) |
http.request.method, url.path, http.route, http.response.status_code | HTTP хүсэлт боловсруулсан бол |
duration_ms | Хугацаа хэмжсэн үйлдэл (тоогоор) |
error.type, error.message, error.stack_trace | Бүх ERROR мөрөнд (§8) |
user.id | Хэрэглэгчийн нэрийн өмнөөс хийсэн үйлдэл — ID, нэр/имэйл биш |
client.address, user_agent.original | Гаднаас ирсэн хүсэлт, аюулгүй байдлын үйл явдал |
4.3 Нэршлийн дүрэм
- OpenTelemetry-д нэр нь тодорхойлогдсон бол түүнийг ашигла —
http.request.method(methodбиш),url.path(pathбиш),http.response.status_code(statusбиш). Ингэснээр нэршлийн талаар маргах шаардлагагүй болж, хожим OTel collector эсвэл trace рүү шилжихэд лог аль хэдийн нийцсэн байна. - OTel-д байхгүй домэйн талбарыг
<домэйн>.<талбар>хэлбэрээр,snake_case-ээр:payment.order_id,vms.machine_id,tether.device_serial. Ерөнхийid,name,typeгэж дангаар бүү нэрлэ. - Нэг түлхүүр = нэг утга, бүх сервист адил.
user.idгэж нэг газар,uidгэж нөгөө газар бичих нь хоёр өөр талбар үүсгэж, хайлт ажиллахгүй болно. - Хэмжих нэгжийг нэрэнд нь бич:
duration_ms,size_bytes,amount_mnt. - Талбарын утга нь хязгааргүй урт биш — том агуулгыг (request body, HTML, base64) логонд бүү хий. Шаардлагатай бол уртыг нь хязгаарлаад
..._truncated: trueтэмдэглэ.
5. message бичих
message бол лог мөрүүдийг групплэх түлхүүр. Динамик утга орвол групп бүрдэхээ болино.
| Зөв | Буруу | Шалтгаан |
|---|---|---|
"message": "Handled HTTP request" + талбарууд | "message": "GET /v1/charges 201 in 42ms" | Бүх мөр өөр — тоолох, дохиолох боломжгүй |
"message": "Charge failed" + payment.order_id | "message": "Charge failed for order ord_92kd" | ID нь мөрийн текст дотор орвол индексжихгүй |
"message": "Deleted expired session" | "message": "done" | Юу болсныг хэлээгүй |
Найруулгын дүрэм (Kubernetes-ийн конвенцийг дагасан):
- Том үсгээр эхэл, төгсгөлд нь цэг бүү тавь.
- Идэвхтэй хэлбэр, өнгөрсөн цаг:
"Could not delete pod","Deleted pod". - Объектыг нэрлэхдээ төрлийг нь хэл:
"Deleted session", зүгээр"Deleted"биш. - Англи хэлээр бич — grep, дохиолол, гадаад хэрэгсэлтэй нийцтэй байна.
6. Түвшин
| Түвшин | Хэзээ | Жишээ |
|---|---|---|
DEBUG | Хөгжүүлэлт, алдаа хайх нарийн мэдээлэл. Production-д байнга идэвхтэй байхгүй | завсрын утга, гадаад дуудлагын payload (нууцгүй) |
INFO | Хэвийн боловч ач холбогдолтой үйл явдал | хүсэлт боловсруулсан, төлбөр амжилттай, ажил эхэлсэн |
WARN | Гэнэтийн боловч систем үргэлжилсэн нөхцөл | retry хийсэн, fallback ажилласан, quota-д ойртсон |
ERROR | Үйлдэл амжилтгүй болсон бөгөөд хүн арга хэмжээ авах ёстой | БД-тэй холболт тасарсан, гадаад төлбөрийн систем хариу өгөхгүй |
Дүрэм:
ERRORнь дохиоллын түвшин. Хэн ч хариу арга хэмжээ авахгүй болWARNболго — эсвэл бүү бич. Хэт олон давтагдсанERROR-ыг хэсэг хугацааны дараа хэн ч үзэхээ болино.- Хэрэглэгчийн буруугаас үүссэн хүлээгдэж буй алдаа (400, 404, баталгаажуулалт амжилтгүй) нь
ERRORбиш —INFO/WARN. FATAL/panic-ийг зөвхөн эхлэх үед (тохиргоо уншиж чадаагүй, порт нээгдэхгүй) ашигла. Хүсэлт боловсруулах явцад процессоо бүү зогсоо.- Production-ийн default:
INFO. Түвшинг дахин build хийхгүйгээр (env, config reload) сольж чаддаг байх ёстой. - Шинэ түвшин бүү нэм. Дөрөв хангалттай —
TRACE,NOTICE,CRITICALнэмбэл хэн ч ялгааг нь мэдэхгүй.
7. Нэг хүсэлт = нэг лог мөр
Хүсэлт бүрийн төгсгөлд бүх мэдээллийг нэгтгэсэн (canonical) нэг мөр бич: тухайн хүсэлтийн талаар мэдэх хэрэгтэй бүх зүйл нэг мөрөнд.
- Багтаах талбарууд:
http.request.method,http.route,http.response.status_code,duration_ms,trace_id,user.id, домэйн ID (payment.order_id), алдаа гарсан болerror.type, тоолуур (db.duration_ms,upstream.duration_ms). - Алхам бүрт INFO бүү бич. Алхмууд нь
DEBUGэсвэл span. - Ашиг: нэг хүсэлтийн явцыг сэргээхийн тулд олон мөр нийлүүлэх шаардлагагүй, лог хэмжээ хэд дахин буурч, тоолох болон групплэх нь ганц query болж хялбарчилна.
- Health check, readiness probe, static asset-ийн хүсэлтийг бүү логло — зөвхөн хэрэггүй мөр нэмнэ.
8. Алдааны лог
- Алдааг нэг удаа, хамгийн дээд түвшинд (handler / boundary) логло. Доод давхарга нь алдаагаа буцаана, логлохгүй: дуудагч нь алдааг хүлээж аваад өөрөө зохицуулж магадгүй, тэгвэл доор бичсэн
ERRORхудал дохиолол болно. - Алдааг бүтэцтэй талбарт хий:
error.type(класс/төрөл),error.message,error.stack_trace.message-тэй нийлүүлж бүү бич. - Алдааг буцаахдаа контекст нэм (Go-д
%w, Rust-дanyhow/thiserror, Java-д cause) — тэгвэл boundary дээрх ганц лог алдаа хаанаас эхэлснийг бүрэн харуулна. - Контекст талбарыг (
payment.order_idгэх мэт) тухайн лог мөрөнд заавал нэм. "Charge failed" гэсэн ганц мөрөөр юу болсныг тогтоох боломжгүй. - Process унасан тохиолдолд (panic/crash) — нэг мөр, stack trace
error.stack_traceталбарт.
9. Trace холболт ба контекст
- Сервис хооронд W3C Trace Context header дамжуулна:
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01(version-trace_id-span_id-flags). Header байхгүй бол шинээр үүсгэ. Байгаа бол дараагийн сервис рүү дамжуул;traceparent-ийг өөрчлөөгүй болtracestate-ийг ч бүү өөрчил. - Хүсэлтийн туршид
trace_id,span_id-г контекстэд (Gocontext.Context, MDC, task-local) хадгалж, лог бүрт автоматаар нэмэгддэг байхаар тохируул. Гар аргаар талбар нэмэх нь мартагдана. - Thread/goroutine дахин ашиглагддаг — хүсэлт дуусахад контекстээ цэвэрлэ, эс бөгөөс өмнөх хэрэглэгчийн ID дараагийн хүсэлтийн лог дээр буруугаар бичигдэнэ.
- Асинхрон ажил, дараалал (queue, cron):
traceparent-ийг мессежийнheader-т дамжуулж, ажлын лог эх хүсэлттэйгээ холбогдоно. request_id(эсвэлX-Request-ID) хэрэглэсээр байж болно, гэхдээtrace_idнь үндсэн холбоос — trace болон логийг хооронд нь холбодог цорын ганц талбар юм.
10. Нууцлал — юу логлохгүй вэ
Хэзээ ч логлохгүй:
- Нууц үг,
token, API key, refresh token, session ID, cookie,Authorizationheader, JWT-ийн бүтэн агуулга - Картын дугаар, CVV, банкны дансны дугаар (шаардлагатай бол
****1234) - Хувийн мэдээлэл: овог нэр, утас, и-мэйл, регистрийн дугаар, хаяг — ID-гаар орлуул (
user.id) - Шифрлэлтийн түлхүүр, холболтын мөр (connection string), дотоод тохиргооны нууц
Дүрэм:
- Эх үүсвэр дээрээ хас. Цуглуулагч дээрх шүүлтүүр бол зөвхөн нөөц хамгаалалт — library солигдох, шинэ талбар нэмэгдэх бүрт ажиллахаа болино.
- Нууц утгыг тусгай төрлөөр илэрхийл: тухайн төрөл логлогдоход
REDACTEDгаргадаг байх (Goslog.LogValuer, RustDebug-ийг гараар бичих, JavatoString()). Тэгвэл мартах боломжгүй болно. - Бүтэн объект, request/response body-г бүхлээр нь бүү логло — шаардлагатай талбарыг нь сонгож ав.
- Log injection: хэрэглэгчээс ирсэн текстийг
message-тэй бүү нийлүүл. Тусдаа талбарт хийвэл JSON encoding нь CR/LF, хиймэл лог мөр үүсгэх оролдлогыг саармагжуулна. - Эргэлзвэл бүү логло. Нэг удаагийн дебаг хийхийн тулд нэмсэн нууц талбар production-д үлддэг.
11. Юуг заавал логлох вэ
Аюулгүй байдал, санхүүгийн шалгалтад дараах үйл явдлууд заавал лог болно (OWASP-ийн жагсаалтыг тус багийн нөхцөлд тохируулсан):
- Нэвтрэлт амжилттай/амжилтгүй болсон, системээс гарсан, нууц үг сэргээсэн, MFA үйлдэл
- Эрх шалгалтад амжилтгүй болсон оролдлого (403), эрх/роль өөрчлөлт, хэрэглэгч үүсгэх/идэвхгүй болгох
- Админы үйлдэл: тохиргоо өөрчлөх, өгөгдөл устгах, бөөнөөр export хийх
- Мөнгөн гүйлгээ: төлбөр, буцаалт, гар аргаар залруулга —
payment.order_id, дүн, үр дүнтэйгээр tokenолгосон/хүчингүй болгосон, төхөөрөмж bind/unclaim хийсэн (tether)- Сервис эхэлсэн/зогссон, тохиргоо ачаалсан (хувилбар, орчинтой нь)
Албан ёсны audit бичлэг нь лог биш. Хэн юу хийснийг нотлох шаардлагатай бол БД дэх audit хүснэгтэд бич — лог нь хадгалах хугацаа дуусахад устдаг бөгөөд өөрчлөгдөөгүй гэдэг нь баталгаагүй.
12. Хэмжээ, cardinality, хадгалалт
Лог нь хязгааргүй биш — өртөг нь эзлэхүүн, индексийн cardinality-тэй шууд хамааралтай.
- Label (Loki stream) нь зөвхөн тогтмол, бага утгатай талбар байна:
service.name,deployment.environment.name, namespace/cluster.trace_id,request_id,user.id,payment.order_id-г label болгож болохгүй. Эдгээрийг label болгох нь Loki-гийн баримт бичигт тусгайлан нэрлэсэн алдаа юм: индекс томорч, chunk жижигрэнэ. - Бусад бүх талбар лог мөрийн дотор байна; шүүлтийг query дээр хий (
{service_name="payment-api"} | json | user_id="usr_18f3"). - Өндөр давтамжтай, хэвийн үйл явдалд sampling тавь (ж: 1/100), алдааг 100% үлдээ.
- Давтагдсан алдааг мөр бүрээр бүү бич — нэгтгэж тоолуураар (
occurrences) эсвэл metric-ээр гарга. - Хадгалах хугацааг баг бүр тодорхойлж, энэ хуудсанд бичнэ. Default: 30 хоног. Үүнээс урт шаардлагатай бол тэр өгөгдөл лог биш — audit хүснэгт эсвэл архивт хадгалагдана.
DEBUG-ийг production-д идэвхжүүлбэл хугацаатай, зорилготой идэвхжүүл (нэг сервис, нэг асуудал) — идэвхтэй хэвээр орхивол өртөг шууд хэдэн дахин өснө.
13. Хөгжүүлэлтийн орчин
- Хөгжүүлэгчийн машин дээр хүн уншихад ээлтэй (текст, өнгөтэй) гаралтыг зөвшөөрнө —
LOG_FORMAT=textгэх мэт нэг л тохиргоогоор солигддог байх. staging,productionдээр үргэлж JSON. Хоёр орчны ялгаа зөвхөн формат, түвшинд байна — логлох газар, талбар нь ижил.System.out.println,fmt.Println,console.log,println!— сервисийн код дотор байж болохгүй. Эдгээр нь цуглуулагч дээр бүтэцгүй мөр болж, хайлтад олдохоо болино.
14. Шалгах жагсаалт
Хийх (Do)
- stdout руу нэг мөр JSON ·
messageтогтмол · утгыг талбарт, тоог тоогоор - OTel нэршил (
http.request.method,url.path) · домэйн талбар<домэйн>.<талбар> trace_id/span_idконтекстээс автоматаар ·traceparent-ийг дамжуулах- Хүсэлт бүрт нэг canonical мөр · алдааг boundary дээр нэг удаа,
error.*талбартай - Нууцыг эх үүсвэр дээр хасах · §11-ийн үйл явдлыг заавал логлох
Бүү хий (Don't)
message-д утга оруулах ·"duration_ms": "42"гэж тэмдэгт мөр болгох · чөлөөт текст,println- Нууц, PII, бүтэн body логлох · хэрэглэгчийн текстийг
message-тэй нийлүүлэх - Алдааг давхар логлох · арга хэмжээ шаардахгүй зүйлийг
ERRORбичих trace_id,user_id-г Loki label болгох · health check логлох- Production-д
DEBUG-ийг идэвхтэй хэвээр орхих · хүсэлтийн явцад процессоо зогсоох
15. Эх сурвалж
Энэ стандартын дүрэм бүр дараах анхдагч эх сурвалжаас гаралтай (2026 оны 09 дүгээр сарын 21-нд шалгасан):
| Эх сурвалж | Юуг тодорхойлсон |
|---|---|
| The Twelve-Factor App — Logs | stdout руу бичих, чиглүүлэлт/хадгалалтыг апп хийхгүй (§3) |
| OpenTelemetry semantic conventions · logs data model | Талбарын нэршил, түвшний утга, trace холбоос (§4, §6) |
| W3C Trace Context | traceparent формат ба дамжуулалт (§9) |
| Stripe — Canonical log lines | Хүсэлт бүрт нэгтгэсэн нэг мөр (§7) |
| Kubernetes logging conventions | message-ийн хэв маяг, алдааг буцаах уу логлох уу (§5, §8) |
| OWASP Logging Cheat Sheet | Заавал логлох үйл явдал, логлохгүй өгөгдөл, log injection (§10, §11) |
| Grafana Loki — label best practices | Label-ын cardinality, юуг label болгохгүй (§12) |
16. Нэр томьёо
| Үг | Юу гэсэн үг вэ |
|---|---|
| structured log | Машин уншиж, indexlэж чаддаг форматтай лог. Энд JSON (§3) |
| collector (цуглуулагч) | Loki / ELK / Datadog — контейнерын stdout-оос логийг цуглуулж, хадгалж, хайх боломжтой болгодог систем |
| stdout | Процессын үндсэн гаралтын урсгал. Лог энд бичигдэнэ, файл руу биш (§3) |
| trace / span | trace нь нэг хүсэлтийн бүх сервисээр дамжсан бүтэн зам; span нь тэр замын нэг алхам. trace_id, span_id нь тэдгээрийн таних дугаар (§9) |
| traceparent | W3C-ийн стандарт header. trace_id, span_id-г сервис хооронд дамжуулна (§9) |
| canonical log line | Хүсэлт бүрийн төгсгөлд бичих, тухайн хүсэлтийн талаарх бүх мэдээллийг агуулсан ганц мөр (§7) |
| cardinality | Талбарын ялгаатай утгын тоо. service.name — цөөн, trace_id — бараг хязгааргүй. Өндөр cardinality-тай талбарыг label болгож болохгүй (§12) |
| label (Loki) | Loki-гийн индексждэг түлхүүр. Утгын тоо цөөн байх ёстой. JSON доторх талбараас өөр зүйл (§12) |
| sampling | Ижил төрлийн лог мөрийн зөвхөн тодорхой хувийг бичих (ж: 1/100) — хэмжээ, өртгийг барих арга (§12) |
| boundary | Хүсэлтийг хүлээж авдаг хамгийн гадна давхарга (HTTP handler, controller). Алдааг энд нэг удаа логлоно (§8) |
context (MDC, context.Context) | Хүсэлтийн туршид зөөгддөг талбаруудын сав. trace_id үүнд сууж, лог бүрт автоматаар нэмэгдэнэ (§9) |
| PII | Хувийн мэдээлэл — нэр, утас, и-мэйл, регистрийн дугаар. Логонд хэзээ ч бичихгүй (§10) |
| redact | Нууц утгыг логонд гарахаас нь өмнө хасах, эсвэл REDACTED болгож солих (§10) |