๐ŸŒ ํ•™์Šต๋…ธํŠธ/๋‚™์„œ์žฅ

[๋ชจ๋‹ˆํ„ฐ๋ง์˜ ์ƒˆ๋กœ์šด ๋ฏธ๋ž˜ ๊ด€์ธก๊ฐ€๋Šฅ์„ฑ #5] 6์žฅ. ๊ด€์ธก๊ฐ€๋Šฅ์„ฑ์˜ ํ‘œ์ค€, ์˜คํ”ˆํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ

mini_world 2024. 4. 14. 16:43

๐Ÿ“Œ ์ด ๋‚ด์šฉ์€ ์ฑ… ๋‚ด์šฉ ๋ฉ”๋ชจ์ž…๋‹ˆ๋‹ค.
๊ฐœ์ธ์ ์ธ ์ƒ๊ฐ๊ณผ ๊ฒฝํ—˜ ๊ทธ๋ฆฌ๊ณ  ์—ฌ๋Ÿฌ ์žก๋‹ด์ด ์žˆ์œผ๋‹ˆ, ์ฑ…์„ ์ฝ์œผ๋ฉฐ ์˜๊ฒฌ์„ ๋‚˜๋ˆ„๊ณ ์‹ถ์€๋ถ„์ด ๋ด์ฃผ์‹œ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

6.1 ์˜คํ”ˆํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์†Œ๊ฐœ

OpenTelemetry๋Š” ํ•œ๋งˆ๋””๋กœ ์ •๋ฆฌํ•˜์ž๋ฉด ๋กœ๊ทธ, ๋ฉ”ํŠธ๋ฆญ, ์ถ”์ ์„ ํ•œ๋ฒˆ์— ์ˆ˜์ง‘ํ•˜๋Š” ์—์ด์ „ํŠธ ์—ญํ• ์„ ํ•œ๋‹ค๊ณ  ๋งํ•  ์ˆ˜ ์žˆ๋‹ค.

์—ญ์‚ฌ

OpenTelemetry๋Š” Cloud Native Computing Foundation (CNCF) ํ”„๋กœ์ ํŠธ๋กœ, ์ด์ „์˜ ๋‘ ํ”„๋กœ์ ํŠธ์ธ OpenTracing๊ณผ OpenCensus๊ฐ€ ํ•ฉ๋ณ‘ํ•˜์—ฌ ๋งŒ๋“ค์–ด์กŒ๋‹ค.

OpenTracing, OpenCensus๋Š” ์ฝ”๋“œ๋ฅผ ๊ณ„์ธกํ•˜๊ณ  ๊ด€์ธก์„ฑ ๋ฐฑ์—”๋“œ๋กœ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋Š” ํ‘œ์ค€์ด ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‘ ํ”„๋กœ์ ํŠธ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ํ†ตํ•ฉํ•˜๊ณ  ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ์˜ ์ƒ์„ฑ,์ˆ˜์ง‘,์ „์†ก ๋ฐฉ์‹์„ ํ‘œ์ค€ํ™”ํ•˜๋Š”๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•˜๊ณ ์žˆ๋‹ค.

๐Ÿ‘‰ OpenTelemetry Mission ๋ณด๋Ÿฌ๊ฐ€๊ธฐ

์•„ํ‚คํ…์ณ

https://opentelemetry.io/docs/

OpenTelemetry๋Š” ์—…๊ณ„ํ‘œ์ค€์œผ๋กœ, CNCF๋‚ด์—์„œ๋„ ํฐ ์˜ํ–ฅ๋ ฅ์„ ๋ฏธ์น˜๊ณ  ์žˆ๋‹ค. (OpenTelemetry Venders)

OpenTelemetry๋Š” ๋ฐฑ์—”๋“œ ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๊ด€์ธก ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ๋‹ค์–‘ํ•œ ๋ฐฑ์—”๋“œ ์‹œ์Šคํ…œ์œผ๋กœ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ํ‘œ์ค€์„ ์ œ๊ณตํ•œ๋‹ค.

๋ชฉํ‘œ

OpenTelemetry๋Š” ์ถ”์ ,์ง€ํ‘œ, ๋กœ๊ทธ ์ƒ๊ด€ ๊ด€๊ณ„๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š”๊ฒƒ์ด ์ฃผ ๋ชฉํ‘œ๋‹ค.

https://opentelemetry.io/docs/specs/otel/logs/

6.2 ์˜คํ”ˆํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์ปดํฌ๋„ŒํŠธ

์˜คํ”ˆํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ์˜ ํ•ต์‹ฌ ๊ฐœ๋…์€ Signals(์‹ ํ˜ธ), Context Propagation(์ปจํ…์ŠคํŠธ์ „ํŒŒ) ๊ทธ๋ฆฌ๊ณ  Pipeline(ํŒŒ์ดํ”„๋ผ์ธ)์ด๋‹ค.

OpenTelemetry
โ”œโ”€โ”€ Signals
โ”‚   โ”œโ”€โ”€ Trace
โ”‚   โ”œโ”€โ”€ Metrics
โ”‚   โ”œโ”€โ”€ Log
โ”‚   โ””โ”€โ”€ Baggage
โ”œโ”€โ”€ Context Propagation 
โ””โ”€โ”€ Pipeline
  • Signals (์‹ ํ˜ธ): ์ถ”์ , ๋ฉ”ํŠธ๋ฆญ, ๋กœ๊ทธ, ๋ฐฐ๊ธฐ์ง€ ๋“ฑ์˜ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ ์œ ํ˜•์— ๋Œ€ํ•œ ๊ธฐ์ˆ ์  ์ŠคํŽ™์„ ์ •์˜ํ•œ๋‹ค.
    ์‹ ํ˜ธ๋Š” ์ถ”์ ,๋ฉ”ํŠธ๋ฆญ,๋กœ๊ทธ,๋ฐฐ๊ธฐ์ง€๋ฅผ ํฌ๊ด„ํ•˜๋Š” ๊ฐœ๋…์ด๋‹ค.
    • **Signals์—์„œ ์ •์˜ํ•˜๋Š”๊ฒƒ**
      • ์‹ ํ˜ธ์— ๋Œ€ํ•œ ๊ฐ€์ด๋“œ๋ผ์ธ์„ ์ œ๊ณตํ•˜๋Š” ์ŠคํŒฉ
      • ์‹ ํ˜ธ ํ‘œํ˜„๋ฐฉ์‹์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฐ์ดํ„ฐ ๋ชจ๋ธ
      • ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฝ”๋“œ๋ฅผ ๊ณ„์ธกํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” API
      • ์‚ฌ์šฉ์ž๊ฐ€ API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ SDK
      • ์‚ฌ์šฉ์„ ๋‹จ์ˆœํ™” ํ•˜๋Š” ๊ณ„์ธก ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
      • ๊ณ„์ธก์„ ๋‹จ์ˆœํ™” ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์—์ด์ „ํŠธ
    • **Signals์— ํฌํ•จ๋˜๋Š”๊ฒƒ**
      • Trace (์ถ”์ ): ์‹œ์Šคํ…œ ์š”์ฒญ ๋ฐ ํŠธ๋žœ์žญ์…˜์˜ ์ด๋™ ์ •๋ณด
      • Metrics (๋ฉ”ํŠธ๋ฆญ): ์‹œ์Šคํ…œ ์ˆ˜์น˜์  ์„ฑ๋Šฅ ์ง€ํ‘œ
      • Log (๋กœ๊ทธ): ์ด๋ฒคํŠธ ๋ฉ”์‹œ์ง€
      • Baggage (๋ฐฐ๊ธฐ์ง€): ์„œ๋น„์Šค ๊ฐ„ ์š”์ฒญ๊ณผ ํ•จ๊ป˜ ์ „๋‹ฌ๋˜๋Š” ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ปจํ…์ŠคํŠธ ์ •๋ณด
  • Context Propagation (์ฝ˜ํ…์ŠคํŠธ ์ „ํŒŒ): ์„œ๋น„์Šค๋‚˜ ์š”์ฒญ ๊ฐ„์— ์ปจํ…์ŠคํŠธ ์ •๋ณด(์˜ˆ: ๋ณด์•ˆ ํ† ํฐ, ์‚ฌ์šฉ์ž ์„ธ์…˜ ์‹๋ณ„์ž ๋“ฑ)๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ •์˜ํ•œ๋‹ค. ์—…์ŠคํŠธ๋ฆผ๊ณผ ๋‹ค์šด์ŠคํŠธ๋ฆผ ์„œ๋น„์Šค ๊ฐ„์— ์ •๋ณด๊ฐ€ ์ผ๊ด€๋˜๊ฒŒ ์œ ์ง€๋˜๋„๋ก ๋ณด์žฅํ•œ๋‹ค.
  • Pipeline (ํŒŒ์ดํ”„๋ผ์ธ): ์ˆ˜์ง‘๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ ์ ˆํ•œ ๋ฐฑ์—”๋“œ ์‹œ์Šคํ…œ(์˜ˆ: ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ, ๋ถ„์„ ํ”Œ๋žซํผ ๋“ฑ)์œผ๋กœ ์ „์†กํ•˜๋Š” ๊ณผ์ •์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ •์˜ํ•œ๋‹ค.

6.2.1 Signals(์‹ ํ˜ธ) ๊ตฌ์„ฑ์š”์†Œ

[1] ๋ฐ์ดํ„ฐ ๋ชจ๋ธ

๋ฐ์ดํ„ฐ ๋ชจ๋ธ์€ ๋ฉ”ํŠธ๋ฆญ, ์ถ”์ , ๋กœ๊ทธ์— ๋Œ€ํ•œ ์ƒ์„ธํ•œ ๋ ˆ์ด์•„์›ƒ๊ณผ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ๊ธฐ์ˆ ํ•œ๋‹ค. (OpenTelemetry Spec)
OpenTelemetry ๋ฐ์ดํ„ฐ ๋ชจ๋ธ์€ ๋ฉ”ํŠธ๋ฆญ,์ถ”์ ,๋กœ๊ทธ๊ฐ„์˜ ์ƒ๊ด€๊ด€๊ณ„๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ํ‘œ์ค€ํ™” ๋ฉ”์„ธ์ง€์™€ ๋ฐ์ดํ„ฐ ํ˜•์‹์ด๋ฉฐ, ๋ฒค๋” ์ค‘๋ฆฝ์ ์œผ๋กœ ์„ค๊ณ„๋˜์—ˆ๋‹ค. gRPC ํ”„๋กœํ† ์ฝœ ๋ฒ„ํผ์™€ ํ˜ธํ™˜์„ฑ์—๋„ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค.

  • Metrics: https://opentelemetry.io/docs/specs/otel/overview/#metric-signal
    • ์ž˜ ๋งŒ๋“ค์–ด์ง„ ๋ฉ”ํŠธ๋ฆญ ์†”๋ฃจ์…˜(์˜ˆ,ํ”„๋กœ๋ฉ”ํ…Œ์šฐ์Šค)์ด ๋งŽ์ด ์žˆ๋‹ค๋Š” ์ ์„ ๊ฐ์•ˆํ•˜์—ฌ, Opentelemetry๋Š” ๋ฉ”ํŠธ๋ฆญ์„ ๊ธฐ์กด ์†”๋ฃจ์…˜์— ํ†ตํ•ฉํ•˜๋Š”๊ฒƒ์— ์ค‘์ ์„ ๋‘๊ณ  ์žˆ๋‹ค.
    • prometheus, StatsD์— ์™„์ „ํžˆ ํ˜ธํ™˜๋œ๋‹ค. (์ฐธ๊ณ )
  • Logs: https://opentelemetry.io/docs/specs/otel/overview/#log-signal
    • ๋กœ๊ทธ๋Š” ๊ธฐ์กด ๋กœ๊ทธ ์‹œ์Šคํ…œ๊ณผ์˜ ํ˜ธํ™˜์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์ถ”์  ์ •๋ณด์™€ ๊ฐ™์€ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„ํ–ˆ๋‹ค.
    • ๊ธฐ์กด ๋กœ๊ทธ ๋ฉ”์„ธ์ง€ ํ˜•์‹์— ์ถ”์  ๋“ฑ์˜ ์ƒ๊ด€๊ด€๊ณ„ ์ •๋ณด์™€ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š”๊ฒƒ์ด ์ผ๋ฐ˜์ ์ธ ์˜คํ”ˆํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ์˜ ๋ฐฉํ–ฅ์„ฑ์ด๋‹ค.
  • Trace : https://opentelemetry.io/docs/specs/otel/overview/#tracing-signal
    • Trace๋Š” ๊ธฐ์กด์˜ ๋‹ค์–‘ํ•œ ์ถ”์  ํ‘œ์ค€์„ ์œ ์ง€ํ•˜๋Š”๊ฒƒ๋ณด๋‹ค๋Š” ์ƒˆ๋กœ์šด ์˜คํ”ˆํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์ถ”์ ์œผ๋กœ ํ†ตํ•ฉํ•˜๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ๋‹ค.
    • Trace์˜ ํ•ต์‹ฌ์€ Spen์ด๋ฉฐ, Spen๊ฐ„์˜ ์ƒ๊ด€๊ด€๊ณ„๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š”๊ฒƒ์ด ํ•ต์‹ฌ์ด๋‹ค.
  • Baggage: https://opentelemetry.io/docs/specs/otel/overview/#baggage-signal
    • ์š”์ฒญ์„ ๋”ฐ๋ผ ์ด๋™ํ•˜๋Š” ํ‚ค-๊ฐ’ ์Œ์˜ ์ง‘ํ•ฉ์„ ์˜๋ฏธํ•˜๋ฉฐ, Baggage๋ฅผ ํ†ตํ•ด์„œ๋น„์Šค๊ฐ„์˜ ์ƒํƒœ์™€ ์ปจํ…์ŠคํŠธ ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์œ ์ง€ํ•œ๋‹ค.
    • ์›๊ฒฉ ๋ถ„์„์— ์ฃผ์„์„ ๋‹ฌ๊ณ  ๋ฉ”ํŠธ๋ฆญ, ์ถ”์  ๋ฐ ๋กœ๊ทธ์— ์ปจํ…์ŠคํŠธ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋œ๋‹ค.

๊ฐ ์‹ ํ˜ธ๋Š” 4๊ฐ€์ง€ ์ฝ”์–ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ํฌํ•จํ•˜๊ณ ์žˆ๋‹ค.

  • APIs: ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ(์ถ”์ , ๋ฉ”ํŠธ๋ฆญ, ๋กœ๊ทธ)๋ฅผ ์ˆ˜์ง‘ํ•˜๊ธฐ ์œ„ํ•œ ์ผ๋ จ์˜ ๊ทœ์น™๊ณผ ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณตํ•จ
  • SDKs: OpenTelemetry ํ”„๋กœ์ ํŠธ์— ์˜ํ•ด ์ œ๊ณต๋˜๋Š” API์˜ ๊ตฌํ˜„์ฒด, API์— ์ •์˜๋œ ๋ช…์„ธ๋ฅผ ์‹ค์ œ๋กœ ์‹คํ–‰ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ํฌํ•จํ•จ (SDK๋Š” API์— ์˜์กด์ )
  • OpenTelemetry Protocol (OTLP)
  • Collector

[2] API

- ์ฐธ๊ณ ๋งํฌ (OpenTelemetry API)

API์™€ SDK๊ฐ€ ์ดํ•ด๋˜์ง€ ์•Š์•„์„œ, ๋งค์šฐ ํ˜ผ๋ž€์Šค๋Ÿฌ์› ๋Š”๋ฐ, ์ฝ”๋“œ ์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด ์กฐ๊ธˆ์ด๋‚˜๋งˆ(?) ์ดํ•ด๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

API๋Š” ์ถ”์ ,๋ฉ”ํŠธ๋ฆญ,๋กœ๊ทธ ๋“ฑ์˜ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘์„ ์œ„ํ•œ ๊ทœ๊ฒฉ(๋ช…์„ธ)๋ฅผ ์ •์˜ํ•œ๋‹ค.
์ฆ‰, API๋Š” ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ทœ์น™์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

# API๋งŒ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์ œ
# ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” SDK๊ฐ€ ํ•„์š”ํ•จ

from opentelemetry import trace

# API๋ฅผ ํ†ตํ•ด ํŠธ๋ ˆ์ด์„œ๋ฅผ ๊ฐ€์ ธ์˜ด
tracer = trace.get_tracer(__name__)

# API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ถ”์  ์‹œ์ž‘
with tracer.start_as_current_span("api-only-span"):
    print("This span is created using API only and won't be exported without an SDK.")

[3] SDK

SDK๋Š” API์ŠคํŽ™์„ ๊ฐ€์ง€๊ณ  ๊ฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋‚˜ ํ™˜๊ฒฝ์—์„œ ์‹ค์ œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
์ฆ‰, SDK๋Š” API๋ช…์„ธ์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘,์ฒ˜๋ฆฌ, ๋‚ด๋ณด๋‚ด๊ธฐ ๋“ฑ์˜ ๊ตฌ์ฒด์ ์ธ ์ฝ”๋“œ๋ฅผ ํฌํ•จํ•˜๊ฒŒ ๋œ๋‹ค.
์ •๋ฆฌํ•˜์ž๋ฉด, API๋Š” ์ด๋ก ์ ์ธ ์„ค๊ณ„๋„์ด๊ณ , SDK๋Š” ์„ค๊ณ„๋„๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์‹ค์ œ ๊ฑด๋ฌผ์„ ์ง“๋Š” ๊ฑด์ถ•์—…์ž๋ผ๊ณ  ๋น„์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.

# API์™€ SDK๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ ์˜ˆ์ œ (Console Spen Exporter SDK ์‚ฌ์šฉ)
# SDK๋Š” API๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋‚ด๋ณด๋‚ธ๋‹ค.

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

# SDK ์„ค์ •: ํŠธ๋ ˆ์ด์„œ ์ œ๊ณต์ž ์„ค์ •
trace.set_tracer_provider(TracerProvider())

# ์ฝ˜์†” ๋‚ด๋ณด๋‚ด๊ธฐ ์„ค์ •
span_processor = SimpleSpanProcessor(ConsoleSpanExporter())
trace.get_tracer_provider().add_span_processor(span_processor)

# API์™€ SDK๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ถ”์  ์‹œ์ž‘
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("api-and-sdk-span"):
    print("This span is created using both API and SDK and will be exported to the console.")

API์™€ SDK๋Š” ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ต๊ฒŒ ์™œ์ด๋ ‡๊ฒŒ ํ•ด๋†จ๋ƒ!!
์ด์œ ๋Š” ์œ ์—ฐ์„ฑ์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค. ๋™์ผํ•œ API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋‹ค์–‘ํ•œ SDK๋ฅผ ์„ ํƒํ•˜๊ฑฐ๋‚˜ ๊ต์ฒดํ•  ์ˆ˜ ์žˆ์–ด, ํŠน์ • ์‹คํ–‰ํ™˜๊ฒฝ ๋ฐ ์š”๊ตฌ์‚ฌํ•ญ์— ๋” ์ ํ•ฉํ•œ SDK๋ฅผ ์„ ํƒํ•˜๊ฑฐ๋‚˜ ๊ต์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค.
์ด ๊ตฌ์กฐ ๋•๋ถ„์— OpenTelemetry๋Š” ๋‹ค์–‘ํ•œ ํ”Œ๋žซํผ๊ณผ ์–ธ์–ด์—์„œ ๋„๋ฆฌ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋Š”๊ฒƒ์ด๋‹ค.

# ๊ฐ™์€ API๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ๋‹ค๋ฅธ SDK๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์ œ (OTLP Span Exporter SDK์‚ฌ์šฉ)

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.exporter.otlp.trace_exporter import OTLPSpanExporter

# SDK ์„ค์ •: ํŠธ๋ ˆ์ด์„œ ์ œ๊ณต์ž ์„ค์ •
trace.set_tracer_provider(TracerProvider())

# OTLP ๋‚ด๋ณด๋‚ด๊ธฐ ์„ค์ •
otlp_exporter = OTLPSpanExporter(endpoint="localhost:4317", insecure=True)
span_processor = SimpleSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)

# API์™€ SDK๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ถ”์  ์‹œ์ž‘
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("api-and-sdk-span-otlp"):
    print("This span is created using both API and SDK and will be exported via OTLP.")

[4] ๊ณ„์ธก๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (instrumentation libraries)

๊ณ„์ธก๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ž๋™๊ณ„์ธก๊ณผ ๊ฐ™์€ ์˜๋ฏธ๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์›น ํ”„๋ ˆ์ž„์›Œํฌ(์˜ˆ: Flask, Django, Spring), ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํด๋ผ์ด์–ธํŠธ(์˜ˆ: JDBC, Sequelize), RPC ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(์˜ˆ: gRPC, Thrift) ๋“ฑ ์— ๋Œ€ํ•œ ๊ณ„์ธก๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๊ณ , ๊ฐ ์–ธ์–ด๋ณ„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํ•ด๋‹น ์–ธ์–ด์˜ ํŠน์„ฑ๊ณผ ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์„ค๊ณ„๋˜์–ด์žˆ๋‹ค.
API๊ฐœ๋ฐœ ์—†์ด ์ž๋™์œผ๋กœ ๊ณ„์ธก์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ๋ฏธ๋ฆฌ ์„ค๊ณ„๋œ OpenTelemetry ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ๊ฒƒ์ด๋‹ค.

https://opentelemetry.io/ecosystem/registry/ ์ด ๋งํฌ์—์„œ ๊ณ„์ธก๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ๋‹ค.
(์ง„์ฒ™๋ฅ ์ด ๋‹ค ๋‹ค๋ฅด๋ฏ€๋กœ ์“ฐ๊ธฐ์ „์— ํ™•์ธํ•ด๋ด์•ผํ•จ)

์˜ˆ์‹œ๋กœ, FastAPI๋Š” opentelemetry-instrumentation-fastapi๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

# opentelemetry-instrumentation-fastapi ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์˜ˆ์ œ

from fastapi import FastAPI
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
from opentelemetry.exporter.otlp.trace_exporter import OTLPSpanExporter

app = FastAPI()

# ํŠธ๋ ˆ์ด์„œ ์ œ๊ณต์ž ์„ค์ •
trace.set_tracer_provider(TracerProvider())

# OTLP Exporter ์„ค์ •
otlp_exporter = OTLPSpanExporter(endpoint="localhost:4317", insecure=True)
span_processor = SimpleSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)

# FastAPI ์•ฑ์— ๋Œ€ํ•œ ๊ณ„์ธก ์„ค์ •
FastAPIInstrumentor.instrument_app(app)

@app.get("/")
async def hello():
    return {"message": "Hello from FastAPI with OpenTelemetry!"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8000)
# opentelemetry-instrumentation-fastapi ์—†์ด fastapi ๊ณ„์ธกํ•˜๊ธฐ
# ๋ฏธ๋“ค์›จ์–ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๊ฒฝ๋กœ์—ฐ์‚ฐ์ž์— ์ง์ ‘ ์ถ”์ ์ฝ”๋“œ ์ถ”๊ฐ€ (์ด ์˜ˆ์ œ์—์„œ๋Š” ๋ฏธ๋“ค์›จ์–ด ๊ธฐ๋Šฅ ์‚ฌ์šฉ)

from fastapi import FastAPI, Request
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.exporter.otlp.trace_exporter import OTLPSpanExporter

app = FastAPI()

# ํŠธ๋ ˆ์ด์„œ ์ œ๊ณต์ž ์„ค์ •
trace.set_tracer_provider(TracerProvider())

# OTLP Exporter ์„ค์ •
otlp_exporter = OTLPSpanExporter(endpoint="localhost:4317", insecure=True)
span_processor = SimpleSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)

# ํŠธ๋ ˆ์ด์„œ ๊ฐ€์ ธ์˜ค๊ธฐ
tracer = trace.get_tracer(__name__)

# ๋ฏธ๋“ค์›จ์–ด ์ถ”๊ฐ€
@app.middleware("http")
async def add_tracing(request: Request, call_next):
    with tracer.start_as_current_span(f"HTTP {request.method} {request.url.path}"):
        response = await call_next(request)
    return response

@app.get("/")
async def hello():
    return {"message": "Hello from FastAPI without automatic instrumentation!"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8000)

๊ทธ๋ƒฅ ๋ณด๊ธฐ์—๋Š” ๋น„์Šท๋น„์Šทํ•œ๋ฐ..
์ง์ ‘ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๊ตฌํ˜„ํ•˜๋Š”๊ฒƒ๋ณด๋‹ค opentelemetry-instrumentation-fastapi ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ๋ฌด์Šจ ์ด์ ์ด ์žˆ์„๊นŒ?

  • ๊ฐ„ํŽธํ•œ ํ†ตํ•ฉ: ์ˆ˜๋™์œผ๋กœ ๊ฐ ๊ฒฝ๋กœ๋‚˜ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์„ค์ •ํ•  ํ•„์š” ์—†์ด ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ํ•„์š” ์—†๊ณ , ๊ณ„์ธก๋กœ์ง์„ ์‹ ๊ฒฝ์“ธ ํ•„์š”๊ฐ€ ์—†๋‹ค.
  • ์ตœ์ ํ™”: ๊ณ„์ธก ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํ•ด๋‹น ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋‚ด๋ถ€ ๋™์ž‘์— ๋Œ€ํ•œ ๊นŠ์€ ์ดํ•ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๊ฐœ๋ฐœ๋œ๋‹ค. ์ง์ ‘ ๊ฐœ๋ฐœํ•˜๋Š”๊ฒƒ๋ณด๋‹ค ํšจ์œจ์ ์ด๊ณ  ์„ฑ๋Šฅ์— ๋œ ์˜ํ–ฅ์„ ๋ฏธ์นœ๋‹ค. ๋˜ํ•œ, ์—๋Ÿฌ ์ถ”์ , ๋น„๋™๊ธฐ ์ง€์› ๋“ฑ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ๋„ ๊ฐ„ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํ™œ๋ฐœํ•œ ์ปค๋ฎค๋‹ˆํ‹ฐ: ์œ ์ง€๋ณด์ˆ˜๋‚˜ ์—…๋ฐ์ดํŠธ ์ธก๋ฉด์—์„œ๋„ ์ข‹๋‹ค. ์ง€์†์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ ๋˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ ๋˜๊ธฐ๋•Œ๋ฌธ์ด๋‹ค. OpenTelemetry ์ž์ฒด์˜ ๋ณ€ํ™”์— ๋”ฐ๋ผ์„œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋„ ์—…๋ฐ์ดํŠธ ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

[5] ๋ฆฌ์†Œ์Šค (Resources)

- ์ฐธ๊ณ : OpenTelemetry Resources

ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ(์˜ˆ: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜, ์„œ๋ฒ„, ์„œ๋น„์Šค)๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋งํ•œ๋‹ค.
ํŠน์ • ์—”ํ‹ฐํ‹ฐ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜์—ฌ, ๋ฆฌ์†Œ์Šค๋ฅผ ์‰ฝ๊ฒŒ ์‹๋ณ„ํ•˜๊ณ , ๊ด€๋ฆฌํ•˜๋ฉฐ, ํ•„ํ„ฐ๋งํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค. ๋น„์œ ํ•˜์ž๋ฉด, AWS ์˜ Resource Tag, K8s์˜ Metadata ๊ฐ™์€ ์—ญํ• ์ด๋‹ค.

OpenTelemetry๋Š” ์ถ”์ ์„ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด TracerProvider, ๋ฉ”ํŠธ๋ฆญ์„ ์ดˆ๊ธฐํ™” ํ•˜๊ธฐ ์œ„ํ•ด MetricProvider๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
๊ฐ Provider ์ƒ์„ฑ ์‹œ์ ์— ๋ฆฌ์†Œ์Šค๋„ ์ƒ์„ฑํ•˜๋ฉฐ, Exporter์—์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ์ฐธ๊ณ ํ•œ๋‹ค.

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

# ๋ฆฌ์†Œ์Šค ์ƒ์„ฑ
resource = Resource.create({
    "service.name": "ExampleService",
    "service.version": "1.0.0",
    "host.name": "example-host"
})

# ํŠธ๋ ˆ์ด์„œ ์ œ๊ณต์ž์— ๋ฆฌ์†Œ์Šค ์„ค์ •
trace.set_tracer_provider(TracerProvider(resource=resource))

# ์ถ”์  ์„ค์ •
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("example-operation"):
    print("This operation is associated with a resource.")

[5-์ถ”๊ฐ€] ์–ดํŠธ๋ฆฌ๋ทฐํŠธ (attributes)

์ฝ”๋“œ๋ฅผ ํ™•์ธํ•˜๋‹ค๋ณด๋‹ˆ, ํƒœ๊ทธ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๊ฒŒ ๋ฆฌ์†Œ์Šค ์™ธ์—๋„ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ๋ผ๋Š”๊ฒŒ ์žˆ๋Š”๊ฑธ ์•Œ๊ฒŒ๋˜์—ˆ๋‹ค.
๋ฌด์—‡์ด ๋‹ค๋ฅธ๊ฑด์ง€ ํ™•์ธํ•˜๊ณ  ๋„˜์–ด๊ฐ€๋ณด์ž

  • ์–ดํŠธ๋ฆฌ๋ทฐํŠธ(Attributes)
    • ๋ชฉ์ : ์–ดํŠธ๋ฆฌ๋ทฐํŠธ๋Š” ๊ฐœ๋ณ„ ์ŠคํŒฌ(์ถ”์  ๋ฐ์ดํ„ฐ์˜ ๊ธฐ๋ณธ ๋‹จ์œ„)์— ์ง์ ‘ ์—ฐ๊ฒฐ๋˜๋ฉฐ, ๊ทธ ์ŠคํŒฌ์˜ ์ž‘์—…์„ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ŠคํŒฌ์—๋Š” ์š”์ฒญ ๋ฉ”์†Œ๋“œ, URL, ์ƒํƒœ ์ฝ”๋“œ์™€ ๊ฐ™์€ ์ •๋ณด๊ฐ€ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ๋กœ ์ถ”๊ฐ€๋  ์ˆ˜ ์žˆ๋‹ค.
    • ์ ์šฉ ๋ฒ”์œ„: ์–ดํŠธ๋ฆฌ๋ทฐํŠธ๋Š” ํŠน์ • ์ŠคํŒฌ์— ๊ตญํ•œ๋˜์–ด ์‚ฌ์šฉ๋˜๋ฉฐ, ์ŠคํŒฌ๋งˆ๋‹ค ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” ๋™์ ์œผ๋กœ ๋ณ€ํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๋ฉฐ, ์ŠคํŒฌ์˜ ์ƒ๋ช…์ฃผ๊ธฐ ๋™์•ˆ์—๋งŒ ์œ ํšจํ•˜๋‹ค.
  • ๋ฆฌ์†Œ์Šค(Resources)
    • ๋ชฉ์ : ๋ฆฌ์†Œ์Šค๋Š” ์ถ”์  ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ(์˜ˆ: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜, ์„œ๋ฒ„, ์„œ๋น„์Šค)์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์ด๋Š” ์ถ”์  ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋œ ํ™˜๊ฒฝ์— ๋Œ€ํ•œ ์ •์ ์ธ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๋ฉฐ, ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ ์ „์ฒด์— ์ผ๊ด€๋˜๊ฒŒ ์ ์šฉ๋œ๋‹ค.
    • ์ ์šฉ ๋ฒ”์œ„: ๋ฆฌ์†Œ์Šค๋Š” ํŠธ๋ ˆ์ด์„œ ํ”„๋กœ๋ฐ”์ด๋”๋‚˜ ๋ฉ”ํŠธ๋ฆญ ํ”„๋กœ๋ฐ”์ด๋”์™€ ์—ฐ๊ฒฐ๋˜์–ด, ํ•ด๋‹น ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ์‚ฌ์šฉํ•ด ์ƒ์„ฑ๋œ ๋ชจ๋“  ์ŠคํŒฌ์ด๋‚˜ ๋ฉ”ํŠธ๋ฆญ์— ๊ณตํ†ต์ ์œผ๋กœ ์ ์šฉ๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํ•˜๋‚˜์˜ ์„œ๋น„์Šค ์ด๋ฆ„, ์„œ๋น„์Šค ๋ฒ„์ „, ํ˜ธ์ŠคํŠธ ์ด๋ฆ„ ๋“ฑ์ด ๋ชจ๋“  ์ŠคํŒฌ์— ๊ฑธ์ณ ์ผ๊ด€์ ์œผ๋กœ ํ‘œํ˜„๋˜์–ด์•ผ ํ•  ์ •๋ณด๋‹ค.

[6] ๋ฐฐ๊ธฐ์ง€ (Baggage)

- ๋งํฌ: https://opentelemetry.io/docs/concepts/signals/baggage/

๋ฐฐ๊ธฐ์ง€๋Š” ์ถ”์ ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ •์˜ ๊ฐ€๋Šฅํ•œ ํ‚ค-๊ฐ’ ์Œ ํ˜•์‹์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ด๋‹ค.

from opentelemetry import trace
from opentelemetry import baggage

# ํŠธ๋ ˆ์ด์„œ์™€ ๋ฐฐ๊ธฐ์ง€ ์ดˆ๊ธฐํ™”
tracer = trace.get_tracer(__name__)

# ์‚ฌ์šฉ์ž ์„ธ์…˜ ์ •๋ณด๋ฅผ ๋ฐฐ๊ธฐ์ง€์— ์ถ”๊ฐ€
baggage.set_baggage("user.session_id", "session_12345")
baggage.set_baggage("user.preferred_language", "en-US")

# ๋ฐฐ๊ธฐ์ง€๋ฅผ ํ™œ์šฉํ•œ ์ถ”์  ์‹œ์ž‘
with tracer.start_as_current_span("example-operation") as span:
    # ๋ฐฐ๊ธฐ์ง€์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•˜์—ฌ ๋กœ๊ทธ์— ๊ธฐ๋ก
    session_id = baggage.get_baggage("user.session_id")
    language = baggage.get_baggage("user.preferred_language")
    span.set_attribute("user.session_id", session_id)
    span.set_attribute("user.language", language)
    print(f"Operating in session {session_id} with language setting {language}")

๋ฐฐ๊ธฐ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชฉ์ ์€ ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์—์„œ ์„œ๋น„์Šค ๊ฐ„ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ค‘์š”ํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
์ „์ฒด ํŠธ๋žœ์ ์…˜์˜ ๋งฅ๋ฝ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๊ฐ ์„œ๋น„์Šค๊ฐ€ ํ•„์š”ํ•œ ์ •๋ณด์— ์ ‘๊ทผํ•  ์ˆ˜์žˆ๋„๋ก ํ•œ๋‹ค.

  • ํ•„์š”์„ฑ
    • ๋ฐฐ๊ธฐ์ง€๋Š” ์ฝ˜ํ…์ŠคํŠธ ๊ฐ’์„ ํ†ต์ผ๋œ ํ˜•์‹๊ณผ ํŒจํ„ด์œผ๋กœ ์ œ๊ณตํ•˜์—ฌ, ๋ชจ๋“  ์„œ๋น„์Šค๊ฐ€ ๋™์ผํ•œ ์ •๋ณด๋ฅผ ๊ณต์œ ํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
    • ์–ธ์–ด๋‚˜ ํ”Œ๋žซํผ์— ๊ด€๊ณ„์—†์ด, ๋ชจ๋“  ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ๋ฐฐ๊ธฐ์ง€๋ฅผ ์ฝ๊ณ  ๊ตฌ๋ฌธ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ์–ด, ๋‹ค์–‘ํ•œ ๊ธฐ์ˆ  ์Šคํƒ์„ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€๊ทœ๋ชจ ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์—์„œ๋„ ํ†ตํ•ฉ์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋Œ€๊ทœ๋ชจ ๋ถ„์‚ฐ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๊ณ  ํŒ€์ด ์›ํ•˜๋Š” ์–ธ์–ด๋‚˜ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž์œจ์„ฑ์„ ์ œ๊ณตํ•˜๋ ค๋Š” ๊ฒฝ์šฐ์— ์œ ์šฉํ•˜๋‹ค.
  • ํ™œ์šฉ๋ฒ•
    • ์™ธ๋ถ€์— ์ž ์žฌ์ ์œผ๋กœ ๋…ธ์ถœํ•ด๋„ ์ƒ๊ด€์—†๋Š” ๋ฏผ๊ฐํ•˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ์— ์‚ฌ์šฉํ•œ๋‹ค. (์˜ˆ, ๊ณ„์ •ID, ์‚ฌ์šฉ์ž ID, ์ œํ’ˆ ID, ์›๋ณธ IP๋“ฑ)
    • ์ƒ์œ„ ์Šคํƒ์—์„œ๋งŒ ์—‘์„ธ์Šค ํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋ณด๊ฐ€ ์žˆ๋‹ค๋ฉด ํ•˜์œ„ ์Šคํƒ์œผ๋กœ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ฐฐ๊ธฐ์ง€์— ์ •์˜ํ•˜๊ณ  ๋‹ค์šด์ŠคํŠธ๋ฆผ ์„œ๋น„์Šค์˜ ์ŠคํŒฌ์— ์ „ํŒŒํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๊ด€์ธก๊ฐ€๋Šฅ์„ฑ ๋ฐฑ์—”๋“œ์—์„œ๋„ ๋ฐฐ๊ธฐ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ณด๋‹ค ์‰ฌ์šด ๊ฒ€์ƒ‰๊ณผ ํ•„ํ„ฐ๋ง์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ์ฃผ์˜์‚ฌํ•ญ
    • ๋ฐฐ๊ธฐ์ง€๋Š” ๋ถ„์‚ฐ ์‹œ์Šคํ…œ ๋‚ด ๋ชจ๋“  ์„œ๋น„์Šค์— ์ „๋‹ฌ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ฏผ๊ฐํ•œ ์ •๋ณด๋Š” ๋ฐฐ๊ธฐ์ง€๋ฅผ ํ†ตํ•ด ์ „์†กํ•˜์ง€ ์•Š๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.
    • ๊ฐ ์‹œ์Šคํ…œ๋งˆ๋‹ค (์˜ˆ, fastapi, spring, rails๋ฅผ ์‚ฌ์šฉํ•˜๊ณ ์žˆ๋‹ค๋ฉด ๊ฐ๊ฐ) ๋™์ผํ•œ key-value ๋ฅผ ๊ฐ€์ง„ ๋ฐฐ๊ธฐ์ง€๋ฅผ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

[7] ์ด๋ฒคํŠธ

์ด๋ฒคํŠธ(Event) ๋Š” ์ถ”์ (Trace) ์ค‘์— ํŠน์ • ์ŠคํŒฌ(Span)๊ณผ ์—ฐ๊ด€๋œ ์‹œ๊ฐ„์— ๋ฏผ๊ฐํ•œ ์ด๋ฒคํŠธ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.์ด๋ฒคํŠธ๋Š” ์ŠคํŒฌ ๋‚ด์—์„œ ํŠน์ • ์ˆœ๊ฐ„์— ๋ฐœ์ƒํ•˜๋Š” ์‚ฌ๊ฑด์„ ๊ธฐ๋กํ•˜์—ฌ ์ถ”์  ๋ฐ์ดํ„ฐ์— ์ถ”๊ฐ€์ ์ธ ์ •๋ณด์™€ ์ปจํ…์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•œ๋‹ค.

๊ธฐ๋ก๋œ ์ด๋ฒคํŠธ๋“ค์€ ์ŠคํŒฌ(Span)๊ณผ ํ•จ๊ป˜ ์ˆ˜์ง‘๋˜์–ด ์ถ”์  ๋ฐ์ดํ„ฐ์˜ ์ผ๋ถ€๋กœ ๊ด€์ธก ๊ฐ€๋Šฅ์„ฑ ๋ฐฑ์—”๋“œ ์‹œ์Šคํ…œ์— ์ „์†ก๋œ๋‹ค.
์ŠคํŒฌ์˜ ์ƒ๋ช…์ฃผ๊ธฐ ๋™์•ˆ ๋ฐœ์ƒํ•˜๋Š” ์ค‘์š”ํ•œ ์ˆœ๊ฐ„๋“ค์„ ํฌ์ฐฉํ•˜์—ฌ ์ถ”๊ฐ€์ ์ธ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋ฏ€๋กœ, ์ด๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ์ €์žฅํ•˜๋Š” ๊ฒƒ์€ ์‹œ์Šคํ…œ์˜ ํ–‰๋™์„ ๋”์šฑ ์ž˜ ์ดํ•ดํ•˜๊ณ  ๋ฌธ์ œ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ์ค‘์š”ํ•˜๋‹ค. (์—๋Ÿฌ๋ชจ๋‹ˆํ„ฐ๋ง, ์„ฑ๋Šฅ๋ถ„์„, ์‚ฌ์šฉ์ž ํ–‰๋™๋ถ„์„๋“ฑ์— ์“ฐ์ผ ์ˆ˜ ์žˆ์Œ)

from fastapi import FastAPI
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

app = FastAPI()

trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)
trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))

# /๋ฅผ ์š”์ฒญํ•˜๋ฉด "Hello, World!"๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ์š”์ฒญ์„ ๋ฐ›์„๋•Œ์™€ ์‘๋‹ต์„ ๋ณด๋‚ผ๋•Œ ์ด๋ฒคํŠธ์— ๊ธฐ๋ก
@app.get("/")
async def hello():
    with tracer.start_as_current_span("hello_request") as span:
        span.add_event("Received request", {"path": "/"})
        response = "Hello, World!"
        span.add_event("Sending response", {"response": response})
        return response

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8000)

[8] ๋งํฌ

๋งํฌ(Link) ๋Š” ์—ฌ๋Ÿฌ ์ŠคํŒฌ(Span) ์‚ฌ์ด์˜ ๊ด€๊ณ„๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ตฌ์กฐ๋‹ค.
์ŠคํŒฌ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ํ‘œํ˜„ํ•˜๋ฉฐ, ์ŠคํŒฌ์€ ๋‹ค๋ฅธ ์ŠคํŒฌ์— ์—ฐ๊ฒฐํ•˜๋Š” ์ŠคํŒฌ ๋งํฌ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์—ฐ๊ฒฐ์€ ์ŠคํŒฌ๋“ค์ด ์„œ๋กœ ๋‹ค๋ฅธ ์ถ”์ ์— ์†ํ•˜๊ฑฐ๋‚˜ ๋™์ผ ์ถ”์ ์— ์†ํ•˜๋ฉด์„œ๋„ ๋ณต์žกํ•œ ์ƒํ˜ธ ์ž‘์šฉ์„ ๊ฐ€์งˆ ๋•Œ ์œ ์šฉํ•˜๋‹ค.

  • ์‚ฌ์šฉ์‚ฌ๋ก€
    • ๋น„๋™๊ธฐ ์ž‘์—…
    • ๋ฐฐ์น˜์ฒ˜๋ฆฌ
    • ๋ณต์žกํ•œ ์˜์กด์„ฑ

๋งํฌ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ŠคํŒฌ ์ฝ˜ํ…์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

# ํŠธ๋ ˆ์ด์„œ ํ”„๋กœ๋ฐ”์ด๋” ์ดˆ๊ธฐํ™”
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)
trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))

# ์ฒซ ๋ฒˆ์งธ ์ŠคํŒฌ ์ƒ์„ฑ
with tracer.start_as_current_span("first_operation") as first_span:
    # ์ฒซ ๋ฒˆ์งธ ์ž‘์—…์„ ์ˆ˜ํ–‰

    # ๋‘ ๋ฒˆ์งธ ์ŠคํŒฌ ์ƒ์„ฑ, ์ฒซ ๋ฒˆ์งธ ์ŠคํŒฌ๊ณผ ๋งํฌ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ
    with tracer.start_as_current_span("second_operation", links=[trace.Link(first_span.get_span_context())]) as second_span:
        # ๋‘ ๋ฒˆ์งธ ์ž‘์—…์„ ์ˆ˜ํ–‰
        pass

    # ์ฒซ ๋ฒˆ์งธ ์ŠคํŒฌ๊ณผ๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์„ธ ๋ฒˆ์งธ ์ŠคํŒฌ์„ ์ƒ์„ฑํ•˜๋˜ ์ฒซ ๋ฒˆ์งธ ์ŠคํŒฌ๊ณผ ๋งํฌ
    with tracer.start_as_current_span("third_operation", links=[trace.Link(first_span.get_span_context())]) as third_span:
        # ์„ธ ๋ฒˆ์งธ ์ž‘์—…์„ ์ˆ˜ํ–‰
        pass

6.2.2 Context Propagation (์ฝ˜ํ…์ŠคํŠธ ์ „ํŒŒ)

- ์ฐธ๊ณ : https://opentelemetry.io/docs/concepts/context-propagation/

์ปจํ…์ŠคํŠธ ์ „ํŒŒ(Context Propagation) ๋Š” ๋ถ„์‚ฐ ์‹œ์Šคํ…œ ๋‚ด์—์„œ ์ถ”์ (Trace) ๋ฐ์ดํ„ฐ๋‚˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋น„์Šค ๊ฐ„์— ์ „๋‹ฌํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด๋‹ค.

์ปจํ…์ŠคํŠธ ์ „ํŒŒ๋ฅผ ํ†ตํ•ด ์š”์ฒญ์ด ๋‹ค์–‘ํ•œ ์„œ๋น„์Šค๋ฅผ ๊ฑฐ์น˜๋ฉด์„œ๋„ ์—ฐ๊ด€๋œ ์ถ”์  ์ •๋ณด(์˜ˆ: ์ŠคํŒฌ ID, ์ถ”์  ID)์™€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(์˜ˆ: Baggage)๋ฅผ ์œ ์ง€ํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
์ถ”์ ์—๋งŒ ๊ตญํ•œ๋˜์ง€๋Š” ์•Š์ง€๋งŒ, ์ถ”์ ์„ ํ†ตํ•ด ํ”„๋กœ์„ธ์Šค ๋ฐ ๋„คํŠธ์›Œํฌ ๊ฒฝ๊ณ„์— ์ž„์˜๋กœ ๋ถ„์‚ฐ๋œ ์„œ๋น„์Šค ์ „๋ฐ˜์— ๊ฑธ์ณ ์‹œ์Šคํ…œ์— ๋Œ€ํ•œ ์ธ๊ณผ์ •๋ณด๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฝ˜ํ…์ŠคํŠธ ์ „ํŒŒ์—๋Š” ๋‘๊ฐ€์ง€ ํ•ต์‹ฌ ๊ฐœ๋…์ด ์žˆ๋‹ค.

Context

- ์ฐธ๊ณ : Context

์ปจํ…์ŠคํŠธ๋Š” ์ถ”์  ์ •๋ณด(์˜ˆ: ์ถ”์  ID, ์ŠคํŒฌ ID)์™€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฐ์ฒด๋กœ, ์„œ๋น„์Šค ๊ฐ„ ์š”์ฒญ์˜ ์—ฐ๊ด€์„ฑ์„ ํŒŒ์•…ํ•˜๊ณ  ์ถ”์ ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๋‹ด๊ณ ์žˆ๋‹ค.

Propagation

์ „ํŒŒ๋Š” ์„œ๋น„์Šค๋‚˜ ํ”„๋กœ์„ธ์Šค ๊ฐ„์— ์ปจํ…์ŠคํŠธ๋ฅผ ์ด๋™์‹œํ‚ค๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์˜๋ฏธํ•œ๋‹ค.
์ปจํ…์ŠคํŠธ ๊ฐ์ฒด๋Š” ์ง๋ ฌํ™”(serialize)๋˜๊ฑฐ๋‚˜ ์—ญ์ง๋ ฌํ™”(deserialize)๋˜์–ด, ํ•„์š”ํ•œ ์ถ”์  ์ •๋ณด๊ฐ€ ๋‹ค์Œ ์„œ๋น„์Šค๋กœ ์ „๋‹ฌ๋œ๋‹ค.
์ผ๋ฐ˜์ ์œผ๋กœ ์ด ์ž‘์—…์€ OpenTelemetry์™€ ๊ฐ™์€ ๊ณ„์ธก ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์˜ํ•ด ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋ฉฐ, ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๊ด€์—ฌํ•  ํ•„์š”๋Š” ์—†๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ W3C์˜ TraceContext ๋ช…์„ธ๋ฅผ ๋”ฐ๋ฅด๋Š” ํ—ค๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปจํ…์ŠคํŠธ๋ฅผ ์ „ํŒŒํ•œ๋‹ค.

OpenTelemetry๋Š” traceparent, tracestate ํ—ค๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋น„์Šค ๊ฐ„์— ์ „ํŒŒ๋œ๋‹ค.

# traceparent ํ—ค๋”
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
              [1]                 [2]                   [3]      [4]
  1. version: traceparent ํ—ค๋”์˜ ๋ฒ„์ „
  2. TraceID: ์ „์ฒด ์ถ”์ ์„ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„ํ•˜๋Š” 16๋ฐ”์ดํŠธ ๊ธธ์ด์˜ ์‹๋ณ„์ž
  3. Perent Spen ID: ํ˜„์žฌ ์ŠคํŒฌ์˜ ๋ถ€๋ชจ ์ŠคํŒฌ์„ ์‹๋ณ„ํ•˜๋Š” 8๋ฐ”์ดํŠธ ๊ธธ์ด์˜ ์‹๋ณ„์ž
  4. Trace Flag: ์ถ”์ ๊ณผ ๊ด€๋ จ๋œ ์ถ”๊ฐ€์ ์ธ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋Š” 1๋ฐ”์ดํŠธ ๊ธธ์ด์˜ ํ”Œ๋ž˜๊ทธ (์ƒ˜ํ”Œ๋ง ์—ฌ๋ถ€, "01" ์ƒ˜ํ”Œ๋ง ํ™œ์„ฑํ™”)
# tracestate ํ—ค๋”
tracestate: congo=t61rcWkgMzE,rojo=00f067aa0ba902b7
            [key]=[value]    ,[key]=[value]

Tracestate ํ—ค๋”์—๋Š” Traceparent ํ—ค๋”์™€ ํ•จ๊ป˜ ์ถ”๊ฐ€ ์‹๋ณ„์ž๋ฅผ ์ „ํŒŒํ•  ์ˆ˜ ์žˆ๋Š” ์ž„์˜ ๋ฐ์ดํ„ฐ์˜ ํ‚ค-๊ฐ’ ์Œ์ด ํฌํ•จ๋œ๋‹ค.

์œ„์—์„œ ์–ธ๊ธ‰ํ•œ Baggage๋ž‘ ์•ฝ๊ฐ„ ํ—ท๊ฐˆ๋ฆฌ๋Š”๋ฐ, ์—ญํ• ๊ณผ ๋ชฉ์ ์ด ๋‹ค๋ฅด๋‹ค.

  • Tracestate:
    • traceparent ํ—ค๋”์™€ ํ•จ๊ป˜ ์ถ”์  ์ปจํ…์ŠคํŠธ๋ฅผ ์ „ํŒŒํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ
    • ์ถ”์ ์‹œ์Šคํ…œ์ด ํ•„์š”๋กœํ•˜๋Š” ๊ธฐ์ˆ ์ , ์šด์˜์  ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์ „ํŒŒ
  • Baggage:
    • ์‚ฌ์šฉ์ž ์ •์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์—ญ์ ์œผ๋กœ ์ „ํŒŒํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ˆ˜์ค€์˜ ์˜๋ฏธ์žˆ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค/์šด์˜ ๋ฐ์ดํ„ฐ ์ „ํŒŒ

6.2.3 Pipeline (ํŒŒ์ดํ”„๋ผ์ธ)

Pipeline

ํŒŒ์ดํ”„๋ผ์ธ(Pipeline) ์€ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘, ์ฒ˜๋ฆฌ ๋ฐ ์ „์†ก์˜ ํ๋ฆ„์„ ์„ค๋ช…ํ•˜๋Š” ๊ฐœ๋…์ด๋ฉฐ, ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์‹ ํ˜ธ(์ถ”์ ,๋ฉ”ํŠธ๋ฆญ,๋กœ๊ทธ)๋ฅผ ์ˆ˜์ง‘, ๋ณ€ํ™˜, ์ˆ˜์ถœํ•˜๋Š” ๊ณผ์ •์„ ํฌํ•จํ•œ๋‹ค.

์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์›Œ์„œ ์ฝ”๋“œ์—์„œ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋ฉด ์ข‹์„๊ฒƒ๊ฐ™๋‹ค.

from fastapi import FastAPI
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor

app = FastAPI()

# Provider: TracerProvider ์„ค์ •
trace.set_tracer_provider(TracerProvider())

# Exporter: ์ถ”์  ๋ฐ์ดํ„ฐ ์ฝ˜์†”๋กœ ์ถœ๋ ฅ
exporter = ConsoleSpanExporter()

# Processor: SimpleSpanProcessor๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ŠคํŒฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ Exporter์— ๋ณด๋ƒ„
processor = SimpleSpanProcessor(exporter)
trace.get_tracer_provider().add_span_processor(processor)

# Telemetry Generator: FastAPI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ž๋™์œผ๋กœ ์ถ”์ ํ•˜๋„๋ก ์„ค์ •
FastAPIInstrumentor.instrument_app(app)

# ๊ธฐ๋ณธ ๋ฃจํŠธ ๊ฒฝ๋กœ: ๊ฐ„๋‹จํ•œ HTTP GET ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ถ”์ 
@app.get("/")
async def read_root():
    tracer = trace.get_tracer(__name__)
    # Telemetry Generator: API ์š”์ฒญ์„ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.
    with tracer.start_as_current_span("read_root"):
        return {"Hello": "World"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8000)
  1. Provider (ํ”„๋กœ๋ฐ”์ด๋”)
    • ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ์˜ ์‹œ์ž‘์ ์€ Provider๋‹ค.
    • SDK์˜ ์ดˆ๊ธฐ ์„ค์ • ๋‹จ๊ณ„์—์„œ ํŠธ๋ ˆ์ด์„œ๋‚˜ ๋ฏธํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋ฉฐ, ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘์˜ ์‹œ์ž‘์  ์—ญํ• ์„ ํ•œ๋‹ค.
    • TracerProvider๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹ ํ˜ธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.
    • ์ถ”์ (TracerProvider), ๋ฉ”ํŠธ๋ฆญ(MeterProvider), ๋กœ๊ทธ (LoggerProvider) ๋“ฑ์˜ ํ”„๋กœ๋ฐ”์ด๋”๊ฐ€ ์žˆ๋‹ค.
  2. Telemetry Generator (ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์ƒ์„ฑ๊ธฐ)
    • ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
    • SDK๊ฐ€ ์„ค์น˜๋œ ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋˜์–ด ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์„ ์ถ”์ ํ•˜๊ฑฐ๋‚˜ ๋ฉ”ํŠธ๋ฆญ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  3. Processor (ํ”„๋กœ์„ธ์„œ)
    • ์ˆ˜์ง‘๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ, ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง, ์ƒ˜ํ”Œ๋ง, ๋ณ€ํ˜• ๋“ฑ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ถœํ•˜๊ธฐ ์ „์— ํ•„์š”ํ•œ ํ˜•์‹์œผ๋กœ ์กฐ์ •ํ•˜๋Š” ๋‹จ๊ณ„๋‹ค.
  4. Exporter (์ต์Šคํฌํ„ฐ)
    • ์ฒ˜๋ฆฌ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ตœ์ข…์ ์œผ๋กœ ์™ธ๋ถ€ ์‹œ์Šคํ…œ(์˜ˆ: ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ, ๋ถ„์„ ์„œ๋น„์Šค)์œผ๋กœ ์ „์†กํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
    • ๋‹ค์–‘ํ•œ ํƒ€์ž…์˜ ์ต์Šคํฌํ„ฐ๊ฐ€ ์กด์žฌํ•˜๋ฉฐ, ๊ฐ๊ฐ ํŠน์ • ๋ฐฑ์—”๋“œ ์‹œ์Šคํ…œ์— ์ตœ์ ํ™”๋˜์–ด ์žˆ๋‹ค.

Collector

- ์ฐธ๊ณ ๋งํฌ: https://opentelemetry.io/docs/collector/

Pipeline์„ ๋ณด๋‹ค๋ณด๋‹ˆ, ๋น„์Šทํ•œ ๊ฐœ๋…์ธ Collector์™€ ์•ฝ๊ฐ„ ํ˜ผ๋™๋˜์—ˆ๋‹ค.

์œ„์—์„œ ์„ค๋ช…ํ•˜๊ณ ์žˆ๋Š” ๋„ค๊ฐ€์ง€ ์š”์†Œ Provider, Generator, Processer, Exporter๋Š” OpenTelemetry์˜ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํŒŒ์ดํ”„๋ผ์ธ์˜ ๊ตฌ์„ฑ ์š”์†Œ์ด๋ฉฐ, SDK๋‚ด์—์„œ ๊ตฌํ˜„๋œ๋‹ค.
์ฆ‰, Pipeline์€ SDK์—์„œ ๊ตฌํ˜„๋˜๋ฉฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ ๋‚ด์— ์ง์ ‘ ํ†ตํ•ฉ๋˜์–ด ๋ฐ์ดํ„ฐ์˜ ์ˆ˜์ง‘๊ณผ ์ฒ˜๋ฆฌ ์ˆ˜์ถœ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

๋ฐ˜๋ฉด์—, OpenTelemetry Collector๋Š” ์ด๋Ÿฌํ•œ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ, ๋…๋ฆฝ์ ์ธ ์„œ๋น„์Šค๋‚˜ ์—์ด์ „ํŠธ ํ˜•ํƒœ๋กœ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ณ„๋„์˜ ๊ตฌ์„ฑ ์š”์†Œ์ด๋‹ค.

Collector๋Š” ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ๋‹ค์–‘ํ•œ ์†Œ์Šค๋กœ๋ถ€ํ„ฐ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ , ์ด๋ฅผ ์ฒ˜๋ฆฌํ•œ ํ›„ ๋‹ค์–‘ํ•œ ๋ฐฑ์—”๋“œ ์‹œ์Šคํ…œ์œผ๋กœ ์ „์†กํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. (์„ค์น˜๋„ ๋„์ปค, ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค, Linux apk rpm ๋“ฑ๋“ฑ ์—ฌ๋Ÿฌ ์˜ต์…˜์„ ์ œ๊ณตํ•œ๋‹ค.(์ฐธ๊ณ ))

6.3 ์ถ”์ 

6.3.1 ์˜คํ”ˆํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์ถ”์  ์†Œ๊ฐœ

์ถ”์ (Trace)๋Š” ์‚ฌ์šฉ์ž๋‚˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์š”์ฒญ์„ ํ• ๋•Œ ์–ด๋–ค์ผ์ด ๋ฐœ์ƒํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ํฐ ๊ทธ๋ฆผ์„ ์ œ๊ณตํ•œ๋‹ค.

์ƒ˜ํ”Œ Trace ์ฝ”๋“œ

์•„๋ž˜ ์ถ”์  ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์–ด๋–ป๊ฒŒ ์ถ”์ ํ•˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž.

pip install opentelemetry-sdk opentelemetry-api
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

# ํŠธ๋ ˆ์ด์„œ ํ”„๋กœ๋ฐ”์ด๋” ์ดˆ๊ธฐํ™”
tracer_provider = TracerProvider()

# ์ฝ˜์†” ์ต์Šคํฌํ„ฐ ์„ค์ •
exporter = ConsoleSpanExporter()
processor = SimpleSpanProcessor(exporter)
tracer_provider.add_span_processor(processor)

# ์ „์—ญ ํŠธ๋ ˆ์ด์„œ ํ”„๋กœ๋ฐ”์ด๋” ์„ค์ •
trace.set_tracer_provider(tracer_provider)

# ํŠธ๋ ˆ์ด์„œ ์–ป๊ธฐ
tracer = trace.get_tracer(__name__)

# ์ค‘์ฒฉ๋œ ์ŠคํŒฌ ์ƒ์„ฑ
with tracer.start_as_current_span("Red") as span_red:
    # 'Red' ์ŠคํŒฌ ์‹œ์ž‘
    with tracer.start_as_current_span("Green") as span_green:
        # 'Green' ์ŠคํŒฌ ์‹œ์ž‘
        with tracer.start_as_current_span("Blue") as span_blue:
            # 'Blue' ์ŠคํŒฌ ์‹œ์ž‘
            print("ํ…Œ์ŠคํŠธ")

์ด๊ฑธ ์‹คํ–‰์‹œํ‚ค๋ฉด JSON ํ˜•์‹์˜ ์ถœ๋ ฅ์ด ๋‚˜์˜จ๋‹ค. ๊ฐ ํ•ญ๋ชฉ์„ ํ™•์ธํ•ด๋ณด์ž.

# ์ถœ๋ ฅ ๊ฒฐ๊ณผ

ํ…Œ์ŠคํŠธ
{
    "name": "Blue",
    "context": {
        "trace_id": "0x7ed659dfc0ffd4525411421dd32c5b0d",
        "span_id": "0x892c5d277b377e8b",
        "trace_state": "[]"                               
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": "0x95bfd1111d35c3d1",
    "start_time": "2024-04-14T13:04:30.679814Z",
    "end_time": "2024-04-14T13:04:30.679839Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {},
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.24.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}
{
    "name": "Green",
    "context": {
        "trace_id": "0x7ed659dfc0ffd4525411421dd32c5b0d",
        "span_id": "0x95bfd1111d35c3d1",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": "0x4b539330e3f8385e",
    "start_time": "2024-04-14T13:04:30.679798Z",
    "end_time": "2024-04-14T13:04:30.680267Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {},
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.24.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}
{
    "name": "Red",
    "context": {
        "trace_id": "0x7ed659dfc0ffd4525411421dd32c5b0d",
        "span_id": "0x4b539330e3f8385e",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": null,
    "start_time": "2024-04-14T13:04:30.679770Z",
    "end_time": "2024-04-14T13:04:30.680328Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {},
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.24.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}

์ƒ˜ํ”Œ Trace ์ฝ”๋“œ + Attributes

Attribute๋Š” ์ŠคํŒฌ์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•œ๋‹ค. (ํƒœ๊ทธ์™€ ๋น„์Šท)

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

# ํŠธ๋ ˆ์ด์„œ ํ”„๋กœ๋ฐ”์ด๋” ์ดˆ๊ธฐํ™”
tracer_provider = TracerProvider()

# ์ฝ˜์†” ์ต์Šคํฌํ„ฐ ์„ค์ •
exporter = ConsoleSpanExporter()
processor = SimpleSpanProcessor(exporter)
tracer_provider.add_span_processor(processor)

# ์ „์—ญ ํŠธ๋ ˆ์ด์„œ ํ”„๋กœ๋ฐ”์ด๋” ์„ค์ •
trace.set_tracer_provider(tracer_provider)

# ํŠธ๋ ˆ์ด์„œ ์–ป๊ธฐ
tracer = trace.get_tracer(__name__)

# ์ค‘์ฒฉ๋œ ์ŠคํŒฌ ์ƒ์„ฑ
with tracer.start_as_current_span("Red", attributes={"color": "red"}) as span_red:
    # 'Red' ์ŠคํŒฌ ์‹œ์ž‘
    with tracer.start_as_current_span("Green", attributes={"color": "green"}) as span_green:
        # 'Green' ์ŠคํŒฌ ์‹œ์ž‘
        with tracer.start_as_current_span("Blue", attributes={"color": "blue"}) as span_blue:
            # 'Blue' ์ŠคํŒฌ ์‹œ์ž‘
            print("ํ…Œ์ŠคํŠธ")

๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค. ์ถ”๊ฐ€ ์ •๋ณด๊ฐ€ attributes์— ์ถ”๊ฐ€๋˜์—ˆ๋‹ค.

ํ…Œ์ŠคํŠธ
{
    "name": "Blue",
    "context": {
        "trace_id": "0x02eb6338a1f40daf7230e2df981eabf0",
        "span_id": "0xb7518ad715b93ed0",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": "0x88769b3b31af6642",
    "start_time": "2024-04-14T13:12:18.444421Z",
    "end_time": "2024-04-14T13:12:18.444444Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "color": "blue"
    },
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.24.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}
{
    "name": "Green",
    "context": {
        "trace_id": "0x02eb6338a1f40daf7230e2df981eabf0",
        "span_id": "0x88769b3b31af6642",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": "0xedc13ebcf868ffe4",
    "start_time": "2024-04-14T13:12:18.444405Z",
    "end_time": "2024-04-14T13:12:18.444880Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "color": "green"
    },
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.24.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}
{
    "name": "Red",
    "context": {
        "trace_id": "0x02eb6338a1f40daf7230e2df981eabf0",
        "span_id": "0xedc13ebcf868ffe4",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": null,
    "start_time": "2024-04-14T13:12:18.444377Z",
    "end_time": "2024-04-14T13:12:18.444940Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "color": "red"
    },
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.24.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}

์ƒ˜ํ”Œ Trace ์ฝ”๋“œ + Attributes + events

์ด๋ฒคํŠธ(Event)๋Š” ์ŠคํŒฌ(Span) ๋‚ด์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ฃผ์š” ์ˆœ๊ฐ„์ด๋‚˜ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฐ ์‚ฌ์šฉํ•œ๋‹ค.
์ด๋ฒคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ์ถ”์  ๋ฐ์ดํ„ฐ์— ์ค‘์š”ํ•œ ํƒ€์ž„์Šคํƒฌํ”„์™€ ๊ด€๋ จ ์ •๋ณด๋ฅผ ํฌํ•จ์‹œ์ผœ ๋ถ„์„ ์‹œ ๋” ๋งŽ์€ ์ปจํ…์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.

'Blue' ์ŠคํŒฌ์— ์ด๋ฒคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

# ํŠธ๋ ˆ์ด์„œ ํ”„๋กœ๋ฐ”์ด๋” ์ดˆ๊ธฐํ™”
tracer_provider = TracerProvider()

# ์ฝ˜์†” ์ต์Šคํฌํ„ฐ ์„ค์ •
exporter = ConsoleSpanExporter()
processor = SimpleSpanProcessor(exporter)
tracer_provider.add_span_processor(processor)

# ์ „์—ญ ํŠธ๋ ˆ์ด์„œ ํ”„๋กœ๋ฐ”์ด๋” ์„ค์ •
trace.set_tracer_provider(tracer_provider)

# ํŠธ๋ ˆ์ด์„œ ์–ป๊ธฐ
tracer = trace.get_tracer(__name__)

# ์ค‘์ฒฉ๋œ ์ŠคํŒฌ ์ƒ์„ฑ
with tracer.start_as_current_span("Red", attributes={"color": "red"}) as span_red:
    # 'Red' ์ŠคํŒฌ ์‹œ์ž‘
    with tracer.start_as_current_span("Green", attributes={"color": "green"}) as span_green:
        # 'Green' ์ŠคํŒฌ ์‹œ์ž‘
        with tracer.start_as_current_span("Blue", attributes={"color": "blue"}) as span_blue:
            # 'Blue' ์ŠคํŒฌ ์‹œ์ž‘
            # ์ด๋ฒคํŠธ ์ถ”๊ฐ€
            span_blue.add_event("Important Step Reached", {"step": "initialization"})
            print("ํ…Œ์ŠคํŠธ")

๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

ํ…Œ์ŠคํŠธ
{
    "name": "Blue",
    "context": {
        "trace_id": "0xca57089d5d70504a5a2d8298ef153326",
        "span_id": "0x0f923cd0477c968d",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": "0x5ffc31811c7f0d2f",
    "start_time": "2024-04-14T13:18:39.256182Z",
    "end_time": "2024-04-14T13:18:39.256210Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "color": "blue"
    },
    "events": [             # ์ด๋ฒคํŠธ ์ถ”๊ฐ€๋จ!!!
        {
            "name": "Important Step Reached",
            "timestamp": "2024-04-14T13:18:39.256188Z",
            "attributes": {
                "step": "initialization"
            }
        }
    ],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.24.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}
{
    "name": "Green",
    "context": {
        "trace_id": "0xca57089d5d70504a5a2d8298ef153326",
        "span_id": "0x5ffc31811c7f0d2f",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": "0xdc7e7fd90c4899c3",
    "start_time": "2024-04-14T13:18:39.256164Z",
    "end_time": "2024-04-14T13:18:39.256639Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "color": "green"
    },
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.24.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}
{
    "name": "Red",
    "context": {
        "trace_id": "0xca57089d5d70504a5a2d8298ef153326",
        "span_id": "0xdc7e7fd90c4899c3",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": null,
    "start_time": "2024-04-14T13:18:39.256136Z",
    "end_time": "2024-04-14T13:18:39.256700Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "color": "red"
    },
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.24.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}

์ƒ˜ํ”Œ Trace ์ฝ”๋“œ + Attributes + events + links

๋งํฌ(Link)๋Š” ํ•œ ์ŠคํŒฌ๊ณผ ๋‹ค๋ฅธ ์ŠคํŒฌ์ด๋‚˜ ํŠธ๋ ˆ์ด์Šค ๊ฐ„์˜ ์—ฐ๊ฒฐ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฐ ์‚ฌ์šฉํ•œ๋‹ค.
๋‹ค๋ฅธ ํŠธ๋ ˆ์ด์Šค์— ์†ํ•˜๋Š” ์ŠคํŒฌ๊ณผ์˜ ์—ฐ๊ด€์„ฑ์„ ํ‘œํ˜„ํ•˜๊ฑฐ๋‚˜, ํŠน์ • ์ž‘์—…์˜ ์—ฐ๊ด€๋œ ์ŠคํŒฌ์„ ์ฐธ์กฐํ•  ๋•Œ ์œ ์šฉํ•˜๋‹ค.

'Blue' ์ŠคํŒฌ์— ๋งํฌ๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž!

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider, RandomIdGenerator
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
from opentelemetry.trace import SpanContext, TraceFlags, TraceState

# ํŠธ๋ ˆ์ด์„œ ํ”„๋กœ๋ฐ”์ด๋” ์ดˆ๊ธฐํ™”
tracer_provider = TracerProvider()

# ์ฝ˜์†” ์ต์Šคํฌํ„ฐ ์„ค์ •
exporter = ConsoleSpanExporter()
processor = SimpleSpanProcessor(exporter)
tracer_provider.add_span_processor(processor)

# ์ „์—ญ ํŠธ๋ ˆ์ด์„œ ํ”„๋กœ๋ฐ”์ด๋” ์„ค์ •
trace.set_tracer_provider(tracer_provider)

# ํŠธ๋ ˆ์ด์„œ ์–ป๊ธฐ
tracer = trace.get_tracer(__name__)

# ๋‹ค๋ฅธ ์ŠคํŒฌ์˜ ์ปจํ…์ŠคํŠธ ์ƒ์„ฑ (์˜ˆ์ œ์šฉ)
id_generator = RandomIdGenerator()
trace_id = id_generator.generate_trace_id()
span_id = id_generator.generate_span_id()
other_span_context = SpanContext(
    trace_id=trace_id,
    span_id=span_id,
    is_remote=True,
    trace_flags=TraceFlags.DEFAULT,
    trace_state=TraceState()
)

# ์ค‘์ฒฉ๋œ ์ŠคํŒฌ ์ƒ์„ฑ
with tracer.start_as_current_span("Red", attributes={"color": "red"}) as span_red:
    # 'Red' ์ŠคํŒฌ ์‹œ์ž‘
    with tracer.start_as_current_span("Green", attributes={"color": "green"}) as span_green:
        # 'Green' ์ŠคํŒฌ ์‹œ์ž‘
        with tracer.start_as_current_span("Blue", attributes={"color": "blue"}, links=[trace.Link(other_span_context)]) as span_blue:
            # 'Blue' ์ŠคํŒฌ ์‹œ์ž‘
            # ์ด๋ฒคํŠธ ์ถ”๊ฐ€
            span_blue.add_event("Important Step Reached", {"step": "initialization"})
            print("ํ…Œ์ŠคํŠธ")

์ด์ œ blue Spen์— ๋งํฌ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค.

ํ…Œ์ŠคํŠธ
{
    "name": "Blue",
    "context": {
        "trace_id": "0xb86e3902eb3919b10076b02d6f46377b",
        "span_id": "0x15c255b44bfa9ed0",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": "0x2342aca79612d0bd",
    "start_time": "2024-04-14T13:35:29.000877Z",
    "end_time": "2024-04-14T13:35:29.000911Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "color": "blue"
    },
    "events": [
        {
            "name": "Important Step Reached",
            "timestamp": "2024-04-14T13:35:29.000886Z",
            "attributes": {
                "step": "initialization"
            }
        }
    ],
    "links": [     # ๋งํฌ ์ƒ์„ฑ
        {
            "context": {
                "trace_id": "0xdb58530034b9cd00f08ef767b5199793",
                "span_id": "0x8586c58f9af73b75",
                "trace_state": "[]"
            },
            "attributes": {}
        }
    ],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.24.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}
{
    "name": "Green",
    "context": {
        "trace_id": "0xb86e3902eb3919b10076b02d6f46377b",
        "span_id": "0x2342aca79612d0bd",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": "0xd419153e6f98cab8",
    "start_time": "2024-04-14T13:35:29.000857Z",
    "end_time": "2024-04-14T13:35:29.001474Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "color": "green"
    },
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.24.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}
{
    "name": "Red",
    "context": {
        "trace_id": "0xb86e3902eb3919b10076b02d6f46377b",
        "span_id": "0xd419153e6f98cab8",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": null,
    "start_time": "2024-04-14T13:35:29.000822Z",
    "end_time": "2024-04-14T13:35:29.001557Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "color": "red"
    },
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.24.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}

6.3.2 ์ถ”์  ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑ

์ด ๋ถ€๋ถ„์€ ๋„์ €ํžˆ.. ๋ญ˜ ์„ค๋ช…ํ•˜๊ณ ์ž ํ•˜๋Š”๊ฑด์ง€, ์–ด๋–ป๊ฒŒ ํ…Œ์ŠคํŠธํ•˜๋ผ๋Š”๊ฑด์ง€ ์•Œ์ˆ˜๊ฐ€ ์—†์–ด ์ผ๋‹จ ๊ทธ๋ƒฅ ๋„˜์–ด๊ฐ„๋‹ค..

6.4 ๋ฉ”ํŠธ๋ฆญ

6.4.1 ์˜คํ”ˆํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฉ”ํŠธ๋ฆญ ์†Œ๊ฐœ

๋ฉ”ํŠธ๋ฆญ์€ ์• ํด๋ฆฌ์ผ€์ด์…˜์—์„œ ์ผ์–ด๋‚˜๋Š” ์ผ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋ณด๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

์˜คํ”ˆํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฉ”ํŠธ๋ฆญ์€ ์นด์šดํ„ฐ, ๊ฒŒ์ด์ง€, ํžˆ์Šคํ† ๊ทธ๋žจ ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฉ”ํŠธ๋ฆญ ํƒ€์ž…์„ ์ง€์›ํ•œ๋‹ค.
๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๋˜ ํ”„๋กœ๋ฉ”ํ…Œ์šฐ์Šค ๋ฉ”ํŠธ๋ฆญ API์™€๋Š” ๋งŽ์€ ์ฐจ์ด์ ์ด ์žˆ๋‹ค.
ํ”„๋กœ๋ฉ”ํ…Œ์šฐ์Šค๋Š” 4๊ฐœ์˜ ๋ฉ”ํŠธ๋ฆญ ์œ ํ˜•์„ ์ œ๊ณตํ•˜์ง€๋งŒ, ์˜คํ”ˆํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ๋Š” 6๊ฐ€์ง€ ๋ฉ”ํŠธ๋ฆญ ์œ ํ˜•์„ ์ œ๊ณตํ•œ๋‹ค.

์ƒ˜ํ”Œ Metric ์ฝ”๋“œ

pip install opentelemetry-api opentelemetry-sdk
# https://github.com/open-telemetry/opentelemetry-python/blob/main/docs/examples/metrics/reader/preferred_aggregation.py
import time

from opentelemetry.metrics import get_meter_provider, set_meter_provider
from opentelemetry.sdk.metrics import Counter, MeterProvider
from opentelemetry.sdk.metrics.export import (
    ConsoleMetricExporter,
    PeriodicExportingMetricReader,
)
from opentelemetry.sdk.metrics.view import LastValueAggregation

aggregation_last_value = {Counter: LastValueAggregation()}

exporter = ConsoleMetricExporter(
    preferred_aggregation=aggregation_last_value,
)

reader = PeriodicExportingMetricReader(
    exporter,
    export_interval_millis=5_000,
)

provider = MeterProvider(metric_readers=[reader])
set_meter_provider(provider)

meter = get_meter_provider().get_meter("preferred-aggregation", "0.1.2")

counter = meter.create_counter("my-counter")

for x in range(10):
    counter.add(x)
    time.sleep(2.0)

์ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

import time

from opentelemetry.metrics import get_meter_provider, set_meter_provider
from opentelemetry.sdk.metrics import Counter, MeterProvider
from opentelemetry.sdk.metrics.export import (
    ConsoleMetricExporter,
    PeriodicExportingMetricReader,
)
from opentelemetry.sdk.metrics.view import LastValueAggregation

# ํŠน์ • ๋ฉ”ํŠธ๋ฆญ ์ธ์ŠคํŠธ๋ฃจ๋จผํŠธ ์œ ํ˜•(Counter ๋“ฑ)์— ๋Œ€ํ•ด ์‚ฌ์šฉํ•  ๊ธฐ๋ณธ ์ง‘๊ณ„ ๋ฐฉ์‹์„ ์ •์˜ (์–ด๋–ป๊ฒŒ ์ง‘๊ณ„ํ•  ๊ฒƒ์ธ์ง€)
aggregation_last_value = {Counter: LastValueAggregation()}

# ๋ฉ”ํŠธ๋ฆญ ์ต์Šคํฌํ„ฐ ์ดˆ๊ธฐํ™”
# ์ฝ˜์†”์— ๋ฉ”ํŠธ๋ฆญ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ณด๋‚ด๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
exporter = ConsoleMetricExporter(
    preferred_aggregation=aggregation_last_value,
)

# ๋ฉ”ํŠธ๋ฆญ ๋ฆฌ๋” ์ดˆ๊ธฐํ™”
# ์„ค์ •๋œ ๊ฐ„๊ฒฉ(์—ฌ๊ธฐ์„œ๋Š” 5000 ๋ฐ€๋ฆฌ์ดˆ, ์ฆ‰ 5์ดˆ๋งˆ๋‹ค)์œผ๋กœ ๋ฉ”ํŠธ๋ฆญ์„ ์ˆ˜์ง‘ํ•˜๊ณ  ์ต์Šคํฌํ„ฐ๋ฅผ ํ†ตํ•ด ๋‚ด๋ณด๋‚ด๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
reader = PeriodicExportingMetricReader(
    exporter,
    export_interval_millis=5_000,
)

# ๋ฉ”ํŠธ๋ฆญ ํ”„๋กœ๋ฐ”์ด๋” ์ดˆ๊ธฐํ™”
# ๋ฉ”ํŠธ๋ฆญ ๋ฆฌ๋”๋ฅผ ๋“ฑ๋กํ•˜์—ฌ ์ˆ˜์ง‘๋œ ๋ฉ”ํŠธ๋ฆญ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋‚ด๋ณด๋‚ด๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค
provider = MeterProvider(metric_readers=[reader])
set_meter_provider(provider)

# get_meter ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ๋ฉ”ํŠธ๋ฆญ์„ ์ƒ์„ฑํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ Meter ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
# Meter๋Š” ๋ฉ”ํŠธ๋ฆญ์˜ ์ธก์ •(์ฆ‰, ๋ฐ์ดํ„ฐ ํฌ์ธํŠธ ์ƒ์„ฑ)์„ ์ฑ…์ž„์ง€๋Š” ๊ฐ์ฒด์ด๋‹ค.
meter = get_meter_provider().get_meter("preferred-aggregation", "0.1.2")

counter = meter.create_counter("my-counter")

for x in range(10):
    counter.add(x)
    time.sleep(2.0)

์–ดํœด ๊ทธ๋Ÿฐ๋ฐ, ์ƒ๊ฐ๋ณด๋‹ค metric์ด ๋ณต์žกํ•˜๊ณ  ์–ด๋ ต๋‹ค.
๋” ๋“ค์–ด๊ฐ€๊ธฐ์ „์— ์šฉ์–ด์™€ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ํ™•์ธํ•˜๊ณ  ๊ฐ€๋Š”๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.

OpenTelemetry ๋ฉ”ํŠธ๋ฆญ ์šฉ์–ด ์ •๋ฆฌ

  [MetricProvider] # ๋ฉ”ํŠธ๋ฆญ ์‹œ์Šคํ…œ์˜ ์‹œ์ž‘์ ์œผ๋กœ, ๋ฉ”ํŠธ๋ฆญ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘์„ ์กฐ์ •ํ•˜๊ณ  Meter ์ธ์Šคํ„ด์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
         ↓
      [Meter] # ๋ฉ”ํŠธ๋ฆญ ์ธ์ŠคํŠธ๋ฃจ๋จผํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•œ๋‹ค. (์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์—์„œ ๋ฉ”ํŠธ๋ฆญ์„ ์ •์˜ํ•  ๋•Œ ์‚ฌ์šฉ)
         ↓
   [Instrument] # ๊ฐœ๋ณ„ ์ธก์ • ์ž‘์—…์„ ์‹คํ–‰ํ•˜๋Š” ๊ฐ์ฒด (์นด์šดํ„ฐ, ๊ฒŒ์ด์ง€ ๋“ฑ)
         ↓
   [Measurement] # ์‹ค์ œ ์ธก์ •๋œ ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฐ์ดํ„ฐ ํฌ์ธํŠธ, ์ธ์ŠคํŠธ๋ฃจ๋จผํŠธ์—์„œ ๋ฉ”ํŠธ๋ฆญ ๊ฐ’์„ ์ธก์ •ํ•  ๋•Œ ์ƒ์„ฑ๋จ
         ↓
  [MetricReader] # ์ˆ˜์ง‘๋œ ๋ฉ”ํŠธ๋ฆญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ค€๋น„ํ•˜์—ฌ ๋ฉ”ํŠธ๋ฆญ ์ต์Šคํฌํ„ฐ๋กœ ์ „๋‹ฌ
         ↓
  [MetricExporter] # ์ฒ˜๋ฆฌ๋œ ๋ฉ”ํŠธ๋ฆญ ๋ฐ์ดํ„ฐ๋ฅผ ์™ธ๋ถ€ ์‹œ์Šคํ…œ์œผ๋กœ ์ „์†ก
         ↓
 [External System]
  • Meter:
    • ๋ฉ”ํŠธ๋ฆญ ๋ฐ์ดํ„ฐ(์ธก์ •๊ฐ’)๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
    • ๋‹ค์–‘ํ•œ ์œ ํ˜•์˜ ๋ฉ”ํŠธ๋ฆญ Instrument๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  • Instrument:
    • ์ธก์ •์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฐ์ฒด (์˜ˆ Counter, ObservableGauge ๋“ฑ)
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํŠน์ • ๋ถ€๋ถ„์—์„œ ์ธก์ •์„ ์ˆ˜์ง‘ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋œ๋‹ค.
  • Measurement:
    • ์‹ค์ œ๋กœ ์ธก์ •๋œ ๋ฐ์ดํ„ฐ ํฌ์ธํŠธ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.
    • ๋ฉ”ํŠธ๋ฆญ ์ธ์ŠคํŠธ๋ฃจ๋จผํŠธ๊ฐ€ ๋ฉ”ํŠธ๋ฆญ ๊ฐ’์„ ์ˆ˜์ง‘ํ•  ๋•Œ๋งˆ๋‹ค ์ธก์ •์ด ์ƒ์„ฑ๋œ๋‹ค.
  • MetricReader:
    • ์ˆ˜์ง‘๋œ ๋ฉ”ํŠธ๋ฆญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ตœ์ข…์ ์œผ๋กœ ๋ฉ”ํŠธ๋ฆญ ์ต์Šคํฌํ„ฐ๋กœ ์ „๋‹ฌํ•œ๋‹ค.
    • ๋ฉ”ํŠธ๋ฆญ์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ์ˆ˜์ง‘ํ•˜๊ณ , ํ•„์š”์— ๋”ฐ๋ผ ์ฆ‰์‹œ ๋‚ด๋ณด๋‚ด๊ฑฐ๋‚˜ ์ข…๋ฃŒ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

6.4.2 ๋ฉ”ํŠธ๋ฆญ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑ

์ด ๋ถ€๋ถ„๋„..... ๋‚ด์ผ์˜ ๋‚˜์—๊ฒŒ ๋ถ€ํƒํ•œ๋‹ค..

6.5. ๋กœ๊ทธ

์ฑ…์—์„œ ๋‹ค๋ฃจ๋Š” ๋‚ด์šฉ์€ ๊ณต์‹๋ฌธ์„œ์—์„œ ๊ฑฐ์˜ ์ฐพ์•„๋ณผ ์ˆ˜์—†์–ด, ๋ณ„๋„๋กœ ์•„๋ž˜ ๊ณต์‹Docs๋ฅผ ํ†ตํ•ด ๋‚ด์šฉ์„ ์ •๋ฆฌํ–ˆ๋‹ค (์ฑ…๋‚ด์šฉ ๐Ÿ™…๐Ÿป‍โ™€๏ธ์•„๋‹˜๐Ÿ™…๐Ÿป‍โ™€๏ธ)

* ์ฐธ๊ณ ๋งํฌ: https://opentelemetry.io/docs/specs/otel/logs/

๋ฉ”ํŠธ๋ฆญ(metrics)๊ณผ ํŠธ๋ ˆ์ด์Šค(traces)์— ๋Œ€ํ•ด์„œ๋Š” ์ƒˆ๋กœ์šด API๋ฅผ ์ง€์ •ํ•˜๊ณ , ์—ฌ๋Ÿฌ ์–ธ์–ด์—์„œ ์ด API์˜ ์ „์ฒด ๊ตฌํ˜„์„ ์ œ๊ณตํ•˜๋Š” '์ฒญ์‚ฌ์ง„ ์„ค๊ณ„(clean-sheet design)' ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ทจํ•˜๊ณ  ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋กœ๊ทธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์— ๋‚ด์žฅ๋œ ๋กœ๊น… ๊ธฐ๋Šฅ์ด ์žˆ๊ณ , ๋„๋ฆฌ ์‚ฌ์šฉํ•˜๋Š” ๋กœ๊น… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๊ธฐ๋•Œ๋ฌธ์—, Metric, Trace์™€ ๋‹ค๋ฅด๊ฒŒ ๊ธฐ์กด ๊ธฐ์ˆ ๊ณผ์˜ ํ˜ธํ™˜์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ, ๊ธฐ์กด ๋กœ๊ทธ ๋ฉ”์„ธ์ง€ ํ˜•์‹์— ์ถ”์  ๋“ฑ์˜ ์ƒ๊ด€๊ด€๊ณ„ ์ •๋ณด์™€ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•˜๋„๋ก ๊ฐœ๋ฐœ๋˜์—ˆ๋‹ค.

๋กœ๊ทธ์— ๋Œ€ํ•œ OpenTelemetry์˜ ์ ‘๊ทผ๋ฐฉ์‹

  • ๋กœ๊ทธ๋ฐ์ดํ„ฐ ๋ชจ๋ธ ์ •์˜ (์ฐธ๊ณ ): ๋กœ๊ทธ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ์„ ์ •์˜ํ•˜์—ฌ LogRecord๊ฐ€ ๋ฌด์—‡์ธ์ง€, ์–ด๋–ค ๋ฐ์ดํ„ฐ๊ฐ€ ๊ธฐ๋ก, ์ „์†ก, ์ €์žฅ ๋ฐ ํ•ด์„๋˜์–ด์•ผ ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ๊ทœ์น™์„ ์ •์˜ํ–ˆ๋‹ค.
  • ๋กœ๊ทธ ๋ธŒ๋ฆฟ์ง€ API(์ฐธ๊ณ ): LogRecords๋ฅผ ๋ฐœํ–‰ํ•˜๊ธฐ ์œ„ํ•œ Logs Bridge API๋ฅผ ์ •์˜ํ–ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์ž๋Š” ์ด API๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋˜์ง€ ์•Š์œผ๋ฉฐ, ์ด๋Š” ๊ธฐ์กด ๋กœ๊น… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ OpenTelemetry ๋กœ๊ทธ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ ๊ฐ„์˜ ๋‹ค๋ฆฌ ์—ญํ• ์„ ํ•˜๋Š” ๋กœ๊ทธ ์–ดํŽœ๋”๋ฅผ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ €์ž๋“ค์—๊ฒŒ ์ œ๊ณต๋œ๋‹ค.
  • ๋ธŒ๋ฆฌ์ง€ API์˜ SDK ๊ตฌํ˜„(์ฐธ๊ณ ): ๋ธŒ๋ฆฌ์ง€ API์˜ SDK ๊ตฌํ˜„์„ ์ •์˜ํ•˜์—ฌ LogRecords์˜ ์ฒ˜๋ฆฌ ๋ฐ ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ƒˆ๋กœ์šด ๋กœ๊ทธ ์‹œ์Šคํ…œ(์ฐธ๊ณ ): ์ƒˆ๋กญ๊ฒŒ ์„ค๊ณ„๋œ ๋กœ๊ทธ ์‹œ์Šคํ…œ์€ OpenTelemetry์˜ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ์— ๋”ฐ๋ผ ๋กœ๊ทธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  • ๊ธฐ์กด ๋กœ๊ทธ ํฌ๋งท ํ˜ธํ™˜์„ฑ: ๊ธฐ์กด ๋กœ๊ทธ ํฌ๋งท์€ OpenTelemetry ๋กœ๊ทธ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ๋กœ ๋ช…ํ™•ํ•˜๊ฒŒ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๋‹ค. OpenTelemetry Collector๋ฅผ ํ†ตํ•ด OpenTelemetry ๋กœ๊ทธ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜์žˆ๋‹ค.

OpenTelemetry ๋กœ๊ทธ ์†Œ์Šค ์œ ํ˜•

๋กœ๊ทธ ์†Œ์Šค ์œ ํ˜•์„ ๋‚˜๋ˆ„๊ณ  ์ •์˜ํ•จ์œผ๋กœ์จ, ๋กœ๊ทธ์˜ ์ˆ˜์ง‘ ๋ฐ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์— ์žˆ์–ด์„œ ๊ตฌ์ฒด์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ œ์‹œํ•˜๊ณ  ์žˆ๋‹ค.

  • System Logs (์‹œ์Šคํ…œ ๋กœ๊ทธ):
    • ์šด์˜ ์ฒด์ œ๊ฐ€ ์ƒ์„ฑํ•˜๋Š” ๋กœ๊ทธ๋กœ, ๋กœ๊ทธ์˜ ํ˜•์‹์ด๋‚˜ ํฌํ•จ๋˜๋Š” ์ •๋ณด๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค. (syslog, windoes event logs..)
    • OpenTelemetry Collector๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ ๋กœ๊ทธ๋Š” ์ž๋™์œผ๋กœ ๋ฆฌ์†Œ์Šค ์ปจํ…์ŠคํŠธ(์˜ˆ: ํ˜ธ์ŠคํŠธ ์ด๋ฆ„, IP ์ฃผ์†Œ)๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์ˆ˜์ง‘ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Infrastructure Logs (์ธํ”„๋ผ ๋กœ๊ทธ):
    • ์ธํ”„๋ผ ๋กœ๊ทธ๋Š” ๋„คํŠธ์›Œํฌ ์žฅ๋น„, ๋ฐ์ดํ„ฐ ์„ผํ„ฐ, ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค, Kubernetes ์ด๋ฒคํŠธ ์™€ ๊ฐ™์€ IT ๋‹ค์–‘ํ•œ ์ธํ”„๋ผ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์ƒ์„ฑ๋˜๋Š” ๋กœ๊ทธ๋ฅผ ๋งํ•œ๋‹ค.
    • OpenTelemetry Collector๋ฅผ ํ†ตํ•ด ๋…ธ๋“œ, ํฌํŠธ, ์ปจํ…Œ์ด๋„ˆ ๋“ฑ์˜ ๋ฆฌ์†Œ์Šค ์ปจํ…์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์ˆ˜์ง‘ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Third-party Application Logs (์ œ3์ž ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๊ทธ):
    • ์„œ๋“œํŒŒํ‹ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๊ทธ๋Š” ์ž˜ ์•Œ๋ ค์ง„ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜, ์˜ˆ๋ฅผ๋“ค์–ด mysql๊ณผ ๊ฐ™์€ ์†”๋ฃจ์…˜์„ ์‚ฌ์šฉํ–ˆ์„๋•Œ์˜ ๋กœ๊ทธ๋ฅผ ๋งํ•œ๋‹ค.
    • OpenTelemetry๋Š” OpenTelemetry Collector์˜ filelog receiver ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, FluentBit๊ณผ ๊ฐ™์€ ๋กœ๊ทธ ์ˆ˜์ง‘ ์—์ด์ „ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์‹œ OpenTelemetry Collector๋กœ ์ „์†กํ•˜์—ฌ ๋กœ๊ทธ๋ฅผ ์ถ”๊ฐ€๋กœ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.
  • Legacy First-Party Applications Logs (๋ ˆ๊ฑฐ์‹œ ์ฒซ ๋ฒˆ์งธ ํŒŒํ‹ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๊ทธ):
    • ์‚ฌ๋‚ด์—์„œ ๊ฐœ๋ฐœ๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋กœ๊ทธ๋ฅผ ๋งํ•œ๋‹ค.
    • OpenTelemetry๋Š” ๋กœ๊ทธ๋ฅผ ์ˆ˜์ง‘ํ• ๋•Œ ์ผ๋ฐ˜ ํ…์ŠคํŠธ๊ฐ€ ์•„๋‹Œ json๊ณผ ๊ฐ™์€ ๊ตฌ์กฐํ™”๋œ ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅํ•˜๋„๋ก ์–ดํ”Œ๋ ˆ์ผ€์ด์…˜์˜ ๋กœ๊ทธ ํฌ๋ฉงํ„ฐ๋ฅผ ์žฌ๊ตฌ์„ฑํ•˜๊ณ  (๋กœ๊ทธ์ˆ˜์ง‘ ์‹ ๋ขฐ์„ฑ ํ–ฅ์ƒ), ๋กœ๊ทธ๋งˆ๋‹ค TraceID, SpanID๋ฅผ ์ถ”๊ฐ€ํ•˜๋„๋ก ๊ถŒ์žฅํ•œ๋‹ค.
    • ์ผ๋ถ€ ๋กœ๊น… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” log appender ํ˜น์€ log bridge๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ ๋ ˆ์ฝ”๋“œ์— ์ถ”๊ฐ€์ •๋ณด(TraceID, SpenID)๋ฅผ ์ถ”๊ฐ€ํ•˜๋„๋ก ๊ถŒ์žฅํ•œ๋‹ค.
  • Via File or Stdout Logs (ํŒŒ์ผ์ด๋‚˜ ํ‘œ์ค€ ์ถœ๋ ฅ์„ ํ†ตํ•œ ๋กœ๊ทธ):
    • ๋กœ๊ทธ๊ฐ€ ํŒŒ์ผ ๋˜๋Š” ํ‘œ์ค€ ์ถœ๋ ฅ์œผ๋กœ ์ž‘์„ฑ๋˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋งํ•œ๋‹ค.
    • OpenTelemetry๋Š” OpenTelemetry Collector์˜ filelog receiver ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, FluentBit๊ณผ ๊ฐ™์€ ๋กœ๊ทธ ์ˆ˜์ง‘ ์—์ด์ „ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์‹œ OpenTelemetry Collector๋กœ ์ „์†กํ•˜์—ฌ ๋กœ๊ทธ๋ฅผ ์ถ”๊ฐ€๋กœ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.
  • Direct to Collector (์ง์ ‘ ์ปฌ๋ ‰ํ„ฐ๋กœ ์ „์†ก):
    • ๋กœ๊ทธ๋ฅผ OpenTelemetry Protocol์„ ํ†ตํ•ด ์ถœ๋ ฅ๋˜๋„๋ก ์ง์ ‘ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ˆ˜์ •ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ์„ ๋งํ•œ๋‹ค.
    • ์žฅ์ : ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์ผ์ด๋‚˜ ๋‹ค๋ฅธ ์ค‘๊ฐ„ ์ €์žฅ์†Œ๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ , ์ง์ ‘ OpenTelemetry Collector ๊ฐ™์€ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ์‹œ์Šคํ…œ์œผ๋กœ ์ „์†กํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ด ๋ฐฉ์‹์€ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์˜ ์ง€์—ฐ์„ ์ตœ์†Œํ™”ํ•˜๊ณ  ํšจ์œจ์„ ๋†’์ผ ์ˆ˜ ์žˆ๋‹ค.
    • ๋‹จ์ : ๋กœ์ปฌ ํŒŒ์ผ๋กœ ๋กœ๊ทธ๋ฅผ ๋ณด์œ ํ•˜๋Š” ๊ฐ„๋‹จํ•จ์ด ์‚ฌ๋ผ์ง€๋ฉฐ, ๋กœ๊ทธ๋ฅผ ์ˆ˜์‹ ํ•  ๋ชฉ์ ์ง€๊ฐ€ ๋กœ๊ทธ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์„๋•Œ๋งŒ ์ž‘๋™ํ•œ๋‹ค.
    • ๋กœ์ปฌํŒŒ์ผ์—๋„ ๋กœ๊ทธ๋ฅผ ์ ์žฌํ•˜๊ณ , OTLP๋กœ ์ปฌ๋ ‰ํ„ฐ๋กœ ์ง์ ‘ ์ „์†ก ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด Bridge API ์™€ SDK๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • New First-Party Application Logs (์ƒˆ ์ฒซ ๋ฒˆ์งธ ํŒŒํ‹ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๊ทธ):
    • ์ƒˆ๋กœ ๊ฐœ๋ฐœ๋˜๊ณ ์žˆ๋Š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์–ด๋–ป๊ฒŒ ๋กœ๊ทธ๋ฅผ ๋ณด๋‚ด๋Š”๊ฒƒ์ด ์ข‹์€์ง€์— ๋Œ€ํ•œ ๋‚ด์šฉ์ด๋‹ค.
    • ๋กœ๊ทธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ธฐ์กด์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๊ณ , Bridge API ์™€ SDK๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ์— TraceID ๋“ฑ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

https://opentelemetry.io/docs/specs/otel/logs/#legacy-and-modern-log-sources

Log ์ฝ”๋“œ ํ…Œ์ŠคํŠธ ํ•ด๋ณด๊ธฐ

์ƒ˜ํ”Œ FastAPI ์ฝ”๋“œ

main.py๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌํ•œ๋‹ค.

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    return {"Hello": "World"}

์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

$ pip install fastapi uvicorn
$ uvicorn main:app --reload

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋˜๋Š”์ง€ http://127.0.0.1:8000์— ์ ‘์†ํ•ด์„œ ํ™•์ธํ•ด๋ณด์ž

์ƒ˜ํ”Œ FastAPI ์ฝ”๋“œ + Trace์ถ”๊ฐ€

์œ„ ์ฝ”๋“œ์— Trace๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

์•„๊นŒ ์ƒ์„ฑํ•œ main.py์— ๋‚ด์šฉ์„ ์‚ญ์ œํ•˜๊ณ  ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ ๋ถ™์—ฌ๋„ฃ์ž.

from fastapi import FastAPI
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor

app = FastAPI()

# OpenTelemetry ํŠธ๋ ˆ์ด์„œ ์„ค์ •
trace.set_tracer_provider(
    TracerProvider(
        resource=Resource.create({SERVICE_NAME: "simple-fastapi-service"})
    )
)
tracer = trace.get_tracer(__name__)

# OTLP๋กœ ํŠธ๋ ˆ์ด์Šค ๋ฐ์ดํ„ฐ๋ฅผ localhost๋กœ ์ „์†ก
otlp_exporter = OTLPSpanExporter(endpoint="localhost:4317", insecure=True)
span_processor = BatchSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)

# FastAPI ์•ฑ์— OpenTelemetry ๊ตฌ์„ฑ
FastAPIInstrumentor.instrument_app(app)

@app.get("/")
def read_root():
    with tracer.start_as_current_span("root_span"):
        return {"Hello": "World"}

์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

$ pip install fastapi uvicorn opentelemetry-api opentelemetry-sdk opentelemetry-instrumentation-fastapi opentelemetry-exporter-otlp
$ uvicorn main:app --reload

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋˜๋Š”์ง€ http://127.0.0.1:8000์— ์ ‘์†ํ•ด์„œ ํ™•์ธํ•ด๋ณด์ž

๋ฐฑ์—”๋“œ๋ฅผ ์„ค์ •ํ•ด๋‘์—ˆ๋‹ค๋ฉด trace๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ฐํ˜€ ๋ณด์ผ๊ฒƒ์ด๋‹ค.

์ƒ˜ํ”Œ FastAPI ์ฝ”๋“œ + Trace + Log์ถ”๊ฐ€

์ด์ œ ์œ„ ์ฝ”๋“œ์— ๋กœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

์•„๊นŒ ์ƒ์„ฑํ•œ main.py์— ๋‚ด์šฉ์„ ์‚ญ์ œํ•˜๊ณ  ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ ๋ถ™์—ฌ๋„ฃ์ž.

from fastapi import FastAPI
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.logging import LoggingInstrumentor
import logging

app = FastAPI()

# OpenTelemetry ํŠธ๋ ˆ์ด์„œ ์„ค์ •
trace.set_tracer_provider(
    TracerProvider(
        resource=Resource.create({SERVICE_NAME: "simple-fastapi-service"})
    )
)
tracer = trace.get_tracer(__name__)

# OTLP๋กœ ํŠธ๋ ˆ์ด์Šค ๋ฐ์ดํ„ฐ๋ฅผ localhost๋กœ ์ „์†ก
otlp_exporter = OTLPSpanExporter(endpoint="localhost:4317", insecure=True)
span_processor = BatchSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)

# FastAPI ์•ฑ์— OpenTelemetry ๊ตฌ์„ฑ
FastAPIInstrumentor.instrument_app(app)

# ๋กœ๊น… ๊ตฌ์„ฑ
LoggingInstrumentor().instrument(set_logging_format=True)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

@app.get("/")
def read_root():
    with tracer.start_as_current_span("root_span"):
        logging.info("Handling root request")
        return {"Hello": "World"}

์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

$ pip install opentelemetry-instrumentation-logging
$ uvicorn main:app --reload

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋˜๋Š”์ง€ http://127.0.0.1:8000์— ์ ‘์†ํ•ด์„œ ํ™•์ธํ•ด๋ณด์ž

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ํ›„ ์›น๋ธŒ๋ผ์šฐ์ €์—์„œ http://127.0.0.1:8000 ์ ‘์†ํ•˜๋ฉด TraceID ํ„ฐ๋ฏธ๋„์— ์ถœ๋ ฅ๋จ ํ™•์ธ
๋ฐฑ์—”๋“œ(tempo)์—์„œ TraceID๊ฐ€ ๊ฒ€์ƒ‰๋˜๋Š”์ง€ ํ™•์ธ

์„ค์ •ํ•œ๋Œ€๋กœ ๋กœ๊ทธ์— ๋‚˜์˜จ TraceID๋ฅผ ํ™•์ธํ• ์ˆ˜ ์žˆ์—ˆ๋‹ค.

6.6 ์ปฌ๋ ‰ํ„ฐ

์˜คํ”ˆํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์ปฌ๋ ‰ํ„ฐ(OpenTelemetry Collector) ๊ฐœ์š”

์ปฌ๋ ‰ํ„ฐ๋Š” ๋‹ค์–‘ํ•œ ์†Œ์Šค(์• ํ”Œ๋ฆฌ์ผ€์ด์…˜, ์‹œ์Šคํ…œ, ์ธํ”„๋ผ ๋“ฑ)์—์„œ ๋กœ๊ทธ, ๋ฉ”ํŠธ๋ฆญ, ํŠธ๋ ˆ์ด์Šค ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘, ๋ณ€ํ™˜ ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•œ ํ›„ ์—ฌ๋Ÿฌ ๋ฐฑ์—”๋“œ(์˜ˆ: ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ, ๋กœ๊ทธ ๋ถ„์„ ์„œ๋น„์Šค)๋กœ ์ „์†กํ•œ๋‹ค.

OpenTelemetry๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ฑฐ๋‚˜ ์†Œ๊ทœ๋ชจ ํ™˜๊ฒฝ์—์„œ๋Š” Collector์—†์ด ๋ฐ”๋กœ ๋ฐฑ์—”๋“œ๋กœ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์ง€๋งŒ,
์ผ๋ฐ˜์ ์œผ๋กœ๋Š” Collector๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.
Collector์—์„œ ์žฌ์‹œ๋„, ์ผ๊ด„์ฒ˜๋ฆฌ, ์•”ํ˜ธํ™”, ๋ฏผ๊ฐ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง ๋“ฑ ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์„ค์น˜ ๋ฐฉ๋ฒ•

์„ค์น˜ ๋ฐฉ๋ฒ•๋„ ๊ฐ„๋‹จํ•˜๋‹ค.
docker ์ปจํ…Œ์ด๋„ˆ๋กœ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜, ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฐธ๊ณ 

Collector ์ ์šฉ ํŒจํ„ด

  1. No Collector (๋งํฌ)
    • ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฐ”๋กœ ๋ฐฑ์—”๋“œ๋กœ ๋ณด๋‚ด๋Š” ๊ตฌ์„ฑ์„ ๋งํ•œ๋‹ค.
    • ์œ„์— ๋‚˜์™”๋˜ ์ƒ˜ํ”Œ์ฝ”๋“œ๋“ค์€ Metric, Log, Trace๋“ค์„ OTLP๋ฅผ ์ด์šฉํ•ด์„œ ๋ฐ”๋กœ ๋ฐฑ์—”๋“œ๋กœ ๋ณด๋ƒˆ๋‹ค.
    • No Collector ํŒจํ„ด์€ ์‚ฌ์šฉํ•˜๊ธฐ ๊ฐ„ํŽธํ•˜๊ณ , ์ถ”๊ฐ€ ์„ค์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์ง€๋งŒ, ์ˆ˜์ง‘, ์ฒ˜๋ฆฌ,๋‚ด๋ณด๋‚ด๊ธฐ ์—”๋“œํฌ์ธํŠธ ๋“ฑ ์ˆ˜์ •์ด ํ•„์š”ํ•œ๊ฒฝ์šฐ ์ฝ”๋“œ์— ์ˆ˜์ •์ด ํ•„์š”ํ•˜๋‹ค.
    • ๋˜ํ•œ ๋‚ด๋ณด๋‚ด๊ธฐ ์ˆ˜๊ฐ€ ์ œํ•œ๋˜์–ด์žˆ์–ด ์ œํ•œ ์‚ฌํ•ญ์„ ํ™•์ธํ•ด์•ผํ•˜๋ฉฐ, ์žฌ์‹œ๋„ ๋“ฑ์— ๋Œ€ํ•ด ์ง์ ‘ ๊ณ ๋ฏผํ•ด์•ผํ•œ๋‹ค.
    • ์ผ๋ฐ˜์ ์œผ๋กœ No Collector ๊ตฌ์„ฑ์€ ํ…Œ์ŠคํŠธํ™˜๊ฒฝ์ด๊ฑฐ๋‚˜ ์•„์ฃผ ์†Œ๊ทœ๋ชจ์ผ๋•Œ๋งŒ ์‚ฌ์šฉํ•˜๋„๋ก ๊ถŒ์žฅํ•œ๋‹ค.
  2. Agent (๋งํฌ)
    • ์„œ๋ฒ„๋‹น ํ•˜๋‚˜์˜ Collector๋ฅผ ๊ตฌ์„ฑํ•˜๊ฑฐ๋‚˜, ์‚ฌ์ด๋“œ์นด ์ปจํ…Œ์ด๋„ˆ๋กœ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐฉ์‹์„ ๋งํ•œ๋‹ค.
    • ๊ตฌ์„ฑ์ด ๊ฐ„๋‹จํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์œผ๋‚˜, ํ™•์žฅ์„ฑ ๋ฌธ์ œ(์„œ๋ฒ„์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ• ์ˆ˜๋ก ์ปฌ๋ ‰ํ„ฐ๋„ ๋งŽ์•„์ง)๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ณ , ์œ ์—ฐ์„ฑ์ด ๋ถ€์กฑํ•˜๋‹ค.
  3. Gateway (๋งํฌ)
    • Collector ์„œ๋ฒ„๋ฅผ ๋”ฐ๋กœ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋งํ•œ๋‹ค. (Collector ์„œ๋ฒ„๊ตฌ์„ฑ)
    • ์ค‘์•™์ง‘์ค‘ํ™”๋œ ์ปฌ๋ ‰ํ„ฐ ์„œ๋ฒ„๊ทธ๋ฃน์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ, ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๋ฐ ๊ด€๋ฆฌ๋ฅผ ์ค‘์•™์—์„œ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์žฅ์ ์œผ๋กœ๋Š” ๋ณด์•ˆ์ •์ฑ…์ด๋‚˜ ์„ค์ • ๋“ฑ์„ ์ค‘์•™์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ฐœ๋ณ„ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ •์— ๋Œ€ํ•ด ์‹ ๊ฒฝ์“ธ ํ•„์š”๊ฐ€ ์—†๋‹ค.
    • ๋‹จ์ ์œผ๋กœ๋Š” ์‹œ์Šคํ…œ์˜ ๋ณต์žก์„ฑ์„ ์ฆ๊ฐ€์‹œํ‚ค๊ณ , ์ถ”๊ฐ€์ ์ธ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ๋˜ํ•œ,  ์ค‘์•™ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ์‹คํŒจํ•  ๊ฒฝ์šฐ ์‹œ์Šคํ…œ ์ „์ฒด์˜ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋‹จ์ผ์‹คํŒจ์ง€์ ์ด ๋  ์ˆ˜ ์žˆ๋‹ค.
    • ๋˜ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—ฌ๋Ÿฌ๋‹จ๊ณ„์— ๊ฑธ์ณ ์ˆ˜์ง‘๋˜๊ธฐ๋•Œ๋ฌธ์— ์ฒ˜๋ฆฌ ์ง€์—ฐ์‹œ๊ฐ„์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋„, ์ƒ๋‹นํ•œ ์–‘์˜ ์ปดํ“จํŒ… ๋ฆฌ์†Œ์Šค๊ฐ€ ์ถ”๊ฐ€๋˜์–ด ์šด์˜๋น„์šฉ์˜ ์ฆ๊ฐ€๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

Collector ๊ตฌ์„ฑ์š”์†Œ

Receivers (๋งํฌ)

  • ๋ฐ์ดํ„ฐ๊ฐ€ Collector๋กœ ๋“ค์–ด๊ฐ€๋Š” ๋ฐฉ๋ฒ•์„ ์ •์˜ํ•œ๋‹ค.
  • Receiver๋Š” ์ง€์ •๋œ ํ˜•์‹์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ ๋‚ด๋ถ€ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•œ ํ›„ ํ•ด๋‹น ํŒŒ์ดํ”„๋ผ์ธ์— ์ •์˜๋œ Processer ์™€ Exporter์— ์ „๋‹ฌํ•œ๋‹ค.
  • Collector์—์„œ๋Š” ํ•˜๋‚˜์ด์ƒ์˜ Receiver๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
  • Collector๋Š” ๊ตฌ์„ฑํ•˜๋”๋ผ๋„ ๋ฐ”๋กœ ํ™œ์„ฑํ™” ๋˜์ง€ ์•Š๋Š”๋‹ค. Service ์„น์…˜ ๋‚ด์˜ ์ ์ ˆํ•œ ํŒŒ์ดํ”„๋ผ์ธ์— ์ถ”๊ฐ€ํ•ด์•ผ ํ™œ์„ฑํ™” ๋œ๋‹ค.
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

Processors (๋งํฌ)

  • Processer๋Š” Receivers์—์„œ ์ˆ˜์ง‘๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ๋ณ€ํ™˜ํ•œ ํ›„ Exporter์— ์ „๋‹ฌํ•œ๋‹ค.
  • ํ•„ํ„ฐ๋ง, ์‚ญ์ œ, ์ด๋ฆ„๋ฐ”๊พธ๊ธฐ, ์žฌ๊ณ„์‚ฐ, ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ถ”๊ฐ€, ์ƒ˜ํ”Œ๋ง ๋“ฑ์˜ ์ž‘์—…์ด ํฌํ•จ๋˜๋ฉฐ ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ˆœ์„œ์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ž‘์—…์˜ ์ˆœ์„œ๊ฐ€ ๊ฒฐ์ •๋œ๋‹ค.
  • Processer๋ฅผ ๊ตฌ์„ฑํ•˜๋”๋ผ๋„ ๋ฐ”๋กœ ํ™œ์„ฑํ™” ๋˜์ง€ ์•Š๋Š”๋‹ค. Service ์„น์…˜ ๋‚ด์˜ ์ ์ ˆํ•œ ํŒŒ์ดํ”„๋ผ์ธ์— ์ถ”๊ฐ€ํ•ด์•ผ ํ™œ์„ฑํ™” ๋œ๋‹ค.
processors:
  attributes:
    actions:
      - key: environment
        value: "production"
        action: insert
      - key: service_name
        value: "example-service"
        action: insert

Exporters (๋งํฌ)

  • Exporter๋Š” ํ•˜๋‚˜์ด์ƒ์˜ ๋ฐฑ์—”๋“œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ณด๋‚ธ๋‹ค.
  • Exporter๋Š” pull ๋˜๋Š” Push ๊ธฐ๋ฐ˜์ผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํ•˜๋‚˜ ์ด์ƒ์˜ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Exporter๋Š” ๊ตฌ์„ฑํ•˜๋”๋ผ๋„ ๋ฐ”๋กœ ํ™œ์„ฑํ™” ๋˜์ง€ ์•Š๋Š”๋‹ค. Service ์„น์…˜ ๋‚ด์˜ ์ ์ ˆํ•œ ํŒŒ์ดํ”„๋ผ์ธ์— ์ถ”๊ฐ€ํ•ด์•ผ ํ™œ์„ฑํ™” ๋œ๋‹ค.
exporters:
  loki:
    endpoint: "http://loki:3100"
  otlp:
    endpoint: "http://tempo:4317"
    protocol: grpc
  prometheusremotewrite:
    endpoint: "http://mimir:9009/api/prom/push"

 

Service

  • Service ์„น์…˜์—์„œ๋Š” Receiver,Processer,Exporter๋ฅผ ํ•˜๋‚˜์˜ ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ์—ฐ๊ฒฐํ•œ๋‹ค.
  • ๋กœ๊ทธ, ๋ฉ”ํŠธ๋ฆญ, ํŠธ๋ ˆ์ด์Šค ๊ฐ๊ฐ์— ๋Œ€ํ•ด ๋ณ„๋„์˜ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•œ๋‹ค.
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [attributes]
      exporters: [otlp]
    metrics:
      receivers: [otlp]
      processors: [attributes]
      exporters: [prometheusremotewrite]
    logs:
      receivers: [otlp]
      processors: [attributes]
      exporters: [loki]

Connectors (๋งํฌ)

  • Connector๋Š” ํŠน์ • ์œ ํ˜•์˜ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์™€ ํ๋ฆ„ ์ œ์–ด๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ์ด๋‹ค.
  • ๋‘ ํŒŒ์ดํ”„๋ผ์ธ์„ ์—ฐ๊ฒฐํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋ฉฐ, ํ•œ ํŒŒ์ดํ”„๋ผ์ธ์˜ ๋์—์„œ ๋ฐ์ดํ„ฐ Export ์—ญํ• ๊ณผ ๋‹ค๋ฅธ ํŒŒ์ดํ”„๋ผ์ธ์˜ ์‹œ์ž‘์—์„œ ๋ฐ์ดํ„ฐ Reciver ์—ญํ• ์„ ๊ฒธํ•œ๋‹ค.
  • ๋ฐ์ดํ„ฐ์˜ ์œ ํ˜•์ด ๋™์ผํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋Š” ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์„ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
  •  
# count ์ปค๋„ฅํ„ฐ๊ฐ€ ์‚ฌ์šฉ๋˜์–ด ํŠธ๋ ˆ์ด์Šค ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ , 
#ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”ํŠธ๋ฆญ ๋ฐ์ดํ„ฐ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋‹ค๋ฅธ ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ์ „์†ก

receivers:
  foo:

exporters:
  bar:

connectors:
  count:
    spanevents:
      my.prod.event.count:
        description: The number of span events from my prod environment.
        conditions:
          - 'attributes["env"] == "prod"'
          - 'name == "prodevent"'

service:
  pipelines:
    traces:
      receivers: [foo]
      exporters: [count]
    metrics:
      receivers: [count]
      exporters: [bar]

6.7 ์˜คํ”ˆํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ๋ฐ๋ชจ