ЁЯУУ рдзрдбрд╛ 02 тАФ Logs: рдЖрд░реЛрдЧреНрдп рдХрдХреНрд╖рд╛рдЪреА рджреИрдирдВрджрд┐рдиреА
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 02 ┬╖ рдорд╛рдЧреЗ: lesson-01-why-observability ┬╖ рдкреБрдвреЗ: lesson-03-metrics
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбрд╛ 01, рдЖрдгрд┐ рдкрд╣рд┐рд▓рд╛ signal: logs. рддреБрдореНрд╣реА structured logging (рдкреНрд░рддреНрдпреЗрдХ рдУрд│реАрдд рдПрдХ JSON
object), log levels, рдкреНрд░рддреНрдпреЗрдХ рдУрд│реАрдд trace id рдХрд╛ рдЕрд╕рддреЛ, рдЖрдгрд┐ рдХрд╛рдп рдХрдзреАрдЪ рд▓рд┐рд╣реВ рдирдпреЗ рд╣реЗ
рд╢рд┐рдХрддрд╛. obs/signals.py рдордзреАрд▓ log() рдЖрдгрд┐ query() log рдУрд│реА рд▓рд┐рд╣рд┐рддрд╛рдд
рдЖрдгрд┐ рд╢реЛрдзрддрд╛рдд; obs/demo.py рдордзреАрд▓ logs() рддреНрдпрд╛ рдЪрд╛рд▓рд╡рддреЗ.
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рдкреНрд░рддреНрдпреЗрдХ рд╡реЗрд│реА рдПрдЦрд╛рджрд╛ рд╡рд┐рджреНрдпрд╛рд░реНрдереА рдЖрд▓рд╛ рдХреА рдХрддрд░рд┐рдирд╛ рдЖрд░реЛрдЧреНрдп рдХрдХреНрд╖рд╛рдЪреНрдпрд╛ рджреИрдирдВрджрд┐рдиреА ЁЯУУ рдордзреНрдпреЗ рд▓рд┐рд╣рд┐рддреЗ.
рддрд┐рдЪреА рдкрд╣рд┐рд▓реА рджреИрдирдВрджрд┐рдиреА рдореЛрдХрд│реНрдпрд╛ рдордЬрдХреБрд░рд╛рдд рд╣реЛрддреА: "3A рдЪреА рдореБрд▓рдЧреА рдЖрд▓реА, рдереЛрдбреА рдЧрд░рдо, рдкрд╛рдгреА рджрд┐рд▓реЗ, рд╕рд╛рдзрд╛рд░рдг 11 рдЪреНрдпрд╛ рд╕реБрдорд╛рд░рд╛рд╕ рдкрд░рдд рдкрд╛рдард╡рд▓реЗ." рдирдВрддрд░ рджреАрдкрд┐рдХрд╛рдиреЗ рд╡рд┐рдЪрд╛рд░рд▓реЗ: "рдпрд╛ рдЖрдард╡рдбреНрдпрд╛рдд 3A рд╡рд░реНрдЧрд╛рддрд▓реЗ рдХрд┐рддреА рд╡рд┐рджреНрдпрд╛рд░реНрдереА рддрд╛рдкрд╛рдиреЗ рдЖрд▓реЗ?" рдХрддрд░рд┐рдирд╛рд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ рдкрд╛рди рд╡рд╛рдЪрд╛рд╡реЗ рд▓рд╛рдЧрд▓реЗ.
рдореНрд╣рдгреВрди рдХрддрд░рд┐рдирд╛рдиреЗ рдкреНрд░рддреНрдпреЗрдХ рдУрд│реАрд╡рд░ рдЪреМрдХрдЯреА рдХрд╛рдврд▓реНрдпрд╛: рд╡реЗрд│ ┬╖ рдХрд┐рддреА рдЧрдВрднреАрд░ ┬╖ рдХрд╛рдп рдШрдбрд▓реЗ ┬╖ рд╡рд░реНрдЧ ┬╖ рд╡рд┐рджреНрдпрд╛рд░реНрдереНрдпрд╛рдЪрд╛ рдХрд╛рд░реНрдб рдХреНрд░рдорд╛рдВрдХ. рдЖрддрд╛ рддреЛ рдкреНрд░рд╢реНрди рдПрдХрд╛ рд╕реЗрдХрдВрджрд╛рдд рд╕реБрдЯрддреЛ: "рд╡рд░реНрдЧ" рдЪреМрдХрдЯреАрдд 3A рдЖрдгрд┐ "рдХрд╛рдп" рдЪреМрдХрдЯреАрдд рддрд╛рдк рд╢реЛрдзрд╛рдпрдЪрд╛.
рдЖрдгрдЦреА рджреЛрди рдирд┐рдпрдо. рдкреНрд░рддреНрдпреЗрдХ рд╡рд┐рджреНрдпрд╛рд░реНрдереНрдпрд╛рдХрдбреЗ рдПрдХ рдорд╛рд░реНрдЧ рдХрд╛рд░реНрдб рдХреНрд░рдорд╛рдВрдХ рдЕрд╕рддреЛ, рдЖрдгрд┐ рдХрддрд░рд┐рдирд╛ рддреЛ рдУрд│реАрд╡рд░ рд▓рд┐рд╣рд┐рддреЗ тАФ рдореНрд╣рдгрдЬреЗ рджреИрдирдВрджрд┐рдиреАрддрд▓реА рдУрд│ рдирдВрддрд░ рдХрд╛рд░реНрдбрд╛рд╢реА рдЬреБрд│рд╡рддрд╛ рдпреЗрддреЗ (рдзрдбрд╛ 05). рдЖрдгрд┐ рдХрддрд░рд┐рдирд╛ рджреИрдирдВрджрд┐рдиреАрдд рд╡рд┐рджреНрдпрд╛рд░реНрдереНрдпрд╛рдЪрд╛ рдШрд░рдЪрд╛ рдкрддреНрддрд╛ рдХрд┐рдВрд╡рд╛ рдкрд╛рд▓рдХрд╛рдВрдЪреЗ bank details рдХрдзреАрдЪ рд▓рд┐рд╣рд┐рдд рдирд╛рд╣реА. рджреИрдирдВрджрд┐рдиреА рдЕрдиреЗрдХ рд▓реЛрдХ рд╡рд╛рдЪрддрд╛рдд. рдЧреБрдкрд┐рддреЗ рддрд┐рдереЗ рдЬрд╛рдд рдирд╛рд╣реАрдд.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
app["ЁЯН│ results-api"] -->|"one JSON object per line"| line["ЁЯУУ {ts, level, msg,<br/>route, status, ms,<br/>trace_id}"]
line --> store["ЁЯЧДя╕П log store<br/>Loki ┬╖ CloudWatch Logs ┬╖ Datadog"]
store -->|"level=ERROR AND route=/results/3A"| two["2 lines<br/>trace ids a3ce929d, b7ad6b71"]
two -.->|"trace_id joins"| tr["ЁЯЧ║я╕П the trace (lesson 05)"]
no["ЁЯЪл never: passwords, tokens,<br/>card numbers, full personal data"]
ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/observability/lesson-diagrams.html#l02
тЭУ рдХрд╛рдп
- Log тАФ рдПрдХрд╛ рдШрдЯрдиреЗрдЪреА рдиреЛрдВрдж, timestamp рд╕рд╣. App рддреА рд▓рд┐рд╣рд┐рддреЗ, log system рддреА рд╕рд╛рдард╡рддреЗ рдЖрдгрд┐ рд╢реЛрдзрддреЗ.
- Unstructured log тАФ рдореЛрдХрд│рд╛ рдордЬрдХреВрд░:
11:40:02 ERROR grade timeout for 3A after 1000ms. рд▓рд┐рд╣рд╛рдпрд▓рд╛ рд╕реЛрдкрд╛; рд╢реЛрдзрд╛рдпрд▓рд╛, рдореЛрдЬрд╛рдпрд▓рд╛ рдХрд┐рдВрд╡рд╛ graph рдХрд╛рдврд╛рдпрд▓рд╛ рдХрдареАрдг (рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рд╢реНрдирд╛рд╕рд╛рдареА рдПрдХ regular expression рд▓рд╛рдЧрддреЗ). - Structured log тАФ рдард░рд▓реЗрд▓реНрдпрд╛ field names рд╕рд╣ рдкреНрд░рддреНрдпреЗрдХ рдУрд│реАрдд рдПрдХ JSON object:
{"ts": "11:40:02", "level": "ERROR", "msg": "grade service timeout", "route": "/results/3A", "status": 504, "ms": 1000, "trace_id": "a3ce929d"}. рдкреНрд░рддреНрдпреЗрдХ field filter, рдореЛрдЬрддрд╛ рдЖрдгрд┐ group рдХрд░рддрд╛ рдпреЗрддреЗ. - Log levels тАФ рдШрдЯрдирд╛ рдХрд┐рддреА рдЧрдВрднреАрд░ рдЖрд╣реЗ. рд╕рд░реНрд╡рд╛рдд рдХрдореА рддреЗ рд╕рд░реНрд╡рд╛рдд рдЬрд╛рд╕реНрдд рдЧрдВрднреАрд░:
- DEBUG тАФ developers рд╕рд╛рдареА рддрдкрд╢реАрд▓; production рдордзреНрдпреЗ рд╕рд╣рд╕рд╛ рдмрдВрдж;
- INFO тАФ рдареЗрд╡рдгреНрдпрд╛рдЬреЛрдЧреНрдпрд╛ рд╕рд╛рдорд╛рдиреНрдп рдШрдЯрдирд╛ ("results viewed");
- WARN / WARNING тАФ рдХрд╛рд╣реАрддрд░реА рд╡рд┐рдЪрд┐рддреНрд░ рдЬреЗ system рдиреЗ рд╣рд╛рддрд╛рд│рд▓реЗ ("slow grade service");
- ERROR тАФ рдПрдЦрд╛рджреА рдХреНрд░рд┐рдпрд╛ рдЕрдпрд╢рд╕реНрд╡реА рдЭрд╛рд▓реА ("grade service timeout", рдкрд╛рд▓рдХрд╛рд▓рд╛ 504);
- FATAL / CRITICAL тАФ process рдкреБрдвреЗ рдЪрд╛рд▓реВ рд╢рдХрдд рдирд╛рд╣реА.
рдЕрдиреЗрдХ tools рдордзреНрдпреЗ DEBUG рдЪреНрдпрд╛ рдЦрд╛рд▓реА TRACE рд╕реБрджреНрдзрд╛ рдЕрд╕рддреЛ. Python рдЪреЗ
loggingDEBUG, INFO, WARNING, ERROR, CRITICAL рд╡рд╛рдкрд░рддреЗ. рддреБрдореНрд╣реА рдПрдХ рдХрд┐рдорд╛рди level рдард░рд╡рддрд╛: WARN рдареЗрд╡рд▓рд╛ рддрд░ INFO рдЖрдгрд┐ DEBUG рдУрд│реА рдЯрд╛рдХрд▓реНрдпрд╛ рдЬрд╛рддрд╛рдд.
- Correlation id / trace id тАФ рдкреНрд░рддреНрдпреЗрдХ request рд╕рд╛рдареА рдПрдХ id, рдЬреЛ рддреА request рдирд┐рд░реНрдорд╛рдг рдХрд░рддреЗ рддреНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ рдУрд│реАрдд, рдкреНрд░рддреНрдпреЗрдХ service рдордзреНрдпреЗ рд▓рд┐рд╣рд┐рд▓рд╛ рдЬрд╛рддреЛ. рддреЛ рдПрдХрд╛ request рдЪреЗ logs рдПрдХрддреНрд░ рдЬреЛрдбрддреЛ, рдЖрдгрд┐ logs рдирд╛ trace рд╢реА рдЬреЛрдбрддреЛ (рдзрдбрд╛ 05). (Page рд╡рд░ рдмрд╕рд╛рд╡реЗрдд рдореНрд╣рдгреВрди lab 8 рдЕрдХреНрд╖рд░рд╛рдВрдЪреЗ рдЫреЛрдЯреЗ ids рд╡рд╛рдкрд░рддреЗ; рдЦрд░рд╛ W3C trace id 32 hex рдЕрдХреНрд╖рд░рд╛рдВрдЪрд╛ рдЕрд╕рддреЛ.)
- рдЧреБрдкрд┐рддреЗ рдХрд┐рдВрд╡рд╛ рд╡реИрдпрдХреНрддрд┐рдХ рдорд╛рд╣рд┐рддреА (PII) рдХрдзреАрдЪ log рдХрд░реВ рдирдХрд╛ тАФ passwords, tokens, API keys, session
cookies, card numbers рдХрд┐рдВрд╡рд╛ рдкреВрд░реНрдг рд╡реИрдпрдХреНрддрд┐рдХ рдорд╛рд╣рд┐рддреА (рдкрддреНрддреНрдпрд╛рд╕рд╣ рдирд╛рд╡реЗ, emails, phone numbers,
рдЖрд░реЛрдЧреНрдпрд╛рдЪреЗ рддрдкрд╢реАрд▓) рдирдХреЛ. Logs рдЕрдиреЗрдХ рдард┐рдХрд╛рдгреА copy рд╣реЛрддрд╛рдд рдЖрдгрд┐ рдЕрдиреЗрдХ рд▓реЛрдХ рддреЗ рд╡рд╛рдЪрддрд╛рдд. рд╡реНрдпрдХреНрддреА рдирд╡реНрд╣реЗ, рдПрдХ id
log рдХрд░рд╛ (
user: parent-9), рдЖрдгрд┐ logger рдордзреНрдпреЗрдЪ redact рдХрд░рд╛ рдореНрд╣рдгрдЬреЗ рдПрдЦрд╛рджреА рдирд┐рд╖реНрдХрд╛рд│рдЬреА рдУрд│ рдорд╛рд╣рд┐рддреА рдмрд╛рд╣реЗрд░ рдЯрд╛рдХреВ рд╢рдХрдгрд╛рд░ рдирд╛рд╣реА. - Retention тАФ logs рдХрд┐рддреА рдХрд╛рд│ рдареЗрд╡рд▓реЗ рдЬрд╛рддрд╛рдд. рдкреНрд░рддрд┐ byte logs рд╣рд╛ рд╕рд░реНрд╡рд╛рдд рдорд╣рд╛рдЧ signal рдЖрд╣реЗ, рдореНрд╣рдгреВрди DEBUG рдХрдореА рдХрд╛рд│ рдареЗрд╡рд╛ (рдХрд┐рдВрд╡рд╛ рдмрдВрдж) рдЖрдгрд┐ ERROR рдЬрд╛рд╕реНрдд рдХрд╛рд│.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг outage рджрд░рдореНрдпрд╛рди рдкреНрд░рд╢реНрди рдиреЗрд╣рдореА рдирд╡рд╛ рдЕрд╕рддреЛ. "11:30 рдкрд╛рд╕реВрди /results/3A рд╡рд░ рдХрд┐рддреА 504s?"
"рдХреЛрдгрддреЗ trace ids?" "рдлрдХреНрдд 3A рд╡рд░реНрдЧ, рдХреА рд╕рдЧрд│реЗ рд╡рд░реНрдЧ?" рдореЛрдХрд│реНрдпрд╛ рдордЬрдХреБрд░рд╛рдд рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рд╢реНрди рдореНрд╣рдгрдЬреЗ
рдирд╡реЗ regular expression рдЖрдгрд┐ рд╣рд│реВ scan. Structured logs рдордзреНрдпреЗ рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рд╢реНрди рдореНрд╣рдгрдЬреЗ рдПрдХрд╛ field рд╡рд░рдЪрд╛
filter. рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ рдУрд│реАрдд trace id рдЕрд╕реЗрд▓ рддрд░ рдПрдХ рдЕрдпрд╢рд╕реНрд╡реА request рдХрд╛рд╣реА рд╕реЗрдХрдВрджрд╛рдВрдд рдкреНрд░рддреНрдпреЗрдХ
service рдордзреВрди follow рдХрд░рддрд╛ рдпреЗрддреЗ.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
obs/signals.py рдордзреАрд▓ log(ts, level, msg, **fields) рдПрдХ JSON
рдУрд│ рдкрд░рдд рдХрд░рддреЗ, рдЬреНрдпрд╛рдд рдЖрдзреА ts, level рдЖрдгрд┐ msg, рдордЧ рддреБрдордЪреА fields рдЕрд╕рддрд╛рдд. query(lines, **match) рдкреНрд░рддреНрдпреЗрдХ
рдУрд│ parse рдХрд░рддреЗ рдЖрдгрд┐ рдЬреНрдпрд╛рдд рдкреНрд░рддреНрдпреЗрдХ field рдЬреБрд│рддреЗ рддреНрдпрд╛ рдареЗрд╡рддреЗ тАФ log search рдЪреА рдПрдХ рдЫреЛрдЯреА рдЖрд╡реГрддреНрддреА.
obs/demo.py рдордзреАрд▓ logs() 11:40 рдкрд╛рд╕реВрдирдЪреНрдпрд╛ рдЪрд╛рд░ рдУрд│реА рд▓рд┐рд╣рд┐рддреЗ рдЖрдгрд┐
route=/results/3A рд╡рд░ level=ERROR рдорд╛рдЧрддреЗ.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
рдПрдХ field (cls) рдЬреЛрдбрд╛, рдПрдХ WARN рдУрд│ рдЬреЛрдбрд╛, рдордЧ рдореЛрдХрд│рд╛ рдордЬрдХреВрд░ рдкрдЯрдХрди рдЙрддреНрддрд░ рджреЗрдК рд╢рдХрд▓рд╛ рдирд╕рддрд╛ рдЕрд╢рд╛
fields рд╡рд░ query рдХрд░рд╛:
python3 obs/demo.py logs
python3 - <<'EOF'
import sys, json; sys.path.insert(0, "obs"); from signals import log, query
lines = [log("11:40:01", "INFO", "results viewed", route="/results/3A", status=200, ms=92, trace_id="4bf92f35", cls="3A"),
log("11:40:02", "ERROR", "grade service timeout", route="/results/3A", status=504, ms=1000, trace_id="a3ce929d", cls="3A"),
log("11:40:02", "WARN", "slow grade service", route="/results/3B", status=200, ms=870, trace_id="00f067aa", cls="3B"),
log("11:40:03", "ERROR", "grade service timeout", route="/results/3A", status=504, ms=1000, trace_id="b7ad6b71", cls="3A")]
print(lines[2])
print("status=504 тЖТ", len(query(lines, status=504)), "lines")
print("cls=3B тЖТ", [d["msg"] for d in query(lines, cls="3B")])
print("trace_id=a3ce929d тЖТ", [(d["ts"], d["level"], d["msg"]) for d in query(lines, trace_id="a3ce929d")])
order = ["DEBUG", "INFO", "WARN", "ERROR"]
for floor in ("INFO", "WARN", "ERROR"):
kept = [l for l in lines if order.index(json.loads(l)["level"]) >= order.index(floor)]
print(f"keep level тЙе {floor:<5} тЖТ {len(kept)} of {len(lines)} lines")
EOF
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
logs рд╣реЗ print рдХрд░рддреЗ:
тФАтФА structured logs: one JSON object per line, not free text
{"ts": "11:40:02", "level": "ERROR", "msg": "grade service timeout", "route": "/results/3A", "status": 504, "ms": 1000, "trace_id": "a3ce929d", "user": "parent-9"}
тФАтФА query level=ERROR route=/results/3A тЖТ 2 lines ┬╖ trace ids ['a3ce929d', 'b7ad6b71']
levels: DEBUG < INFO < WARN < ERROR ┬╖ never log passwords, tokens or full personal data
a request id / trace id in EVERY line is what joins logs to traces
рддреБрдордЪрд╛ snippet рд╣реЗ print рдХрд░рддреЛ:
{"ts": "11:40:02", "level": "WARN", "msg": "slow grade service", "route": "/results/3B", "status": 200, "ms": 870, "trace_id": "00f067aa", "cls": "3B"}
status=504 тЖТ 2 lines
cls=3B тЖТ ['slow grade service']
trace_id=a3ce929d тЖТ [('11:40:02', 'ERROR', 'grade service timeout')]
keep level тЙе INFO тЖТ 4 of 4 lines
keep level тЙе WARN тЖТ 3 of 4 lines
keep level тЙе ERROR тЖТ 2 of 4 lines
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
рдирд╡реЗ field (cls) рд▓рдЧреЗрдЪ рд╡рд┐рдЪрд╛рд░рддрд╛ рдпреЗрдгрд╛рд░рд╛ рдирд╡рд╛ рдкреНрд░рд╢реНрди рдмрдирд▓реЗ тАФ рдХреЛрдгрддреЗрд╣реА regular expression рдирд╛рд╣реА.
Trace id рдиреЗ рдиреЗрдордХреА рдПрдХ request рд╡реЗрдЧрд│реА рдХрд╛рдврд▓реА. рдЖрдгрд┐ рдХрд┐рдорд╛рди level рдард░рд╡рддреЛ рдХреА рддреБрдореНрд╣реА рдХрд┐рддреА
рдареЗрд╡рддрд╛: ERROR рд▓рд╛ рддреБрдореНрд╣реА рдЕрд░реНрдзреНрдпрд╛ рдУрд│реА рд╕рд╛рдард╡рддрд╛, рдкрдг grade service рд╣рд│реВ рд╣реЛрдд рдЪрд╛рд▓рд▓реА рдЖрд╣реЗ рд╣реЗ рд╕рд╛рдВрдЧрдгрд╛рд░реА
WARN рдУрд│ рдЧрдорд╛рд╡рддрд╛ тАФ рдореНрд╣рдгрдЬреЗ рдкрд╣рд┐рд▓реА рдЦреВрдг.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- рдорд╣рддреНрддреНрд╡рд╛рдЪреНрдпрд╛ рдХрд┐рдорддреА рд╡рд╛рдХреНрдпрд╛рдЪреНрдпрд╛ рдЖрдд рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдореЛрдХрд│реНрдпрд╛ рдордЬрдХреБрд░рд╛рдЪреНрдпрд╛ рдУрд│реА (
"timeout for 3A after 1000ms") - рдкреНрд░рддреНрдпреЗрдХ service рдордзреНрдпреЗ рд╡реЗрдЧрд│реЗ field name (
userId,user_id,uid) тАФ рдирд╛рд╡рд╛рдВрд╡рд░ рдПрдХрдордд рдХрд░рд╛ (рдзрдбрд╛ 06) - trace id рдирд╛рд╣реА, рддреНрдпрд╛рдореБрд│реЗ рдПрдХрд╛ request рдЪреНрдпрд╛ рдУрд│реА services рдкрд▓реАрдХрдбреЗ рдЬреЛрдбрддрд╛ рдпреЗрдд рдирд╛рд╣реАрдд
- рдЧреБрдкрд┐рддреЗ рдХрд┐рдВрд╡рд╛ рд╡реИрдпрдХреНрддрд┐рдХ рдорд╛рд╣рд┐рддреА log рдХрд░рдгреЗ: headers рдордзрд▓реЗ tokens, failed-login рдУрд│реАрддрд▓реЗ passwords, рдкреВрд░реНрдг request bodies
- production рдордзреНрдпреЗ "рдлрдХреНрдд рдЖрддреНрддрд╛рдкреБрд░рддрд╛" DEBUG рдЪрд╛рд▓реВ тАФ bug рд╕рд╛рдкрдбрдгреНрдпрд╛рдЖрдзреАрдЪ bill рдпреЗрддреЗ
- loop рдЪреНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ рдкрд╛рдпрд░реАрд╕рд╛рдареА рдПрдХ log рдУрд│: рдШрдЯрдирд╛ log рдХрд░рд╛, рдЖрдгрд┐ рдкреБрдиреНрд╣рд╛ рдкреБрдиреНрд╣рд╛ рд╣реЛрдгрд╛рд▒реНрдпрд╛ рдЧреЛрд╖реНрдЯреА metric рдиреЗ рдореЛрдЬрд╛ (рдзрдбрд╛ 03)
- error-ratio metric рдРрд╡рдЬреА log рдордЬрдХреБрд░рд╛рд╡рд░ alert рдХрд░рдгреЗ ("ERROR рджрд┐рд╕рддреЗ") (рдзрдбрд╛ 08)
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд
On a real account тАФ Python рдордзреВрди structlog рд╡рд╛рдкрд░реВрди JSON logs, redaction рдЪреНрдпрд╛ рдкрд╛рдпрд░реАрд╕рд╣ рдЖрдгрд┐ рд╕рдзреНрдпрд╛рдЪрд╛ OpenTelemetry trace id рдкреНрд░рддреНрдпреЗрдХ рдУрд│реАрдд рдЬреЛрдбреВрди:
import structlog
from opentelemetry import trace
SECRET_KEYS = {"password", "token", "authorization", "card_number"}
def redact(logger, method, event):
for k in SECRET_KEYS & event.keys():
event[k] = "[REDACTED]"
return event
def add_trace_id(logger, method, event):
ctx = trace.get_current_span().get_span_context()
if ctx.is_valid:
event["trace_id"] = format(ctx.trace_id, "032x") # 32 hex characters
event["span_id"] = format(ctx.span_id, "016x") # 16 hex characters
return event
structlog.configure(processors=[
structlog.contextvars.merge_contextvars,
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso", utc=True),
add_trace_id,
redact,
structlog.processors.JSONRenderer(),
])
log = structlog.get_logger(service="results-api")
log.error("grade service timeout", route="/results/3A", status=504, duration_ms=1000, user="parent-9")
рдлрдХреНрдд standard logging module рд╡рд╛рдкрд░реВрди, рдПрдХ рдЫреЛрдЯрд╛ JSON formatter рддреЗрдЪ рдХрд╛рдо рдХрд░рддреЛ:
import json, logging
class JsonFormatter(logging.Formatter):
def format(self, record):
return json.dumps({"ts": self.formatTime(record), "level": record.levelname,
"msg": record.getMessage(), **getattr(record, "fields", {})})
handler = logging.StreamHandler(); handler.setFormatter(JsonFormatter())
logging.basicConfig(level=logging.INFO, handlers=[handler])
logging.getLogger("results-api").warning("slow grade service", extra={"fields": {"route": "/results/3B", "ms": 870}})
CloudWatch Logs Insights рдордзреНрдпреЗ рддреЛрдЪ search:
fields @timestamp, level, msg, route, trace_id
| filter level = "ERROR" and route = "/results/3A"
| stats count(*) as errors by bin(1m)
Grafana Loki (LogQL) рдЖрдгрд┐ Datadog Logs рдордзреНрдпреЗ:
{service="results-api"} | json | level="ERROR" | route="/results/3A"
service:results-api status:error @route:"/results/3A"
ЁЯПн Production рдордзреНрдпреЗ рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рд╕рд╛рдорд╛рдпрд┐рдХ field names (
service,trace_id,route,status,duration_ms) рдПрдХрджрд╛рдЪ, рд╕рдЧрд│реНрдпрд╛ teams рд╕рд╛рдареА рдард░рд╡рд╛, рдЖрдгрд┐ redaction рд╕рд╛рдорд╛рдпрд┐рдХ logger рдордзреНрдпреЗ рдареЗрд╡рд╛ тАФ рдкреНрд░рддреНрдпреЗрдХ developer рдЪреНрдпрд╛ рдЖрдард╡рдгреАрд╡рд░ рд╕реЛрдбреВ рдирдХрд╛.
тПня╕П рдкреБрдвреЗ
рджреИрдирдВрджрд┐рдиреА рдПрдХрд╛ рднреЗрдЯреАрдЪреА рдЧреЛрд╖реНрдЯ рд╕рд╛рдВрдЧрддреЗ. рд╡реЗрд│реЗрдиреБрд╕рд╛рд░ рдХрд┐рддреА рдЖрдгрд┐ рдХрд┐рддреА рдЬрд▓рдж рд╣реЗ рдкрд╛рд╣рдгреНрдпрд╛рд╕рд╛рдареА рдХрддрд░рд┐рдирд╛рд▓рд╛ рдПрдХ рддрдХреНрддрд╛ рд▓рд╛рдЧрддреЛ.
git checkout lesson-03-metrics