vitalwatch 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. vitalwatch-0.1.1/LICENSE +21 -0
  2. vitalwatch-0.1.1/PKG-INFO +320 -0
  3. vitalwatch-0.1.1/README.md +306 -0
  4. vitalwatch-0.1.1/pyproject.toml +28 -0
  5. vitalwatch-0.1.1/setup.cfg +4 -0
  6. vitalwatch-0.1.1/src/vitalwatch/__init__.py +274 -0
  7. vitalwatch-0.1.1/src/vitalwatch/asgi.py +434 -0
  8. vitalwatch-0.1.1/src/vitalwatch/cli.py +212 -0
  9. vitalwatch-0.1.1/src/vitalwatch/client.py +156 -0
  10. vitalwatch-0.1.1/src/vitalwatch/django.py +221 -0
  11. vitalwatch-0.1.1/src/vitalwatch/env.py +122 -0
  12. vitalwatch-0.1.1/src/vitalwatch/flask.py +231 -0
  13. vitalwatch-0.1.1/src/vitalwatch/release.py +191 -0
  14. vitalwatch-0.1.1/src/vitalwatch/reporter.py +392 -0
  15. vitalwatch-0.1.1/src/vitalwatch/scrubber.py +151 -0
  16. vitalwatch-0.1.1/src/vitalwatch.egg-info/PKG-INFO +320 -0
  17. vitalwatch-0.1.1/src/vitalwatch.egg-info/SOURCES.txt +29 -0
  18. vitalwatch-0.1.1/src/vitalwatch.egg-info/dependency_links.txt +1 -0
  19. vitalwatch-0.1.1/src/vitalwatch.egg-info/entry_points.txt +2 -0
  20. vitalwatch-0.1.1/src/vitalwatch.egg-info/top_level.txt +1 -0
  21. vitalwatch-0.1.1/tests/test_asgi.py +542 -0
  22. vitalwatch-0.1.1/tests/test_client.py +110 -0
  23. vitalwatch-0.1.1/tests/test_django.py +250 -0
  24. vitalwatch-0.1.1/tests/test_env.py +89 -0
  25. vitalwatch-0.1.1/tests/test_environment.py +49 -0
  26. vitalwatch-0.1.1/tests/test_flask.py +237 -0
  27. vitalwatch-0.1.1/tests/test_hooks.py +182 -0
  28. vitalwatch-0.1.1/tests/test_release.py +244 -0
  29. vitalwatch-0.1.1/tests/test_reporter.py +178 -0
  30. vitalwatch-0.1.1/tests/test_scrubber.py +96 -0
  31. vitalwatch-0.1.1/tests/test_status.py +176 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CestnyTR
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,320 @@
1
+ Metadata-Version: 2.4
2
+ Name: vitalwatch
3
+ Version: 0.1.1
4
+ Summary: VitalWatch Hub için Python raporlayıcı — sunucu hataları, yavaş istekler, canlılık
5
+ Author: CestnyTR
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/CestnyTR/vitalwatch-python
8
+ Project-URL: Hub, https://github.com/CestnyTR/vitalwatch-hub
9
+ Keywords: monitoring,error-tracking,flask,wsgi,vitalwatch
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Dynamic: license-file
14
+
15
+ # vitalwatch (Python)
16
+
17
+ [VitalWatch Hub](https://github.com/CestnyTR/vitalwatch-hub) için Python raporlayıcı: sunucu
18
+ hataları, yavaş istekler, canlılık.
19
+
20
+ Bağımlılık yok. Build adımı yok. Python 3.9+.
21
+
22
+ Kardeş paketler: `cestnytr/vitalwatch-laravel` (Laravel), `vitalwatch-node` (Node),
23
+ `t.js` (tarayıcı, hub reposunda `resources/sdk/t.js`).
24
+
25
+ ---
26
+
27
+ ## Kapsam — ve neden bu kadar dar
28
+
29
+ Bu paket **yalnızca sunucu tarafını** kapsar. Tarayıcı hataları hub'dan servis edilen
30
+ `t.js` ile toplanır; o dosya bilinçli olarak pakete taşınmadı — hosted olması bir
31
+ düzeltmenin dakikalar içinde tüm sitelere yayılmasını sağlıyor, paket olsaydı her
32
+ düzeltme için bütün projelerin yeniden dağıtılması gerekirdi.
33
+
34
+ Toplanmayanlar — yapılandırmayla dahi açılamaz:
35
+
36
+ IP adresi · User-Agent · istek gövdesi · query string **değerleri** (yalnızca yol) ·
37
+ oturum verisi · çerez · ham SQL literalleri
38
+
39
+ ---
40
+
41
+ ## Kurulum
42
+
43
+ ```bash
44
+ pip install git+https://github.com/CestnyTR/vitalwatch-python.git
45
+ ```
46
+
47
+ `.env` dosyasına — kardeş paketlerle aynı değişken isimleri:
48
+
49
+ ```env
50
+ VITALWATCH_ENABLED=true
51
+ VITALWATCH_URL=https://vitalwatch.example.com
52
+ VITALWATCH_KEY=proje-anahtari
53
+ VITALWATCH_SECRET=<panelden alınan 64 karakterlik secret>
54
+ VITALWATCH_ENV=production
55
+ ```
56
+
57
+ ### Flask
58
+
59
+ ```python
60
+ from flask import Flask
61
+ from vitalwatch.flask import VitalWatchFlask
62
+
63
+ app = Flask(__name__)
64
+ VitalWatchFlask(app)
65
+ ```
66
+
67
+ Tek satır şunları kurar: yakalanmamış istisnalar (`teardown_request`), 5xx ve yavaş
68
+ istekler (`after_request`), süreç düzeyi kancalar (`sys.excepthook`,
69
+ `threading.excepthook`) ve 8 saatlik canlılık isteği.
70
+
71
+ - **Tek arıza, tek kayıt:** hata veren istek yalnızca stack'li `exception` açar;
72
+ aynı istek için ayrıca `http_5xx` gönderilmez (0.1.1+).
73
+ - **Kendi hata işleyiciniz varsa:** `@app.errorhandler(Exception)` ya da
74
+ `errorhandler(500)` tanımlıysa hata yine stack'iyle gelir. İşleyici 4xx dönerse
75
+ gönderilmez. Eskiden bu durumda hata kayboluyor, yalnızca stack'siz `http_5xx`
76
+ geliyordu.
77
+ - **`abort(4xx)` gönderilmez:** bilinçli yanıt, arıza değil.
78
+ - **Kurulum sırası serbest:** `VitalWatchFlask(app)` önce, `vitalwatch.init(...)` sonra
79
+ çağrılabilir; istek kancaları da süreç kancaları da (`sys.excepthook`,
80
+ `threading.excepthook`) güncel raporlayıcıyı çağrı anında çözer. Kurulumda
81
+ yapılandırma yoksa canlılık `init(...)` çağrılınca başlar.
82
+ - **İki kez kurmak zararsız:** aynı uygulamada ikinci `VitalWatchFlask(app)` hiçbir
83
+ şey eklemez; süreç kancaları süreç başına bir kez kurulur.
84
+ - **Uygulamayı düşürmez:** kurulum (bozuk ya da UTF-8 olmayan `.env` dahil)
85
+ hiçbir koşulda hata fırlatmaz.
86
+ - **Süreç kancalarını kapatmak:** `VitalWatchFlask(app, hook_process_errors=False)`
87
+ yalnızca istek kancalarını kurar.
88
+
89
+ ### Django
90
+
91
+ ```python
92
+ # settings.py
93
+ MIDDLEWARE = [
94
+ "vitalwatch.django.VitalWatchMiddleware", # listenin başına yakın
95
+ # ...
96
+ ]
97
+ ```
98
+
99
+ Ara katman yavaş ve 5xx istekleri ölçer, yakalanmamış istisnayı stack'iyle gönderir.
100
+ Hata iki yoldan görülür: Django'nun istisnayı 500'e çevirirken yolladığı
101
+ `got_request_exception` sinyali (görünümün yanında alttaki ara katmanlardan çıkan
102
+ hatalar da) ve `process_exception`. Kurallar Flask'la aynı:
103
+
104
+ - **Tek arıza, tek kayıt;** hata veren istek için ayrıca `http_5xx` açılmaz.
105
+ - **4xx gönderilmez:** `Http404`, `PermissionDenied`, `BadRequest`,
106
+ `SuspiciousOperation`.
107
+ - **Rota deseni gider:** `/urun/<int:urun_id>/`, gerçek id değil. `re_path`
108
+ desenlerinde baştaki `^` ve sondaki `$` atılır.
109
+ - **Hata yutulmaz:** `process_exception` `None` döner; Django'nun 500 sayfası ve
110
+ `handler500` olduğu gibi çalışır.
111
+ - **Süreç kancaları** ilk kurulumda bir kez açılır; kapatmak için
112
+ `VITALWATCH_HOOK_PROCESS = False`.
113
+
114
+ ### FastAPI / Starlette (ASGI)
115
+
116
+ ```python
117
+ from fastapi import FastAPI
118
+ from vitalwatch.asgi import VitalWatchASGIMiddleware
119
+
120
+ app = FastAPI()
121
+ app.add_middleware(VitalWatchASGIMiddleware)
122
+ ```
123
+
124
+ Starlette'te aynı satır (`Starlette(middleware=[Middleware(VitalWatchASGIMiddleware)])` de
125
+ olur). Başka bir ASGI uygulaması doğrudan sarılabilir: `app = VitalWatchASGIMiddleware(app)`.
126
+ Saf ASGI ara katmanıdır; Starlette/FastAPI import anında yüklenmez.
127
+
128
+ - **Tek arıza, tek kayıt;** 4xx `HTTPException` ve doğrulama hataları (422)
129
+ gönderilmez. Bilinçli `HTTPException(5xx)` stack'siz `http_5xx` olarak düşer.
130
+ - **Rota deseni gider:** `/urun/{urun_id}`; `include_router` öneki ve `Mount`
131
+ yolu desene eklenir.
132
+ - **Kendi hata işleyiciniz varsa:** `@app.exception_handler(Exception)` hatayı
133
+ yine bizim katmanımızdan geçirir. Belirli bir sınıfa bağlı işleyici
134
+ (`@app.exception_handler(ValueError)`) 5xx dönerse hata yine stack'iyle gelir,
135
+ 4xx dönerse gönderilmez. Ara katman sırası fark etmez (`add_middleware` ya da
136
+ `middleware=[...]` içinde herhangi bir yer). Elle sarılan uygulamada
137
+ (`VitalWatchASGIMiddleware(app)`) Starlette yığınını ilk istekte kurduğu için bu
138
+ ilk istekten sonra devreye girer; o ilk istekte 5xx stack'siz `http_5xx` olur.
139
+ - **Süre yanıtın son baytında durur:** `BackgroundTasks` ve yanıt sonrası işler
140
+ yavaş istek sayılmaz. Uzun akış (SSE, büyük indirme) son parçaya kadar sürer
141
+ ve yavaş sayılabilir.
142
+ - **İç içe katman tek kayıt açar:** `Mount` edilen alt uygulamada da
143
+ `VitalWatchASGIMiddleware` varsa istek yalnızca dıştakinde ölçülür.
144
+ - **Hata yutulmaz:** raporlanır ve yeniden fırlatılır; lifespan ve websocket
145
+ olduğu gibi geçer.
146
+ - **Süreç kancalarını kapatmak:**
147
+ `app.add_middleware(VitalWatchASGIMiddleware, hook_process_errors=False)`.
148
+
149
+ ### Çerçevesiz
150
+
151
+ ```python
152
+ import vitalwatch
153
+
154
+ vitalwatch.hook_process() # kancalar + canlılık
155
+ vitalwatch.report(error) # elle bildirim
156
+
157
+ vitalwatch.init(ignore=(KeyboardInterrupt, "BenimBeklenenHatam")) # sınıf ya da ad
158
+ vitalwatch.start_heartbeat() # yalnızca canlılık (hook_process zaten başlatır)
159
+ vitalwatch.stop_heartbeat()
160
+ ```
161
+
162
+ `init()` ortam değişkenlerini okur; verilen argümanlar onları ezer. Çağrılmazsa ilk
163
+ kullanımda kendiliğinden kurulur.
164
+
165
+ ### Kurulumu doğrulayın
166
+
167
+ ```bash
168
+ vitalwatch-status
169
+ ```
170
+
171
+ Bu adım atlanmamalı. Paketin en pahalı arıza biçimi **sessiz çalışmamadır**: secret
172
+ eksik kopyalanmışsa hiçbir şey patlamaz, hiçbir log düşmez — hub geçersiz imzaya da
173
+ `204` döner. Kurulum aylarca çalışmıyor olabilir ve o sessizlik "sorun yok" sanılır.
174
+
175
+ ```bash
176
+ vitalwatch-status --test # gerçek bir sınama olayı — panelde hata olarak görünür
177
+ vitalwatch-status --vitalwatch # yalnızca canlılık isteği — panele hata düşürmez
178
+ ```
179
+
180
+ `--test` tek bir kurulumu doğrularken doğru seçim. **Onlarca kurulumu gezen bir
181
+ güncelleme döngüsünde `--vitalwatch` kullanılır** — `--test` orada panele onlarca sahte hata
182
+ bırakır, yani izleme aracı kendi gürültüsünü üretir.
183
+
184
+ Verinin gerçekten ulaştığı yalnızca **hub panelinden** doğrulanır: proje satırındaki
185
+ bağlantı durumu `Bağlı` görünmelidir. Komut bunu kendi başına söyleyemez, çünkü hub
186
+ geçerli ile geçersiz imzayı dışarıya aynı yanıtla karşılar.
187
+
188
+ Komut ayrıca şunları gösterir:
189
+
190
+ - **`Sürüm etiketi`** — olaylara eklenecek `release` ve nereden bulunduğu:
191
+ `abc123def456 (kaynak: .git)`; kaynak `VITALWATCH_RELEASE`, bir CI/PaaS değişkeninin
192
+ adı, `.git` ya da `yok`. Ayrıntı: [Sürüm etiketi](#sürüm-etiketi-release).
193
+ - **`Güncel sürüm`** — GitHub etiketlerindeki en yeni kararlı sürüm (paket PyPI'da
194
+ değil). Kurulu sürümden yeniyse bir `Güncelleme var: pip install -U
195
+ --force-reinstall "vitalwatch @ git+…@vX.Y.Z"` satırı yazılır; bu bir uyarıdır,
196
+ çıkış kodunu değiştirmez. Ağ yoksa ya da GitHub hız sınırına takıldıysa
197
+ `denetlenemedi` yazar (2 sn zaman aşımı). Ağa hiç çıkılmaması için
198
+ `VITALWATCH_STATUS_OFFLINE=1` (`1`, `true`, `yes`, `on`, `evet`; büyük/küçük harf fark
199
+ etmez) — o durumda `Güncel sürüm` satırı hiç yazılmaz.
200
+
201
+ ---
202
+
203
+ ## Yapılandırma
204
+
205
+ | Değişken | Varsayılan | Açıklama |
206
+ |---|---|---|
207
+ | `VITALWATCH_ENABLED` | `true` | `false` iken hiçbir veri gönderilmez |
208
+ | `VITALWATCH_URL` | — | Hub adresi (https) |
209
+ | `VITALWATCH_KEY` | — | Proje anahtarı |
210
+ | `VITALWATCH_SECRET` | — | 64 karakterlik imza secret'i |
211
+ | `VITALWATCH_ENV` | `APP_ENV` | `production` · `staging` · `local`. Takma adlar çevrilir: `prod`/`live` → production, `stage`/`stg`/`preprod`/`uat` → staging, `dev`/`development`/`test`/`testing` → local. Tanınmayan değeri hub reddeder; teşhis komutu hata verir |
212
+ | `VITALWATCH_RELEASE` | kendiliğinden | Sürüm etiketi. Boşsa CI/PaaS commit değişkenlerinden ya da `.git`'ten bulunur — aşağıya bakın |
213
+ | `VITALWATCH_STATUS_OFFLINE` | — | `1`/`true`/`yes`/`on`/`evet` iken `vitalwatch-status` güncelleme denetimi için ağa çıkmaz |
214
+ | `VITALWATCH_TIMEOUT` | `2.0` | Gönderim zaman aşımı, **saniye**. 100 ve üstü milisaniye sayılır — Node aynı adla ms bekliyor, `2000` yazan 2000 saniye beklemesin. Geçersiz, sıfır/eksi, NaN ya da sonsuz → 2 sn; sonuç 0.1–10 sn aralığına sıkıştırılır |
215
+ | `VITALWATCH_SLOW_REQUEST_MS` | `1000` | Yavaş istek eşiği |
216
+
217
+ Ortam değişkenleri `os.environ` ve `.env` / `.env.local` / `.env.<APP_ENV>`
218
+ dosyalarından okunur; süreç ortamı her zaman kazanır. Yalnızca `VITALWATCH_` önekli
219
+ anahtarlar okunur.
220
+
221
+ ### Sürüm etiketi (`release`)
222
+
223
+ `VITALWATCH_RELEASE` neredeyse hiçbir kurulumda doldurulmuyordu; hub bu yüzden bir hatanın
224
+ **hangi deploy ile başladığını** söyleyemiyordu. Deploy'larımız çoğunlukla sunucuda
225
+ `git pull` + gunicorn yeniden başlatma: yeni süreç açılışta commit'i kendisi okur, deploy
226
+ sonrası ilk hatada panel yeni sürümü görür. Etiket kurulumda bir kez çözülür (olay başına
227
+ değil); ilk dolu kaynak kazanır:
228
+
229
+ 1. `init(release=...)` ile açıkça verilen değer.
230
+ 2. `VITALWATCH_RELEASE` — kırpılır, en çok 64 karakter, olduğu gibi.
231
+ 3. Sırasıyla `GIT_COMMIT`, `GIT_SHA`, `COMMIT_SHA`, `SOURCE_VERSION`,
232
+ `VERCEL_GIT_COMMIT_SHA`, `RENDER_GIT_COMMIT`, `HEROKU_SLUG_COMMIT`, `CI_COMMIT_SHA`.
233
+ 7–40 hane hex ise küçük harfle ilk 12 hane; değilse kırpılmış, en çok 64 karakter.
234
+ 4. Çalışma dizinindeki git checkout'u: `.git/HEAD` → dal ref dosyası, yoksa
235
+ `packed-refs`; ayrık HEAD'de sha'nın kendisi. `.git` bir dosyaysa (submodule vb.)
236
+ `gitdir:` izlenir. Yalnızca geçerli 40 hane sha, küçük harfle ilk 12 hane.
237
+ `git` komutu çalıştırılmaz, dosyalar okunur; okunamazsa sessizce vazgeçilir.
238
+ `ref:` yalnızca `refs/` altını gösterebilir, `..` içeremez. **Git worktree'leri
239
+ (`commondir`) desteklenmez** — dal ref'i ortak dizinde durur ve bulunamaz; orada
240
+ `VITALWATCH_RELEASE` kullanın.
241
+ 5. Hiçbiri yoksa etiket gönderilmez.
242
+
243
+ Kural Node ve Laravel paketleriyle aynı. Gunicorn'un çalışma dizini uygulama kökü
244
+ değilse (`--chdir` yoksa) 4. adım bir şey bulamaz; `vitalwatch-status` aynı dizinden
245
+ çalıştırılınca bunu `kaynak: yok` olarak gösterir.
246
+
247
+ ---
248
+
249
+ ## Neler raporlanır
250
+
251
+ | Tür | Ne zaman |
252
+ |---|---|
253
+ | `exception` | Yakalanmamış istisna, `report()` çağrısı |
254
+ | `fatal` | `MemoryError`, `RecursionError`, `SystemError` — yorumlayıcının sınırına çarpmak |
255
+ | `http_5xx` | Yanıt kodu ≥ 500 |
256
+ | `slow_request` | Süre ≥ `VITALWATCH_SLOW_REQUEST_MS` |
257
+
258
+ Başarılı ve hızlı istekler **raporlanmaz**: her isteği göndermek izlenen uygulamaya da
259
+ hub'a da yük olurdu.
260
+
261
+ ---
262
+
263
+ ## Davranış garantileri
264
+
265
+ 1. **Hata yutulmaz.** Handler zinciri korunur, `raise` engellenmez.
266
+ 2. **Süreç yönetimine karışılmaz.** `sys.exit` çağrılmaz, mevcut `excepthook` zincirlenir.
267
+ 3. **Kendi hatasını raporlamaz.** Tüm SDK kodu `try/except` ile sarılır.
268
+ 4. **Kullanıcı isteğini bekletmez.** Gönderim arka plan thread'inden, kısa zaman aşımıyla.
269
+ 5. **Hub erişilemezse sessizce vazgeçilir.**
270
+ 6. **`VITALWATCH_ENABLED=false` iken hiçbir veri gönderilmez.**
271
+
272
+ ---
273
+
274
+ ## Güncelleme ve sürümleme
275
+
276
+ Sürüm **sabitlenmez**: kurulan her zaman deponun son hâlidir. Karşılığında bir kural
277
+ zorunlu hâle gelir — **her yayında sürüm numarası yükseltilmelidir.**
278
+
279
+ Sebep ölçüldü: pip, doğrudan git URL'i verilen bir paketi depoyu klonlayıp metadata'sını
280
+ okuduktan sonra sürüm numarasına bakar. Numara kuruluyla aynıysa **kurulumu atlar** ve
281
+ `-U` bunu değiştirmez:
282
+
283
+ | Komut | Sürüm aynıysa | Sürüm yükselmişse |
284
+ |---|---|---|
285
+ | `pip install -r requirements.txt` | atlar | kurar |
286
+ | `pip install -U ...` | atlar | kurar |
287
+ | `pip install --force-reinstall ...` | kurar | kurar |
288
+
289
+ Yani numara yükseltilmeden yapılan bir yayın, kurulu projelere **hiç ulaşmaz** ve bu
290
+ sessizce olur — paketin bütün tasarımının karşı durduğu arıza biçimi.
291
+
292
+ Sürüm çıkarma sırası:
293
+
294
+ 1. `pyproject.toml` içindeki `version` **ve** `src/vitalwatch/reporter.py` içindeki
295
+ `SDK_VERSION` birlikte yükseltilir. İkisi ayrışırsa panelde yanlış SDK sürümü görünür.
296
+ 2. `CHANGELOG.md`'ye giriş.
297
+ 3. `python -m unittest discover -s tests`
298
+ 4. Commit, `git tag vX.Y.Z`, `git push origin vX.Y.Z`.
299
+ 5. Kurulu projelerde `pip install -r requirements.txt`, ardından `vitalwatch-status` ile
300
+ doğrulama. Panelde SDK sürümünün yenilendiği görülmelidir.
301
+
302
+ Acil bir düzeltme sürüm numarası yükseltmeden dağıtılacaksa tek yol
303
+ `--force-reinstall`; ama o durumda panel eski sürüm numarasını göstermeye devam eder ve
304
+ hangi sunucunun güncellendiği izlenemez.
305
+
306
+ ## Test
307
+
308
+ ```bash
309
+ # Çerçeve testleri için — yoksa 53 test sessizce atlanır
310
+ pip install "Flask==3.0.0" Django fastapi starlette httpx
311
+ python -m unittest discover -s tests
312
+ ```
313
+
314
+ Çıktıda `skipped=` görünüyorsa bir çerçeve kurulu değildir ve o entegrasyon
315
+ **sınanmamıştır** (Flask 17, Django 12, FastAPI/Starlette 24 test).
316
+ 0.1.0'daki çift kayıt ve kaybolan hata tam bu yüzden fark edilmedi.
317
+
318
+ Testler hub'ın imza doğrulamasını (`VerifyProjectSignature`) ve gövde sınırlarını
319
+ birebir uygulayan sahte bir hub'a karşı koşar: imzanın gerçekten kabul edilip
320
+ edilmediği burada görülür, "204 aldım" demek yeterli değildir.
@@ -0,0 +1,306 @@
1
+ # vitalwatch (Python)
2
+
3
+ [VitalWatch Hub](https://github.com/CestnyTR/vitalwatch-hub) için Python raporlayıcı: sunucu
4
+ hataları, yavaş istekler, canlılık.
5
+
6
+ Bağımlılık yok. Build adımı yok. Python 3.9+.
7
+
8
+ Kardeş paketler: `cestnytr/vitalwatch-laravel` (Laravel), `vitalwatch-node` (Node),
9
+ `t.js` (tarayıcı, hub reposunda `resources/sdk/t.js`).
10
+
11
+ ---
12
+
13
+ ## Kapsam — ve neden bu kadar dar
14
+
15
+ Bu paket **yalnızca sunucu tarafını** kapsar. Tarayıcı hataları hub'dan servis edilen
16
+ `t.js` ile toplanır; o dosya bilinçli olarak pakete taşınmadı — hosted olması bir
17
+ düzeltmenin dakikalar içinde tüm sitelere yayılmasını sağlıyor, paket olsaydı her
18
+ düzeltme için bütün projelerin yeniden dağıtılması gerekirdi.
19
+
20
+ Toplanmayanlar — yapılandırmayla dahi açılamaz:
21
+
22
+ IP adresi · User-Agent · istek gövdesi · query string **değerleri** (yalnızca yol) ·
23
+ oturum verisi · çerez · ham SQL literalleri
24
+
25
+ ---
26
+
27
+ ## Kurulum
28
+
29
+ ```bash
30
+ pip install git+https://github.com/CestnyTR/vitalwatch-python.git
31
+ ```
32
+
33
+ `.env` dosyasına — kardeş paketlerle aynı değişken isimleri:
34
+
35
+ ```env
36
+ VITALWATCH_ENABLED=true
37
+ VITALWATCH_URL=https://vitalwatch.example.com
38
+ VITALWATCH_KEY=proje-anahtari
39
+ VITALWATCH_SECRET=<panelden alınan 64 karakterlik secret>
40
+ VITALWATCH_ENV=production
41
+ ```
42
+
43
+ ### Flask
44
+
45
+ ```python
46
+ from flask import Flask
47
+ from vitalwatch.flask import VitalWatchFlask
48
+
49
+ app = Flask(__name__)
50
+ VitalWatchFlask(app)
51
+ ```
52
+
53
+ Tek satır şunları kurar: yakalanmamış istisnalar (`teardown_request`), 5xx ve yavaş
54
+ istekler (`after_request`), süreç düzeyi kancalar (`sys.excepthook`,
55
+ `threading.excepthook`) ve 8 saatlik canlılık isteği.
56
+
57
+ - **Tek arıza, tek kayıt:** hata veren istek yalnızca stack'li `exception` açar;
58
+ aynı istek için ayrıca `http_5xx` gönderilmez (0.1.1+).
59
+ - **Kendi hata işleyiciniz varsa:** `@app.errorhandler(Exception)` ya da
60
+ `errorhandler(500)` tanımlıysa hata yine stack'iyle gelir. İşleyici 4xx dönerse
61
+ gönderilmez. Eskiden bu durumda hata kayboluyor, yalnızca stack'siz `http_5xx`
62
+ geliyordu.
63
+ - **`abort(4xx)` gönderilmez:** bilinçli yanıt, arıza değil.
64
+ - **Kurulum sırası serbest:** `VitalWatchFlask(app)` önce, `vitalwatch.init(...)` sonra
65
+ çağrılabilir; istek kancaları da süreç kancaları da (`sys.excepthook`,
66
+ `threading.excepthook`) güncel raporlayıcıyı çağrı anında çözer. Kurulumda
67
+ yapılandırma yoksa canlılık `init(...)` çağrılınca başlar.
68
+ - **İki kez kurmak zararsız:** aynı uygulamada ikinci `VitalWatchFlask(app)` hiçbir
69
+ şey eklemez; süreç kancaları süreç başına bir kez kurulur.
70
+ - **Uygulamayı düşürmez:** kurulum (bozuk ya da UTF-8 olmayan `.env` dahil)
71
+ hiçbir koşulda hata fırlatmaz.
72
+ - **Süreç kancalarını kapatmak:** `VitalWatchFlask(app, hook_process_errors=False)`
73
+ yalnızca istek kancalarını kurar.
74
+
75
+ ### Django
76
+
77
+ ```python
78
+ # settings.py
79
+ MIDDLEWARE = [
80
+ "vitalwatch.django.VitalWatchMiddleware", # listenin başına yakın
81
+ # ...
82
+ ]
83
+ ```
84
+
85
+ Ara katman yavaş ve 5xx istekleri ölçer, yakalanmamış istisnayı stack'iyle gönderir.
86
+ Hata iki yoldan görülür: Django'nun istisnayı 500'e çevirirken yolladığı
87
+ `got_request_exception` sinyali (görünümün yanında alttaki ara katmanlardan çıkan
88
+ hatalar da) ve `process_exception`. Kurallar Flask'la aynı:
89
+
90
+ - **Tek arıza, tek kayıt;** hata veren istek için ayrıca `http_5xx` açılmaz.
91
+ - **4xx gönderilmez:** `Http404`, `PermissionDenied`, `BadRequest`,
92
+ `SuspiciousOperation`.
93
+ - **Rota deseni gider:** `/urun/<int:urun_id>/`, gerçek id değil. `re_path`
94
+ desenlerinde baştaki `^` ve sondaki `$` atılır.
95
+ - **Hata yutulmaz:** `process_exception` `None` döner; Django'nun 500 sayfası ve
96
+ `handler500` olduğu gibi çalışır.
97
+ - **Süreç kancaları** ilk kurulumda bir kez açılır; kapatmak için
98
+ `VITALWATCH_HOOK_PROCESS = False`.
99
+
100
+ ### FastAPI / Starlette (ASGI)
101
+
102
+ ```python
103
+ from fastapi import FastAPI
104
+ from vitalwatch.asgi import VitalWatchASGIMiddleware
105
+
106
+ app = FastAPI()
107
+ app.add_middleware(VitalWatchASGIMiddleware)
108
+ ```
109
+
110
+ Starlette'te aynı satır (`Starlette(middleware=[Middleware(VitalWatchASGIMiddleware)])` de
111
+ olur). Başka bir ASGI uygulaması doğrudan sarılabilir: `app = VitalWatchASGIMiddleware(app)`.
112
+ Saf ASGI ara katmanıdır; Starlette/FastAPI import anında yüklenmez.
113
+
114
+ - **Tek arıza, tek kayıt;** 4xx `HTTPException` ve doğrulama hataları (422)
115
+ gönderilmez. Bilinçli `HTTPException(5xx)` stack'siz `http_5xx` olarak düşer.
116
+ - **Rota deseni gider:** `/urun/{urun_id}`; `include_router` öneki ve `Mount`
117
+ yolu desene eklenir.
118
+ - **Kendi hata işleyiciniz varsa:** `@app.exception_handler(Exception)` hatayı
119
+ yine bizim katmanımızdan geçirir. Belirli bir sınıfa bağlı işleyici
120
+ (`@app.exception_handler(ValueError)`) 5xx dönerse hata yine stack'iyle gelir,
121
+ 4xx dönerse gönderilmez. Ara katman sırası fark etmez (`add_middleware` ya da
122
+ `middleware=[...]` içinde herhangi bir yer). Elle sarılan uygulamada
123
+ (`VitalWatchASGIMiddleware(app)`) Starlette yığınını ilk istekte kurduğu için bu
124
+ ilk istekten sonra devreye girer; o ilk istekte 5xx stack'siz `http_5xx` olur.
125
+ - **Süre yanıtın son baytında durur:** `BackgroundTasks` ve yanıt sonrası işler
126
+ yavaş istek sayılmaz. Uzun akış (SSE, büyük indirme) son parçaya kadar sürer
127
+ ve yavaş sayılabilir.
128
+ - **İç içe katman tek kayıt açar:** `Mount` edilen alt uygulamada da
129
+ `VitalWatchASGIMiddleware` varsa istek yalnızca dıştakinde ölçülür.
130
+ - **Hata yutulmaz:** raporlanır ve yeniden fırlatılır; lifespan ve websocket
131
+ olduğu gibi geçer.
132
+ - **Süreç kancalarını kapatmak:**
133
+ `app.add_middleware(VitalWatchASGIMiddleware, hook_process_errors=False)`.
134
+
135
+ ### Çerçevesiz
136
+
137
+ ```python
138
+ import vitalwatch
139
+
140
+ vitalwatch.hook_process() # kancalar + canlılık
141
+ vitalwatch.report(error) # elle bildirim
142
+
143
+ vitalwatch.init(ignore=(KeyboardInterrupt, "BenimBeklenenHatam")) # sınıf ya da ad
144
+ vitalwatch.start_heartbeat() # yalnızca canlılık (hook_process zaten başlatır)
145
+ vitalwatch.stop_heartbeat()
146
+ ```
147
+
148
+ `init()` ortam değişkenlerini okur; verilen argümanlar onları ezer. Çağrılmazsa ilk
149
+ kullanımda kendiliğinden kurulur.
150
+
151
+ ### Kurulumu doğrulayın
152
+
153
+ ```bash
154
+ vitalwatch-status
155
+ ```
156
+
157
+ Bu adım atlanmamalı. Paketin en pahalı arıza biçimi **sessiz çalışmamadır**: secret
158
+ eksik kopyalanmışsa hiçbir şey patlamaz, hiçbir log düşmez — hub geçersiz imzaya da
159
+ `204` döner. Kurulum aylarca çalışmıyor olabilir ve o sessizlik "sorun yok" sanılır.
160
+
161
+ ```bash
162
+ vitalwatch-status --test # gerçek bir sınama olayı — panelde hata olarak görünür
163
+ vitalwatch-status --vitalwatch # yalnızca canlılık isteği — panele hata düşürmez
164
+ ```
165
+
166
+ `--test` tek bir kurulumu doğrularken doğru seçim. **Onlarca kurulumu gezen bir
167
+ güncelleme döngüsünde `--vitalwatch` kullanılır** — `--test` orada panele onlarca sahte hata
168
+ bırakır, yani izleme aracı kendi gürültüsünü üretir.
169
+
170
+ Verinin gerçekten ulaştığı yalnızca **hub panelinden** doğrulanır: proje satırındaki
171
+ bağlantı durumu `Bağlı` görünmelidir. Komut bunu kendi başına söyleyemez, çünkü hub
172
+ geçerli ile geçersiz imzayı dışarıya aynı yanıtla karşılar.
173
+
174
+ Komut ayrıca şunları gösterir:
175
+
176
+ - **`Sürüm etiketi`** — olaylara eklenecek `release` ve nereden bulunduğu:
177
+ `abc123def456 (kaynak: .git)`; kaynak `VITALWATCH_RELEASE`, bir CI/PaaS değişkeninin
178
+ adı, `.git` ya da `yok`. Ayrıntı: [Sürüm etiketi](#sürüm-etiketi-release).
179
+ - **`Güncel sürüm`** — GitHub etiketlerindeki en yeni kararlı sürüm (paket PyPI'da
180
+ değil). Kurulu sürümden yeniyse bir `Güncelleme var: pip install -U
181
+ --force-reinstall "vitalwatch @ git+…@vX.Y.Z"` satırı yazılır; bu bir uyarıdır,
182
+ çıkış kodunu değiştirmez. Ağ yoksa ya da GitHub hız sınırına takıldıysa
183
+ `denetlenemedi` yazar (2 sn zaman aşımı). Ağa hiç çıkılmaması için
184
+ `VITALWATCH_STATUS_OFFLINE=1` (`1`, `true`, `yes`, `on`, `evet`; büyük/küçük harf fark
185
+ etmez) — o durumda `Güncel sürüm` satırı hiç yazılmaz.
186
+
187
+ ---
188
+
189
+ ## Yapılandırma
190
+
191
+ | Değişken | Varsayılan | Açıklama |
192
+ |---|---|---|
193
+ | `VITALWATCH_ENABLED` | `true` | `false` iken hiçbir veri gönderilmez |
194
+ | `VITALWATCH_URL` | — | Hub adresi (https) |
195
+ | `VITALWATCH_KEY` | — | Proje anahtarı |
196
+ | `VITALWATCH_SECRET` | — | 64 karakterlik imza secret'i |
197
+ | `VITALWATCH_ENV` | `APP_ENV` | `production` · `staging` · `local`. Takma adlar çevrilir: `prod`/`live` → production, `stage`/`stg`/`preprod`/`uat` → staging, `dev`/`development`/`test`/`testing` → local. Tanınmayan değeri hub reddeder; teşhis komutu hata verir |
198
+ | `VITALWATCH_RELEASE` | kendiliğinden | Sürüm etiketi. Boşsa CI/PaaS commit değişkenlerinden ya da `.git`'ten bulunur — aşağıya bakın |
199
+ | `VITALWATCH_STATUS_OFFLINE` | — | `1`/`true`/`yes`/`on`/`evet` iken `vitalwatch-status` güncelleme denetimi için ağa çıkmaz |
200
+ | `VITALWATCH_TIMEOUT` | `2.0` | Gönderim zaman aşımı, **saniye**. 100 ve üstü milisaniye sayılır — Node aynı adla ms bekliyor, `2000` yazan 2000 saniye beklemesin. Geçersiz, sıfır/eksi, NaN ya da sonsuz → 2 sn; sonuç 0.1–10 sn aralığına sıkıştırılır |
201
+ | `VITALWATCH_SLOW_REQUEST_MS` | `1000` | Yavaş istek eşiği |
202
+
203
+ Ortam değişkenleri `os.environ` ve `.env` / `.env.local` / `.env.<APP_ENV>`
204
+ dosyalarından okunur; süreç ortamı her zaman kazanır. Yalnızca `VITALWATCH_` önekli
205
+ anahtarlar okunur.
206
+
207
+ ### Sürüm etiketi (`release`)
208
+
209
+ `VITALWATCH_RELEASE` neredeyse hiçbir kurulumda doldurulmuyordu; hub bu yüzden bir hatanın
210
+ **hangi deploy ile başladığını** söyleyemiyordu. Deploy'larımız çoğunlukla sunucuda
211
+ `git pull` + gunicorn yeniden başlatma: yeni süreç açılışta commit'i kendisi okur, deploy
212
+ sonrası ilk hatada panel yeni sürümü görür. Etiket kurulumda bir kez çözülür (olay başına
213
+ değil); ilk dolu kaynak kazanır:
214
+
215
+ 1. `init(release=...)` ile açıkça verilen değer.
216
+ 2. `VITALWATCH_RELEASE` — kırpılır, en çok 64 karakter, olduğu gibi.
217
+ 3. Sırasıyla `GIT_COMMIT`, `GIT_SHA`, `COMMIT_SHA`, `SOURCE_VERSION`,
218
+ `VERCEL_GIT_COMMIT_SHA`, `RENDER_GIT_COMMIT`, `HEROKU_SLUG_COMMIT`, `CI_COMMIT_SHA`.
219
+ 7–40 hane hex ise küçük harfle ilk 12 hane; değilse kırpılmış, en çok 64 karakter.
220
+ 4. Çalışma dizinindeki git checkout'u: `.git/HEAD` → dal ref dosyası, yoksa
221
+ `packed-refs`; ayrık HEAD'de sha'nın kendisi. `.git` bir dosyaysa (submodule vb.)
222
+ `gitdir:` izlenir. Yalnızca geçerli 40 hane sha, küçük harfle ilk 12 hane.
223
+ `git` komutu çalıştırılmaz, dosyalar okunur; okunamazsa sessizce vazgeçilir.
224
+ `ref:` yalnızca `refs/` altını gösterebilir, `..` içeremez. **Git worktree'leri
225
+ (`commondir`) desteklenmez** — dal ref'i ortak dizinde durur ve bulunamaz; orada
226
+ `VITALWATCH_RELEASE` kullanın.
227
+ 5. Hiçbiri yoksa etiket gönderilmez.
228
+
229
+ Kural Node ve Laravel paketleriyle aynı. Gunicorn'un çalışma dizini uygulama kökü
230
+ değilse (`--chdir` yoksa) 4. adım bir şey bulamaz; `vitalwatch-status` aynı dizinden
231
+ çalıştırılınca bunu `kaynak: yok` olarak gösterir.
232
+
233
+ ---
234
+
235
+ ## Neler raporlanır
236
+
237
+ | Tür | Ne zaman |
238
+ |---|---|
239
+ | `exception` | Yakalanmamış istisna, `report()` çağrısı |
240
+ | `fatal` | `MemoryError`, `RecursionError`, `SystemError` — yorumlayıcının sınırına çarpmak |
241
+ | `http_5xx` | Yanıt kodu ≥ 500 |
242
+ | `slow_request` | Süre ≥ `VITALWATCH_SLOW_REQUEST_MS` |
243
+
244
+ Başarılı ve hızlı istekler **raporlanmaz**: her isteği göndermek izlenen uygulamaya da
245
+ hub'a da yük olurdu.
246
+
247
+ ---
248
+
249
+ ## Davranış garantileri
250
+
251
+ 1. **Hata yutulmaz.** Handler zinciri korunur, `raise` engellenmez.
252
+ 2. **Süreç yönetimine karışılmaz.** `sys.exit` çağrılmaz, mevcut `excepthook` zincirlenir.
253
+ 3. **Kendi hatasını raporlamaz.** Tüm SDK kodu `try/except` ile sarılır.
254
+ 4. **Kullanıcı isteğini bekletmez.** Gönderim arka plan thread'inden, kısa zaman aşımıyla.
255
+ 5. **Hub erişilemezse sessizce vazgeçilir.**
256
+ 6. **`VITALWATCH_ENABLED=false` iken hiçbir veri gönderilmez.**
257
+
258
+ ---
259
+
260
+ ## Güncelleme ve sürümleme
261
+
262
+ Sürüm **sabitlenmez**: kurulan her zaman deponun son hâlidir. Karşılığında bir kural
263
+ zorunlu hâle gelir — **her yayında sürüm numarası yükseltilmelidir.**
264
+
265
+ Sebep ölçüldü: pip, doğrudan git URL'i verilen bir paketi depoyu klonlayıp metadata'sını
266
+ okuduktan sonra sürüm numarasına bakar. Numara kuruluyla aynıysa **kurulumu atlar** ve
267
+ `-U` bunu değiştirmez:
268
+
269
+ | Komut | Sürüm aynıysa | Sürüm yükselmişse |
270
+ |---|---|---|
271
+ | `pip install -r requirements.txt` | atlar | kurar |
272
+ | `pip install -U ...` | atlar | kurar |
273
+ | `pip install --force-reinstall ...` | kurar | kurar |
274
+
275
+ Yani numara yükseltilmeden yapılan bir yayın, kurulu projelere **hiç ulaşmaz** ve bu
276
+ sessizce olur — paketin bütün tasarımının karşı durduğu arıza biçimi.
277
+
278
+ Sürüm çıkarma sırası:
279
+
280
+ 1. `pyproject.toml` içindeki `version` **ve** `src/vitalwatch/reporter.py` içindeki
281
+ `SDK_VERSION` birlikte yükseltilir. İkisi ayrışırsa panelde yanlış SDK sürümü görünür.
282
+ 2. `CHANGELOG.md`'ye giriş.
283
+ 3. `python -m unittest discover -s tests`
284
+ 4. Commit, `git tag vX.Y.Z`, `git push origin vX.Y.Z`.
285
+ 5. Kurulu projelerde `pip install -r requirements.txt`, ardından `vitalwatch-status` ile
286
+ doğrulama. Panelde SDK sürümünün yenilendiği görülmelidir.
287
+
288
+ Acil bir düzeltme sürüm numarası yükseltmeden dağıtılacaksa tek yol
289
+ `--force-reinstall`; ama o durumda panel eski sürüm numarasını göstermeye devam eder ve
290
+ hangi sunucunun güncellendiği izlenemez.
291
+
292
+ ## Test
293
+
294
+ ```bash
295
+ # Çerçeve testleri için — yoksa 53 test sessizce atlanır
296
+ pip install "Flask==3.0.0" Django fastapi starlette httpx
297
+ python -m unittest discover -s tests
298
+ ```
299
+
300
+ Çıktıda `skipped=` görünüyorsa bir çerçeve kurulu değildir ve o entegrasyon
301
+ **sınanmamıştır** (Flask 17, Django 12, FastAPI/Starlette 24 test).
302
+ 0.1.0'daki çift kayıt ve kaybolan hata tam bu yüzden fark edilmedi.
303
+
304
+ Testler hub'ın imza doğrulamasını (`VerifyProjectSignature`) ve gövde sınırlarını
305
+ birebir uygulayan sahte bir hub'a karşı koşar: imzanın gerçekten kabul edilip
306
+ edilmediği burada görülür, "204 aldım" demek yeterli değildir.
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vitalwatch"
7
+ version = "0.1.1"
8
+ description = "VitalWatch Hub için Python raporlayıcı — sunucu hataları, yavaş istekler, canlılık"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [{ name = "CestnyTR" }]
12
+ requires-python = ">=3.9"
13
+ keywords = ["monitoring", "error-tracking", "flask", "wsgi", "vitalwatch"]
14
+
15
+ # Bağımlılık YOK ve olmayacak: paket izlenen projelere kurulur, her bağımlılık
16
+ # orada bir sürüm çakışması riskidir. Flask entegrasyonu flask'ı yalnızca
17
+ # kullanıldığı anda ve kendi içinde import eder.
18
+ dependencies = []
19
+
20
+ [project.urls]
21
+ Homepage = "https://github.com/CestnyTR/vitalwatch-python"
22
+ Hub = "https://github.com/CestnyTR/vitalwatch-hub"
23
+
24
+ [project.scripts]
25
+ vitalwatch-status = "vitalwatch.cli:main"
26
+
27
+ [tool.setuptools.packages.find]
28
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+