|
| 1 | +<!DOCTYPE html> |
| 2 | +<html lang="en"> |
| 3 | +<head> |
| 4 | + <meta charset="UTF-8"> |
| 5 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 6 | + <meta name="color-scheme" content="light dark"> |
| 7 | + <title>Observability experimentations</title> |
| 8 | + <link rel="stylesheet" href="https://roussev.com/styles.css"> |
| 9 | + <style> |
| 10 | + .architecture-diagram { |
| 11 | + margin: 2rem 0; |
| 12 | + padding: 1.5rem; |
| 13 | + background: var(--bg-secondary, #f5f5f5); |
| 14 | + border-radius: 8px; |
| 15 | + overflow-x: auto; |
| 16 | + } |
| 17 | + .architecture-diagram h2 { |
| 18 | + margin-top: 0; |
| 19 | + margin-bottom: 1rem; |
| 20 | + color: var(--text-primary, #333); |
| 21 | + } |
| 22 | + .link-icon { |
| 23 | + width: 20px; |
| 24 | + height: 20px; |
| 25 | + vertical-align: middle; |
| 26 | + margin-right: 8px; |
| 27 | + display: inline-block; |
| 28 | + } |
| 29 | + .token-box { |
| 30 | + display: inline-flex; |
| 31 | + gap: 0.25rem; |
| 32 | + align-items: center; |
| 33 | + margin-left: 0.5rem; |
| 34 | + vertical-align: middle; |
| 35 | + } |
| 36 | + .token-input { |
| 37 | + width: 120px; |
| 38 | + padding: 0.25rem 0.5rem; |
| 39 | + font-family: monospace; |
| 40 | + font-size: 0.7rem; |
| 41 | + border: 1px solid var(--border-color, #ccc); |
| 42 | + border-radius: 4px; |
| 43 | + background: var(--bg-primary, white); |
| 44 | + color: var(--text-primary, #333); |
| 45 | + overflow: hidden; |
| 46 | + text-overflow: ellipsis; |
| 47 | + } |
| 48 | + .copy-btn { |
| 49 | + padding: 0.25rem 0.75rem; |
| 50 | + background: #0066cc; |
| 51 | + color: white; |
| 52 | + border: none; |
| 53 | + border-radius: 4px; |
| 54 | + cursor: pointer; |
| 55 | + font-size: 0.8rem; |
| 56 | + white-space: nowrap; |
| 57 | + transition: background 0.2s; |
| 58 | + } |
| 59 | + .copy-btn:hover { |
| 60 | + background: #0052a3; |
| 61 | + } |
| 62 | + .copy-btn:active { |
| 63 | + background: #003d7a; |
| 64 | + } |
| 65 | + .copy-btn.copied { |
| 66 | + background: #28a745; |
| 67 | + } |
| 68 | + @media (prefers-color-scheme: dark) { |
| 69 | + .architecture-diagram { |
| 70 | + background: var(--bg-secondary, #1a1a1a); |
| 71 | + } |
| 72 | + .token-input { |
| 73 | + background: var(--bg-primary, #2a2a2a); |
| 74 | + color: var(--text-primary, #e0e0e0); |
| 75 | + border-color: var(--border-color, #444); |
| 76 | + } |
| 77 | + } |
| 78 | + </style> |
| 79 | +</head> |
| 80 | +<body> |
| 81 | + <div class="profile-container"> |
| 82 | + <div class="profile-header"> |
| 83 | + <div class="profile-path">items-service/README.md</div> |
| 84 | + </div> |
| 85 | + |
| 86 | + <article class="profile-content"> |
| 87 | + <h1>Observability</h1> |
| 88 | + |
| 89 | + <p>This is an Observability experiment using Bun, Postgre and OpenTelemetry showcasing cool technologies below:</p> |
| 90 | + |
| 91 | + <p> |
| 92 | + <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/grafana/grafana-original.svg" class="link-icon" alt="Grafana"> |
| 93 | + <a href="/grafana/d/items-service-metrics/items-service-metrics" target="_blank">Grafana</a> - Metrics dashboard |
| 94 | + </p> |
| 95 | + <p> |
| 96 | + <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/prometheus/prometheus-original.svg" class="link-icon" alt="Prometheus"> |
| 97 | + <a href="/items/prometheus-queries" target="_blank">Prometheus queries</a> |
| 98 | + </p> |
| 99 | + <p> |
| 100 | + <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/prometheus/prometheus-original.svg" class="link-icon" alt="Prometheus"> |
| 101 | + <a href="/items/metrics" target="_blank">Prometheus raw metrics</a> |
| 102 | + </p> |
| 103 | + <p> |
| 104 | + <svg class="link-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 105 | + <path d="M12 2L2 7L12 12L22 7L12 2Z" fill="#60D0E4"/> |
| 106 | + <path d="M2 17L12 22L22 17" stroke="#60D0E4" stroke-width="2"/> |
| 107 | + <path d="M2 12L12 17L22 12" stroke="#60D0E4" stroke-width="2"/> |
| 108 | + </svg> |
| 109 | + <a href="/jaeger" target="_blank">Jaeger</a> - Distributed tracing |
| 110 | + </p> |
| 111 | + <p> |
| 112 | + <svg class="link-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 113 | + <rect x="2" y="4" width="20" height="3" fill="#F5A800"/> |
| 114 | + <rect x="2" y="10" width="20" height="3" fill="#F5A800"/> |
| 115 | + <rect x="2" y="16" width="20" height="3" fill="#F5A800"/> |
| 116 | + </svg> |
| 117 | + <a href="/grafana/explore?orgId=1&left=%7B%22datasource%22:%22loki%22,%22queries%22:%5B%7B%22refId%22:%22A%22,%22expr%22:%22%7Bapp%3D%5C%22items-service%5C%22%7D%22%7D%5D,%22range%22:%7B%22from%22:%22now-1h%22,%22to%22:%22now%22%7D%7D" target="_blank">Loki</a> - Log aggregation |
| 118 | + </p> |
| 119 | + |
| 120 | + <p><strong>Kubernetes Dashboard:</strong></p> |
| 121 | + <p> |
| 122 | + <svg class="link-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 123 | + <path d="M10.9 2.1l-7.2 4.2v8.4l7.2 4.2 7.2-4.2V6.3L10.9 2.1zm5.5 11.8l-5.5 3.2-5.5-3.2V7.5l5.5-3.2 5.5 3.2v6.4z" fill="#326CE5"/> |
| 124 | + <circle cx="10.9" cy="10.9" r="2.5" fill="#326CE5"/> |
| 125 | + </svg> |
| 126 | + <a href="https://kube.roussev.com" target="_blank">Headlamp</a> - k8s readonly UI (copy token to access) |
| 127 | + <span class="token-box"> |
| 128 | + <input |
| 129 | + type="text" |
| 130 | + class="token-input" |
| 131 | + id="headlamp-token" |
| 132 | + value="eyJhbGciOiJSUzI1NiIsImtpZCI6IkYwNnVfdXZvVDdvbHhYZnVrTGhHLWg1ZEhZN1hoMHNCczEyWjBOUm5GU1EifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJoZWFkbGFtcC1yZWFkb25seSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJoZWFkbGFtcC1yZWFkb25seSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImE0ZDg0ZGU4LWRmNTctNDg2ZC1hZjJkLWYyZTlhYTkxMGFlNiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTpoZWFkbGFtcC1yZWFkb25seSJ9.Sr1CZT5gAXBtT6ERnbTEPsvuUxuhQ9HtqqykIHpnoWoSNSU363p4Hs4FJGQorEgen8ne0hH_cOOIhthy7djqFxN16ctHGEpzmGPmooOswK5fA0pSKBC6NWyfxiTjmvJVar6W7K7-unX7rIp6uPLI0ULZmPwqBx7ZgAbEbhBmNC0bxUFi2W7EqttRRhIeWcFIdfr9ww5M-1LJOLCb9Usnz6pPhW8QAIhZZ2looXEWT5zclRQc0JpkWpzWpoyG0pB2HRVMl-xlGytz1QMpD4kHuBC3gAcr5pZUco0DUIgpm8_7_yNuQ7mc9WWCJC8mEwaos1352a2cE_DwsOdhMdSIXQ" |
| 133 | + readonly |
| 134 | + /> |
| 135 | + <button class="copy-btn" onclick="copyToken()">Copy</button> |
| 136 | + </span> |
| 137 | + </p> |
| 138 | + |
| 139 | + |
| 140 | + <p><strong>Dummy Service :</strong></p> |
| 141 | + <p> |
| 142 | + <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/swagger/swagger-original.svg" class="link-icon" alt="Swagger"> |
| 143 | + <a href="/items/docs" target="_blank">Open API / Swagger Docs</a> |
| 144 | + </p> |
| 145 | + |
| 146 | + <div class="architecture-diagram"> |
| 147 | + <h2>Architecture</h2> |
| 148 | + <pre class="mermaid"> |
| 149 | +graph TB |
| 150 | + %% Client |
| 151 | + subgraph ClientLayer["Client Layer"] |
| 152 | + Client[HTTP Client] |
| 153 | + end |
| 154 | + |
| 155 | + %% Application |
| 156 | + subgraph AppLayer["Application Layer"] |
| 157 | + App[items-service] |
| 158 | + ItemsEndpoint[/items endpoint/] |
| 159 | + MetricsEndpoint[/metrics endpoint/] |
| 160 | + OTel[OpenTelemetry SDK] |
| 161 | + Logs[Application Logs] |
| 162 | + end |
| 163 | + |
| 164 | + %% Observability |
| 165 | + subgraph ObsLayer["Observability Stack"] |
| 166 | + Prometheus[Prometheus Metrics] |
| 167 | + Jaeger[Jaeger Tracing] |
| 168 | + Grafana[Grafana Dashboards] |
| 169 | + |
| 170 | + subgraph LogPipeline["Logs Pipeline"] |
| 171 | + Promtail[Promtail / FluentBit Log Agent] |
| 172 | + LogStore[Loki / OpenSearch / Elastic] |
| 173 | + end |
| 174 | + end |
| 175 | + |
| 176 | + %% Data Layer (bottom) |
| 177 | + subgraph DataLayer["Data Layer"] |
| 178 | + DB[(PostgreSQL Database)] |
| 179 | + end |
| 180 | + |
| 181 | + %% Request Flow |
| 182 | + Client -->|HTTP requests| App |
| 183 | + App --> ItemsEndpoint |
| 184 | + ItemsEndpoint -->|SQL queries| DB |
| 185 | + |
| 186 | + %% Metrics Flow |
| 187 | + App --> MetricsEndpoint |
| 188 | + Prometheus -.->|scrapes metrics| MetricsEndpoint |
| 189 | + Grafana -.->|reads metrics| Prometheus |
| 190 | + |
| 191 | + %% Tracing Flow |
| 192 | + App -->|trace spans| OTel |
| 193 | + OTel -->|OTLP| Jaeger |
| 194 | + Grafana -.->|trace links| Jaeger |
| 195 | + |
| 196 | + %% Logs Flow |
| 197 | + App -->|stdout logs| Logs |
| 198 | + Promtail -.->|reads logs| Logs |
| 199 | + Promtail -->|pushes logs| LogStore |
| 200 | + Grafana -.->|logs query| LogStore |
| 201 | + |
| 202 | + %% Styling |
| 203 | + style App fill:#e85d04,stroke:#dc2f02,stroke-width:3px,color:#fff |
| 204 | + style ItemsEndpoint fill:#ffba08,stroke:#faa307,stroke-width:2px,color:#000 |
| 205 | + style MetricsEndpoint fill:#ffba08,stroke:#faa307,stroke-width:2px,color:#000 |
| 206 | + style OTel fill:#f5a800,stroke:#d89000,stroke-width:2px,color:#000 |
| 207 | + style Logs fill:#6c757d,stroke:#495057,stroke-width:2px,color:#fff |
| 208 | + |
| 209 | + style DB fill:#336791,stroke:#2d5a7b,stroke-width:2px,color:#fff |
| 210 | + |
| 211 | + style Prometheus fill:#e6522c,stroke:#c93a1f,stroke-width:2px,color:#fff |
| 212 | + style Jaeger fill:#60d0e4,stroke:#4db8ca,stroke-width:2px,color:#000 |
| 213 | + style Grafana fill:#f46800,stroke:#d85600,stroke-width:2px,color:#fff |
| 214 | + |
| 215 | + style Promtail fill:#1f78b4,stroke:#145684,stroke-width:2px,color:#fff |
| 216 | + style LogStore fill:#7cb342,stroke:#558b2f,stroke-width:2px,color:#fff |
| 217 | + style LogPipeline fill:#ffffff,stroke:#999,stroke-width:1px,color:#000 |
| 218 | + |
| 219 | + </pre> |
| 220 | + </div> |
| 221 | + </article> |
| 222 | + </div> |
| 223 | + |
| 224 | + <script type="module"> |
| 225 | + import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; |
| 226 | + mermaid.initialize({ |
| 227 | + startOnLoad: true, |
| 228 | + theme: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default' |
| 229 | + }); |
| 230 | + </script> |
| 231 | + |
| 232 | + <script> |
| 233 | + function copyToken() { |
| 234 | + const tokenInput = document.getElementById('headlamp-token'); |
| 235 | + const copyBtn = event.target; |
| 236 | + |
| 237 | + // Select and copy the token |
| 238 | + tokenInput.select(); |
| 239 | + tokenInput.setSelectionRange(0, 99999); // For mobile devices |
| 240 | + |
| 241 | + navigator.clipboard.writeText(tokenInput.value).then(() => { |
| 242 | + // Change button text and style |
| 243 | + const originalText = copyBtn.textContent; |
| 244 | + copyBtn.textContent = 'Copied!'; |
| 245 | + copyBtn.classList.add('copied'); |
| 246 | + |
| 247 | + // Reset after 2 seconds |
| 248 | + setTimeout(() => { |
| 249 | + copyBtn.textContent = originalText; |
| 250 | + copyBtn.classList.remove('copied'); |
| 251 | + }, 2000); |
| 252 | + }).catch(err => { |
| 253 | + console.error('Failed to copy:', err); |
| 254 | + alert('Failed to copy token. Please copy it manually.'); |
| 255 | + }); |
| 256 | + } |
| 257 | + </script> |
| 258 | +</body> |
| 259 | +</html> |
| 260 | + |
0 commit comments