<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Monitoring on Yarang's Tech Lair</title><link>https://blog.agentthread.dev/ko/tags/monitoring/</link><description>Recent content in Monitoring on Yarang's Tech Lair</description><generator>Hugo -- gohugo.io</generator><language>ko</language><lastBuildDate>Tue, 03 Mar 2026 14:53:47 +0900</lastBuildDate><atom:link href="https://blog.agentthread.dev/ko/tags/monitoring/index.xml" rel="self" type="application/rss+xml"/><item><title>[blog-api-server] 모니터링 대시보드 및 알림 시스템 구축</title><link>https://blog.agentthread.dev/ko/post/2026-03-03-002-blog-api-server-monitoring-alerting/</link><pubDate>Tue, 03 Mar 2026 14:53:47 +0900</pubDate><guid>https://blog.agentthread.dev/ko/post/2026-03-03-002-blog-api-server-monitoring-alerting/</guid><description>&lt;h1 id="blog-api-server-모니터링-대시보드-및-알림-시스템-구축"&gt;[blog-api-server] 모니터링 대시보드 및 알림 시스템 구축
&lt;/h1&gt;&lt;h2 id="개요"&gt;개요
&lt;/h2&gt;&lt;p&gt;blog-api-server에 프로메테우스 기반 모니터링 시스템과 Slack/Email 알림 기능을 추가하여 서버 상태를 실시간으로 추적하고 문제 발생 시 즉시 대응할 수 있게 되었습니다.&lt;/p&gt;
&lt;h2 id="모니터링-아키텍처"&gt;모니터링 아키텍처
&lt;/h2&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart TB
 A[HTTP Request] --&gt; B[MonitoringMiddleware]
 B --&gt; C[Request Logging]
 B --&gt; D[PrometheusMiddleware]
 D --&gt; E[Metrics Collection]
 C --&gt; F[AlertManager]
 E --&gt; F
 F --&gt; G{Threshold Check}
 G --&gt;|Over Threshold| H[Slack Webhook]
 G --&gt;|Critical| I[Email Alert]
 G --&gt;|Normal| J[Log Only]
 K[Prometheus Server] --&gt; L[/metrics/prometheus]
 M[Dashboard] --&gt; N[/dashboard]&lt;/pre&gt;&lt;h2 id="prometheus-메트릭"&gt;Prometheus 메트릭
&lt;/h2&gt;&lt;h3 id="기본-http-메트릭"&gt;기본 HTTP 메트릭
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;메트릭명&lt;/th&gt;
					&lt;th&gt;타입&lt;/th&gt;
					&lt;th&gt;라벨&lt;/th&gt;
					&lt;th&gt;설명&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;http_requests_total&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Counter&lt;/td&gt;
					&lt;td&gt;method, endpoint, status&lt;/td&gt;
					&lt;td&gt;총 HTTP 요청 수&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;http_request_duration_seconds&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Histogram&lt;/td&gt;
					&lt;td&gt;method, endpoint&lt;/td&gt;
					&lt;td&gt;요청 처리 시간 분포&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;http_errors_total&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Counter&lt;/td&gt;
					&lt;td&gt;method, endpoint, status&lt;/td&gt;
					&lt;td&gt;총 에러 수&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;active_requests&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Gauge&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
					&lt;td&gt;현재 활성 요청 수&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="비즈니스-메트릭"&gt;비즈니스 메트릭
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;메트릭명&lt;/th&gt;
					&lt;th&gt;타입&lt;/th&gt;
					&lt;th&gt;라벨&lt;/th&gt;
					&lt;th&gt;설명&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;git_operations_total&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Counter&lt;/td&gt;
					&lt;td&gt;operation, status&lt;/td&gt;
					&lt;td&gt;Git 작업 수&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;git_operation_duration_seconds&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Histogram&lt;/td&gt;
					&lt;td&gt;operation&lt;/td&gt;
					&lt;td&gt;Git 작업 소요 시간&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;translation_requests_total&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Counter&lt;/td&gt;
					&lt;td&gt;source_lang, target_lang, status&lt;/td&gt;
					&lt;td&gt;번역 요청 수&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;translation_duration_seconds&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Histogram&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
					&lt;td&gt;번역 소요 시간&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;post_operations_total&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Counter&lt;/td&gt;
					&lt;td&gt;operation, language, status&lt;/td&gt;
					&lt;td&gt;포스트 작업 수&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="메트릭-수집-코드"&gt;메트릭 수집 코드
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# prometheus_exporter.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; prometheus_client &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Counter, Histogram, Gauge
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;http_requests_total &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Counter(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;http_requests_total&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Total HTTP requests&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;method&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;endpoint&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;http_request_duration_seconds &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Histogram(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;http_request_duration_seconds&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;HTTP request latency&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;method&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;endpoint&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 미들웨어에서 자동 수집&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;PrometheusMiddleware&lt;/span&gt;(BaseHTTPMiddleware):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dispatch&lt;/span&gt;(self, request: Request, call_next):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; start_time &lt;span style="color:#f92672"&gt;=&lt;/span&gt; time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;time()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; call_next(request)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; duration &lt;span style="color:#f92672"&gt;=&lt;/span&gt; time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;time() &lt;span style="color:#f92672"&gt;-&lt;/span&gt; start_time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; http_requests_total&lt;span style="color:#f92672"&gt;.&lt;/span&gt;labels(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; method&lt;span style="color:#f92672"&gt;=&lt;/span&gt;request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; endpoint&lt;span style="color:#f92672"&gt;=&lt;/span&gt;request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;url&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; status&lt;span style="color:#f92672"&gt;=&lt;/span&gt;response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;status_code
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;.&lt;/span&gt;inc()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; http_request_duration_seconds&lt;span style="color:#f92672"&gt;.&lt;/span&gt;labels(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; method&lt;span style="color:#f92672"&gt;=&lt;/span&gt;request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;method,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; endpoint&lt;span style="color:#f92672"&gt;=&lt;/span&gt;request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;url&lt;span style="color:#f92672"&gt;.&lt;/span&gt;path
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;.&lt;/span&gt;observe(duration)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="알림-시스템"&gt;알림 시스템
&lt;/h2&gt;&lt;h3 id="알림-규칙"&gt;알림 규칙
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;규칙명&lt;/th&gt;
					&lt;th&gt;조건&lt;/th&gt;
					&lt;th&gt;심각도&lt;/th&gt;
					&lt;th&gt;쿨다운&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;High Error Rate&lt;/td&gt;
					&lt;td&gt;error_rate &amp;gt; 5%&lt;/td&gt;
					&lt;td&gt;WARNING&lt;/td&gt;
					&lt;td&gt;5분&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Critical Error Rate&lt;/td&gt;
					&lt;td&gt;error_rate &amp;gt; 20%&lt;/td&gt;
					&lt;td&gt;CRITICAL&lt;/td&gt;
					&lt;td&gt;1분&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Slow Response Time&lt;/td&gt;
					&lt;td&gt;avg_response_time &amp;gt; 2000ms&lt;/td&gt;
					&lt;td&gt;WARNING&lt;/td&gt;
					&lt;td&gt;10분&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;High Slow Request Rate&lt;/td&gt;
					&lt;td&gt;slow_request_rate &amp;gt; 10%&lt;/td&gt;
					&lt;td&gt;WARNING&lt;/td&gt;
					&lt;td&gt;5분&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="알림-채널"&gt;알림 채널
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Slack Webhook&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;모든 심각도 수준 지원&lt;/li&gt;
&lt;li&gt;색상으로 심각도 표현 (INFO: 녹색, WARNING: 주황, ERROR: 빨강, CRITICAL: 짙은 빨강)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Email&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CRITICAL 수준만 전송&lt;/li&gt;
&lt;li&gt;SMTP 기반 이메일 발송&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="alertmanager-코드"&gt;AlertManager 코드
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# alerting.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AlertManager&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;check_and_alert&lt;/span&gt;(self, metrics: Dict[str, Any]):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; rule &lt;span style="color:#f92672"&gt;in&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rules:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; rule&lt;span style="color:#f92672"&gt;.&lt;/span&gt;should_trigger(metrics):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_send_alert(rule, metrics)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_send_alert&lt;/span&gt;(self, rule: AlertRule, metrics: Dict[str, Any]):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; message &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;Alert Rule: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;rule&lt;span style="color:#f92672"&gt;.&lt;/span&gt;name&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;Condition: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;rule&lt;span style="color:#f92672"&gt;.&lt;/span&gt;condition&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;Current Metrics:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;- Total Requests: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;metrics&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;total_requests&amp;#39;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;- Error Count: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;metrics&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;error_count&amp;#39;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;- Error Rate: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;metrics&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;error_rate_percent&amp;#39;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;- Slow Requests: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;metrics&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;slow_request_count&amp;#39;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;slack&lt;span style="color:#f92672"&gt;.&lt;/span&gt;send(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Alert: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;rule&lt;span style="color:#f92672"&gt;.&lt;/span&gt;name&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; message&lt;span style="color:#f92672"&gt;=&lt;/span&gt;message&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strip(),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; severity&lt;span style="color:#f92672"&gt;=&lt;/span&gt;rule&lt;span style="color:#f92672"&gt;.&lt;/span&gt;severity
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="대시보드-구성"&gt;대시보드 구성
&lt;/h2&gt;&lt;h3 id="모니터링-엔드포인트"&gt;모니터링 엔드포인트
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;경로&lt;/th&gt;
					&lt;th&gt;인증&lt;/th&gt;
					&lt;th&gt;설명&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/health&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;불필요&lt;/td&gt;
					&lt;td&gt;서버 상태 확인&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/metrics&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;필요&lt;/td&gt;
					&lt;td&gt;JSON 형식 메트릭&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/metrics/prometheus&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;불필요&lt;/td&gt;
					&lt;td&gt;Prometheus 형식 메트릭&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/metrics/reset&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;필요&lt;/td&gt;
					&lt;td&gt;메트릭 초기화&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/dashboard&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;불필요&lt;/td&gt;
					&lt;td&gt;웹 대시보드&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/alerts/rules&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;필요&lt;/td&gt;
					&lt;td&gt;알림 규칙 목록&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/alerts/send&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;필요&lt;/td&gt;
					&lt;td&gt;수동 알림 전송&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="요청-추적-미들웨어"&gt;요청 추적 미들웨어
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# middleware.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MonitoringMiddleware&lt;/span&gt;(BaseHTTPMiddleware):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dispatch&lt;/span&gt;(self, request: Request, call_next):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; request_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; str(uuid&lt;span style="color:#f92672"&gt;.&lt;/span&gt;uuid4())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; start_time &lt;span style="color:#f92672"&gt;=&lt;/span&gt; time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;time()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; call_next(request)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; process_time &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;time() &lt;span style="color:#f92672"&gt;-&lt;/span&gt; start_time) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 응답 헤더에 추적 정보 추가&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;headers[&lt;span style="color:#e6db74"&gt;&amp;#34;X-Process-Time&amp;#34;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;process_time&lt;span style="color:#e6db74"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.2f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;headers[&lt;span style="color:#e6db74"&gt;&amp;#34;X-Request-ID&amp;#34;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; request_id
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; response
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="환경-변수-설정"&gt;환경 변수 설정
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Slack 알림&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SLACK_WEBHOOK_URL&lt;span style="color:#f92672"&gt;=&lt;/span&gt;https://hooks.slack.com/services/...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Email 알림&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SMTP_SERVER&lt;span style="color:#f92672"&gt;=&lt;/span&gt;smtp.gmail.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SMTP_PORT&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;587&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SMTP_USERNAME&lt;span style="color:#f92672"&gt;=&lt;/span&gt;your-email@gmail.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SMTP_PASSWORD&lt;span style="color:#f92672"&gt;=&lt;/span&gt;your-app-password
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ALERT_FROM_EMAIL&lt;span style="color:#f92672"&gt;=&lt;/span&gt;your-email@gmail.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ALERT_TO_EMAILS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;admin@example.com,ops@example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 임계값 설정&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SLOW_REQUEST_THRESHOLD&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 1초&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;VERY_SLOW_THRESHOLD&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 3초&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAX_BODY_LOG_LENGTH&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="향후-계획"&gt;향후 계획
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Grafana 통합&lt;/strong&gt;: Prometheus 데이터를 Grafana 대시보드로 시각화&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;알림 규칙 동적 구성&lt;/strong&gt;: 런타임에 알림 규칙 추가/제거 API&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;메트릭 보관&lt;/strong&gt;: 장기 메트릭 데이터 저장 및 분석&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Health Check 강화&lt;/strong&gt;: 의존 서비스(Git, LLM) 상태 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="결론"&gt;결론
&lt;/h2&gt;&lt;p&gt;프로메테우스 기반 모니터링과 Slack/Email 알림 시스템을 통해 서버 상태를 실시간으로 모니터링하고 문제 발생 시 즉시 대응할 수 있는 인프라가 구축되었습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;영어 버전:&lt;/strong&gt; &lt;a class="link" href="https://blog.agentthread.dev/post/2026-03-03-002-blog-api-server-monitoring-alerting/" &gt;English Version&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>