pytraceflow 0.1.0__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 (44) hide show
  1. pytraceflow-0.1.0/LICENSE +0 -0
  2. pytraceflow-0.1.0/MANIFEST.in +9 -0
  3. pytraceflow-0.1.0/PKG-INFO +187 -0
  4. pytraceflow-0.1.0/README.md +157 -0
  5. pytraceflow-0.1.0/export_otlp.py +104 -0
  6. pytraceflow-0.1.0/pyproject.toml +39 -0
  7. pytraceflow-0.1.0/pytraceflow.egg-info/PKG-INFO +187 -0
  8. pytraceflow-0.1.0/pytraceflow.egg-info/SOURCES.txt +42 -0
  9. pytraceflow-0.1.0/pytraceflow.egg-info/dependency_links.txt +1 -0
  10. pytraceflow-0.1.0/pytraceflow.egg-info/entry_points.txt +4 -0
  11. pytraceflow-0.1.0/pytraceflow.egg-info/requires.txt +12 -0
  12. pytraceflow-0.1.0/pytraceflow.egg-info/top_level.txt +3 -0
  13. pytraceflow-0.1.0/pytraceflow.py +516 -0
  14. pytraceflow-0.1.0/pytraceflow_visual.py +1533 -0
  15. pytraceflow-0.1.0/samples/__init__.py +0 -0
  16. pytraceflow-0.1.0/samples/basic/basic_positional_sample.py +105 -0
  17. pytraceflow-0.1.0/samples/basic/basic_sample.py +132 -0
  18. pytraceflow-0.1.0/samples/complex/__init__.py +0 -0
  19. pytraceflow-0.1.0/samples/complex/complex_app.py +22 -0
  20. pytraceflow-0.1.0/samples/complex/demo/__init__.py +1 -0
  21. pytraceflow-0.1.0/samples/complex/demo/common/__init__.py +1 -0
  22. pytraceflow-0.1.0/samples/complex/demo/common/logger.py +14 -0
  23. pytraceflow-0.1.0/samples/complex/demo/common/math_utils.py +16 -0
  24. pytraceflow-0.1.0/samples/complex/demo/pricing/__init__.py +1 -0
  25. pytraceflow-0.1.0/samples/complex/demo/pricing/discounts.py +23 -0
  26. pytraceflow-0.1.0/samples/complex/demo/pricing/tax.py +13 -0
  27. pytraceflow-0.1.0/samples/complex/demo/repository/__init__.py +1 -0
  28. pytraceflow-0.1.0/samples/complex/demo/repository/products.py +16 -0
  29. pytraceflow-0.1.0/samples/complex/demo/services/__init__.py +1 -0
  30. pytraceflow-0.1.0/samples/complex/demo/services/checkout.py +27 -0
  31. pytraceflow-0.1.0/samples/concurrent/__init__.py +0 -0
  32. pytraceflow-0.1.0/samples/concurrent/conc_demo/__init__.py +1 -0
  33. pytraceflow-0.1.0/samples/concurrent/conc_demo/common/__init__.py +1 -0
  34. pytraceflow-0.1.0/samples/concurrent/conc_demo/common/log.py +11 -0
  35. pytraceflow-0.1.0/samples/concurrent/conc_demo/services/__init__.py +1 -0
  36. pytraceflow-0.1.0/samples/concurrent/conc_demo/services/runner.py +19 -0
  37. pytraceflow-0.1.0/samples/concurrent/conc_demo/tasks/__init__.py +1 -0
  38. pytraceflow-0.1.0/samples/concurrent/conc_demo/tasks/cpu.py +11 -0
  39. pytraceflow-0.1.0/samples/concurrent/conc_demo/tasks/io.py +13 -0
  40. pytraceflow-0.1.0/samples/concurrent/conc_demo.py +22 -0
  41. pytraceflow-0.1.0/samples/error/__init__.py +0 -0
  42. pytraceflow-0.1.0/samples/error/error_internal_sample.py +29 -0
  43. pytraceflow-0.1.0/samples/error/error_sample.py +33 -0
  44. pytraceflow-0.1.0/setup.cfg +4 -0
Binary file
@@ -0,0 +1,9 @@
1
+ include LICENSE
2
+ include README.md
3
+ exclude pft*.html
4
+ exclude pft*.json
5
+ exclude jaeger.zip
6
+ recursive-exclude jaeger-bin *
7
+ recursive-exclude __pycache__ *
8
+ recursive-exclude .venv *
9
+ recursive-include samples *.py
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: pytraceflow
3
+ Version: 0.1.0
4
+ Summary: Post-mortem Python trace profiler that emits JSON plus an HTML viewer and OTLP exporter.
5
+ Author: FlowTrace maintainers
6
+ License: MIT
7
+ Keywords: tracing,profiling,debugging,observability,otlp,jaeger
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Software Development :: Debuggers
16
+ Classifier: Topic :: System :: Monitoring
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Provides-Extra: memory
21
+ Requires-Dist: psutil>=5.9; extra == "memory"
22
+ Provides-Extra: otlp
23
+ Requires-Dist: opentelemetry-sdk>=1.22.0; extra == "otlp"
24
+ Requires-Dist: opentelemetry-exporter-otlp>=1.22.0; extra == "otlp"
25
+ Provides-Extra: all
26
+ Requires-Dist: psutil>=5.9; extra == "all"
27
+ Requires-Dist: opentelemetry-sdk>=1.22.0; extra == "all"
28
+ Requires-Dist: opentelemetry-exporter-otlp>=1.22.0; extra == "all"
29
+ Dynamic: license-file
30
+
31
+ # PyTraceFlow.
32
+
33
+ [![PyPI](https://img.shields.io/pypi/v/pytraceflow)](https://pypi.org/project/pytraceflow/)
34
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg?logo=python)](https://pypi.org/project/pytraceflow/)
35
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
36
+ [![Status](https://img.shields.io/badge/status-alpha-orange)](#)
37
+ [![Downloads](https://img.shields.io/pypi/dm/pytraceflow?color=blue)](https://pypi.org/project/pytraceflow/)
38
+
39
+ FlowTrace is a trace visualizer designed as a "post-mortem debugger": instead of pausing and resuming, it captures calls (inputs, outputs, caller, module, duration, errors) into a hierarchical JSON so you can inspect them later without re-running.
40
+
41
+ ![FlowTrace overview](images/pytraceflow.jpg)
42
+ ![Call details panel](images/pytraceflow_calls.jpg)
43
+
44
+ ## Quick start (3 steps)
45
+ 1. Capture the complex sample to JSON: `python pytraceflow.py -s samples/complex/complex_app.py -o pft_complex.json`
46
+ 2. Render the HTML viewer: `python pytraceflow_visual.py -i pft_complex.json -o pft_complex.html`
47
+ ![Call details panel](images/pytraceflow_visual.jpg)
48
+ 3. Optional export to OTLP/Jaeger (HTTP 4318): `python export_otlp.py -i pft_complex.json --endpoint http://localhost:4318/v1/traces --service pytraceflow-complex`
49
+ ![Call details panel](images/pytraceflow_to_otlp_menu.jpg)
50
+ ![Call details panel](images/pytraceflow_to_otlp_spans.jpg)
51
+ ## Basic flow
52
+ 1. Profile a script: `python pytraceflow.py -s your_script.py -o pft.json`
53
+ 2. Generate the viewer: `python pytraceflow_visual.py -i pft.json -o pft.html`
54
+ 3. Open `pft.html` and browse:
55
+ - Search terms: opens the matching node in a floating panel.
56
+ - Expand/collapse nodes; open calls.
57
+ - Controls to show/hide badges, Python internals, language (es/en), and light/dark mode.
58
+
59
+ ### Passing script arguments
60
+ - Any arguments unknown to `pytraceflow.py` are forwarded to the profiled script (no `--` separator required).
61
+ - Example with options: `python pytraceflow.py -s samples/basic/basic_sample.py --customer "ana maria" --tier gold --coffee 3`
62
+ - Example with positionals: `python pytraceflow.py -s samples/basic/basic_positional_sample.py "juan perez" silver 1 2 0 0.18`
63
+
64
+ ## Features
65
+ - Captures inputs/outputs, caller, module, duration, and errors.
66
+ - Groups instances and nested calls while preserving hierarchy.
67
+ - Search with highlighting and floating panels; option to hide Python internals.
68
+ - Dark mode by default, quick controls, and multi-language.
69
+ - Performance knobs: `--flush-interval` (seconds, <=0 disables background flush), `--flush-every-call` (legacy, slower), `--log-flushes` (stderr).
70
+ - Overhead controls: memoria desactivada por defecto; habilita con `--with-memory` (usa psutil + tracemalloc), o combina `--no-tracemalloc` / `--no-memory`. `--skip-inputs` evita serializar args/kwargs.
71
+ - Root entry now records total runtime; STDERR line: `[FlowTrace] Profiling finished in X.XXXs (script=...)`.
72
+ - Export existing traces to OTLP/Jaeger via `export_otlp.py`, with span names enriched by module and instance id to make nested calls distinct in Jaeger UI.
73
+
74
+ ## CLI options
75
+ - `-s/--script` (required): target script path.
76
+ - `-o/--output`: JSON output path (default `pft.json`).
77
+ - `--flush-interval`: seconds between background flushes; `<=0` disables thread (default `1.0`).
78
+ - `--flush-every-call`: force flush on every event (slow; legacy).
79
+ - `--log-flushes`: log each flush to stderr.
80
+ - `--with-memory`: enable memory snapshots (psutil + tracemalloc). Default is off; expect slower runs when enabled.
81
+ - `--no-memory`: disable memory snapshots.
82
+ - `--no-tracemalloc`: keep psutil but skip tracemalloc.
83
+ - `--skip-inputs`: do not serialize call inputs/outputs.
84
+ - OTLP export (optional, requires `opentelemetry-*`): `--export-otlp-endpoint http://localhost:4318/v1/traces`, `--export-otlp-service myapp`, repeat `--export-otlp-header key=value` for extra headers.
85
+ - Any other args are forwarded to the profiled script.
86
+
87
+ ### Usage examples
88
+ - Default fast run: `python pytraceflow.py -s samples/basic/basic_sample.py -o pft.json`
89
+ - With memory metrics: `python pytraceflow.py -s samples/basic/basic_sample.py --with-memory --flush-interval 2.0`
90
+ - Minimal overhead: `python pytraceflow.py -s samples/basic/basic_sample.py --flush-interval 0 --skip-inputs`
91
+ - Legacy per-call flush with logs: `python pytraceflow.py -s samples/basic/basic_sample.py --flush-every-call --log-flushes`
92
+ - Memory via psutil only: `python pytraceflow.py -s samples/basic/basic_sample.py --with-memory --no-tracemalloc`
93
+ - Export to OTLP/HTTP: `python pytraceflow.py -s samples/basic/basic_sample.py --export-otlp-endpoint http://localhost:4318/v1/traces --export-otlp-service pytraceflow-sample`
94
+ - Export a saved trace to Jaeger (OTLP/HTTP, port 4318): `python export_otlp.py -i pft.json --endpoint http://localhost:4318/v1/traces --service pytraceflow-sample`
95
+ - Export with custom headers (auth/tenant): `python export_otlp.py -i pft.json --endpoint http://localhost:4318/v1/traces --service pytraceflow-sample --header Authorization=Bearer_TOKEN --header X-Tenant=acme`
96
+
97
+ ## Included examples
98
+ - `script.py` basic example.
99
+ - `complex_app.py` with modules `demo/...` (prices, taxes, discounts).
100
+ - `conc_demo.py` with CPU-bound (multiprocessing) and IO-bound (threads) to view concurrent traces.
101
+ - `basic_positional_sample.py` shows the same flow using positional arguments.
102
+ - `error_sample.py` intentionally fails (missing config) to show the error badge.
103
+
104
+ ## Backlog / ideas
105
+ - Filters by module/class/time.
106
+ - Export filtered views.
107
+
108
+ ## License
109
+ MIT License. See `LICENSE` for full text.
110
+
111
+ ---
112
+
113
+ FlowTrace es un visualizador de trazas de ejecucion, pensado como un "debugger post-mortem": en lugar de parar y reanudar, captura las llamadas (inputs, outputs, caller, modulo, duracion, errores) en un JSON jerarquico para inspeccionarlo despues sin reejecutar.
114
+
115
+ ![Vista general de FlowTrace](images/pytraceflow.jpg)
116
+ ![Panel de detalle de llamadas](images/pytraceflow_calls.jpg)
117
+
118
+ ## Inicio rápido (3 pasos)
119
+ 1. Capturar el ejemplo complejo a JSON: `python pytraceflow.py -s samples/complex/complex_app.py -o pft_complex.json`
120
+ 2. Generar el visor HTML: `python pytraceflow_visual.py -i pft_complex.json -o pft_complex.html`
121
+ ![Panel visual de llamadas](images/pytraceflow_visual.jpg)
122
+ 3. Exportar a OTLP/Jaeger (HTTP 4318): `python export_otlp.py -i pft_complex.json --endpoint http://localhost:4318/v1/traces --service pytraceflow-complex`
123
+ ![Menu de exportacion a OTLP/Jaeger](images/pytraceflow_to_otlp_menu.jpg)
124
+ ![Spans exportados a Jaeger](images/pytraceflow_to_otlp_spans.jpg)
125
+
126
+ ## Flujo basico
127
+ 1. Perfilar un script: `python pytraceflow.py -s tu_script.py -o pft.json`
128
+ 2. Generar visor: `python pytraceflow_visual.py -i pft.json -o pft.html`
129
+ 3. Abrir `pft.html` y navegar:
130
+ - Buscar trminos: abre el nodo coincidente en panel flotante.
131
+ - Expandir/colapsar nodos; abrir calls.
132
+ - Controles para mostrar/ocultar badges, internals de Python, idioma (es/en) y modo claro/oscuro.
133
+
134
+ ### Pasar argumentos al script
135
+ - Cualquier argumento que `pytraceflow.py` no reconoce se reenvia al script perfilado (no hace falta `--`).
136
+ - Ejemplo con opciones: `python pytraceflow.py -s samples/basic/basic_sample.py --customer "ana maria" --tier gold --coffee 3`
137
+ - Ejemplo con posicionales: `python pytraceflow.py -s samples/basic/basic_positional_sample.py "juan perez" silver 1 2 0 0.18`
138
+
139
+ ## Caracteristicas
140
+ - Captura inputs/outputs, caller, modulo, duracion y errores.
141
+ - Agrupa instancias y llamadas anidadas preservando jerarquia.
142
+ - Buscador con resaltado y paneles flotantes; opcion para ocultar internals de Python.
143
+ - Modo oscuro por defecto, controles rapidos y multilenguaje.
144
+ - Ajustes de performance: `--flush-interval` (segundos, <=0 desactiva flush en background), `--flush-every-call` (modo anterior, mas lento), `--log-flushes` (stderr).
145
+ - Controles de overhead: memoria viene desactivada por defecto; `--with-memory` la habilita (psutil + tracemalloc), combinable con `--no-tracemalloc` / `--no-memory`. `--skip-inputs` evita serializar args/kwargs.
146
+ - La llamada raiz registra el tiempo total; se imprime en STDERR `[FlowTrace] Profiling finished in X.XXXs (script=...)`.
147
+ - Export de trazas existentes a OTLP/Jaeger con `export_otlp.py`; los spans incluyen módulo e id de instancia para distinguir llamadas anidadas en Jaeger.
148
+
149
+ ## Opciones CLI
150
+ - `-s/--script` (obligatorio): ruta del script a perfilar.
151
+ - `-o/--output`: ruta del JSON de salida (por defecto `pft.json`).
152
+ - `--flush-interval`: segundos entre flushes en background; `<=0` desactiva el hilo (por defecto `1.0`).
153
+ - `--flush-every-call`: fuerza flush en cada evento (lento; legado).
154
+ - `--log-flushes`: loguea cada flush a stderr.
155
+ - `--with-memory`: habilita snapshots de memoria (psutil + tracemalloc). Por defecto está apagado; al activarlo las ejecuciones serán más lentas.
156
+ - `--no-memory`: desactiva snapshots de memoria.
157
+ - `--no-tracemalloc`: deja psutil pero omite tracemalloc.
158
+ - `--skip-inputs`: no serializa inputs/outputs de las llamadas.
159
+ - Export OTLP (opcional, requiere `opentelemetry-*`): `--export-otlp-endpoint http://localhost:4318/v1/traces`, `--export-otlp-service miapp`, headers extra con `--export-otlp-header clave=valor` (repetible).
160
+ - Cualquier otro argumento se reenvía al script perfilado.
161
+
162
+ ### Ejemplos de uso
163
+ - Ejecución rápida por defecto: `python pytraceflow.py -s samples/basic/basic_sample.py -o pft.json`
164
+ - Con métricas de memoria: `python pytraceflow.py -s samples/basic/basic_sample.py --with-memory --flush-interval 2.0`
165
+ - Overhead mínimo: `python pytraceflow.py -s samples/basic/basic_sample.py --flush-interval 0 --skip-inputs`
166
+ - Flush por llamada con logs: `python pytraceflow.py -s samples/basic/basic_sample.py --flush-every-call --log-flushes`
167
+ - Solo psutil (sin tracemalloc): `python pytraceflow.py -s samples/basic/basic_sample.py --with-memory --no-tracemalloc`
168
+ - Export a OTLP/HTTP: `python pytraceflow.py -s samples/basic/basic_sample.py --export-otlp-endpoint http://localhost:4318/v1/traces --export-otlp-service pytraceflow-sample`
169
+ - Exportar un JSON ya capturado a Jaeger (OTLP/HTTP, puerto 4318): `python export_otlp.py -i pft.json --endpoint http://localhost:4318/v1/traces --service pytraceflow-sample`
170
+ - Exportar con cabeceras extra (auth/tenant): `python export_otlp.py -i pft.json --endpoint http://localhost:4318/v1/traces --service pytraceflow-sample --header Authorization=Bearer_TOKEN --header X-Tenant=acme`
171
+
172
+ ## Ejemplos incluidos
173
+ - `script.py` ejemplo basico.
174
+ - `complex_app.py` con modulos `demo/...` (precios, impuestos, descuentos).
175
+ - `conc_demo.py` con CPU-bound (multiproceso) e IO-bound (hilos) para ver trazas concurrentes.
176
+ - `basic_positional_sample.py` muestra el mismo flujo usando argumentos posicionales.
177
+ - `error_sample.py` falla adrede (config faltante) para ver el badge de error.
178
+ - `error_internal_sample.py` falla dentro de una llamada interna (`validate_config`) para ver errores anidados.
179
+
180
+ ## Pendientes / ideas
181
+ - Filtros por mdulo/clase/tiempo.
182
+ - Export de vistas filtradas.
183
+ - Integracin con spans/telemetra.
184
+
185
+ ## Licencia
186
+ MIT License. Ver `LICENSE` para el texto completo.
187
+
@@ -0,0 +1,157 @@
1
+ # PyTraceFlow.
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/pytraceflow)](https://pypi.org/project/pytraceflow/)
4
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg?logo=python)](https://pypi.org/project/pytraceflow/)
5
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
+ [![Status](https://img.shields.io/badge/status-alpha-orange)](#)
7
+ [![Downloads](https://img.shields.io/pypi/dm/pytraceflow?color=blue)](https://pypi.org/project/pytraceflow/)
8
+
9
+ FlowTrace is a trace visualizer designed as a "post-mortem debugger": instead of pausing and resuming, it captures calls (inputs, outputs, caller, module, duration, errors) into a hierarchical JSON so you can inspect them later without re-running.
10
+
11
+ ![FlowTrace overview](images/pytraceflow.jpg)
12
+ ![Call details panel](images/pytraceflow_calls.jpg)
13
+
14
+ ## Quick start (3 steps)
15
+ 1. Capture the complex sample to JSON: `python pytraceflow.py -s samples/complex/complex_app.py -o pft_complex.json`
16
+ 2. Render the HTML viewer: `python pytraceflow_visual.py -i pft_complex.json -o pft_complex.html`
17
+ ![Call details panel](images/pytraceflow_visual.jpg)
18
+ 3. Optional export to OTLP/Jaeger (HTTP 4318): `python export_otlp.py -i pft_complex.json --endpoint http://localhost:4318/v1/traces --service pytraceflow-complex`
19
+ ![Call details panel](images/pytraceflow_to_otlp_menu.jpg)
20
+ ![Call details panel](images/pytraceflow_to_otlp_spans.jpg)
21
+ ## Basic flow
22
+ 1. Profile a script: `python pytraceflow.py -s your_script.py -o pft.json`
23
+ 2. Generate the viewer: `python pytraceflow_visual.py -i pft.json -o pft.html`
24
+ 3. Open `pft.html` and browse:
25
+ - Search terms: opens the matching node in a floating panel.
26
+ - Expand/collapse nodes; open calls.
27
+ - Controls to show/hide badges, Python internals, language (es/en), and light/dark mode.
28
+
29
+ ### Passing script arguments
30
+ - Any arguments unknown to `pytraceflow.py` are forwarded to the profiled script (no `--` separator required).
31
+ - Example with options: `python pytraceflow.py -s samples/basic/basic_sample.py --customer "ana maria" --tier gold --coffee 3`
32
+ - Example with positionals: `python pytraceflow.py -s samples/basic/basic_positional_sample.py "juan perez" silver 1 2 0 0.18`
33
+
34
+ ## Features
35
+ - Captures inputs/outputs, caller, module, duration, and errors.
36
+ - Groups instances and nested calls while preserving hierarchy.
37
+ - Search with highlighting and floating panels; option to hide Python internals.
38
+ - Dark mode by default, quick controls, and multi-language.
39
+ - Performance knobs: `--flush-interval` (seconds, <=0 disables background flush), `--flush-every-call` (legacy, slower), `--log-flushes` (stderr).
40
+ - Overhead controls: memoria desactivada por defecto; habilita con `--with-memory` (usa psutil + tracemalloc), o combina `--no-tracemalloc` / `--no-memory`. `--skip-inputs` evita serializar args/kwargs.
41
+ - Root entry now records total runtime; STDERR line: `[FlowTrace] Profiling finished in X.XXXs (script=...)`.
42
+ - Export existing traces to OTLP/Jaeger via `export_otlp.py`, with span names enriched by module and instance id to make nested calls distinct in Jaeger UI.
43
+
44
+ ## CLI options
45
+ - `-s/--script` (required): target script path.
46
+ - `-o/--output`: JSON output path (default `pft.json`).
47
+ - `--flush-interval`: seconds between background flushes; `<=0` disables thread (default `1.0`).
48
+ - `--flush-every-call`: force flush on every event (slow; legacy).
49
+ - `--log-flushes`: log each flush to stderr.
50
+ - `--with-memory`: enable memory snapshots (psutil + tracemalloc). Default is off; expect slower runs when enabled.
51
+ - `--no-memory`: disable memory snapshots.
52
+ - `--no-tracemalloc`: keep psutil but skip tracemalloc.
53
+ - `--skip-inputs`: do not serialize call inputs/outputs.
54
+ - OTLP export (optional, requires `opentelemetry-*`): `--export-otlp-endpoint http://localhost:4318/v1/traces`, `--export-otlp-service myapp`, repeat `--export-otlp-header key=value` for extra headers.
55
+ - Any other args are forwarded to the profiled script.
56
+
57
+ ### Usage examples
58
+ - Default fast run: `python pytraceflow.py -s samples/basic/basic_sample.py -o pft.json`
59
+ - With memory metrics: `python pytraceflow.py -s samples/basic/basic_sample.py --with-memory --flush-interval 2.0`
60
+ - Minimal overhead: `python pytraceflow.py -s samples/basic/basic_sample.py --flush-interval 0 --skip-inputs`
61
+ - Legacy per-call flush with logs: `python pytraceflow.py -s samples/basic/basic_sample.py --flush-every-call --log-flushes`
62
+ - Memory via psutil only: `python pytraceflow.py -s samples/basic/basic_sample.py --with-memory --no-tracemalloc`
63
+ - Export to OTLP/HTTP: `python pytraceflow.py -s samples/basic/basic_sample.py --export-otlp-endpoint http://localhost:4318/v1/traces --export-otlp-service pytraceflow-sample`
64
+ - Export a saved trace to Jaeger (OTLP/HTTP, port 4318): `python export_otlp.py -i pft.json --endpoint http://localhost:4318/v1/traces --service pytraceflow-sample`
65
+ - Export with custom headers (auth/tenant): `python export_otlp.py -i pft.json --endpoint http://localhost:4318/v1/traces --service pytraceflow-sample --header Authorization=Bearer_TOKEN --header X-Tenant=acme`
66
+
67
+ ## Included examples
68
+ - `script.py` basic example.
69
+ - `complex_app.py` with modules `demo/...` (prices, taxes, discounts).
70
+ - `conc_demo.py` with CPU-bound (multiprocessing) and IO-bound (threads) to view concurrent traces.
71
+ - `basic_positional_sample.py` shows the same flow using positional arguments.
72
+ - `error_sample.py` intentionally fails (missing config) to show the error badge.
73
+
74
+ ## Backlog / ideas
75
+ - Filters by module/class/time.
76
+ - Export filtered views.
77
+
78
+ ## License
79
+ MIT License. See `LICENSE` for full text.
80
+
81
+ ---
82
+
83
+ FlowTrace es un visualizador de trazas de ejecucion, pensado como un "debugger post-mortem": en lugar de parar y reanudar, captura las llamadas (inputs, outputs, caller, modulo, duracion, errores) en un JSON jerarquico para inspeccionarlo despues sin reejecutar.
84
+
85
+ ![Vista general de FlowTrace](images/pytraceflow.jpg)
86
+ ![Panel de detalle de llamadas](images/pytraceflow_calls.jpg)
87
+
88
+ ## Inicio rápido (3 pasos)
89
+ 1. Capturar el ejemplo complejo a JSON: `python pytraceflow.py -s samples/complex/complex_app.py -o pft_complex.json`
90
+ 2. Generar el visor HTML: `python pytraceflow_visual.py -i pft_complex.json -o pft_complex.html`
91
+ ![Panel visual de llamadas](images/pytraceflow_visual.jpg)
92
+ 3. Exportar a OTLP/Jaeger (HTTP 4318): `python export_otlp.py -i pft_complex.json --endpoint http://localhost:4318/v1/traces --service pytraceflow-complex`
93
+ ![Menu de exportacion a OTLP/Jaeger](images/pytraceflow_to_otlp_menu.jpg)
94
+ ![Spans exportados a Jaeger](images/pytraceflow_to_otlp_spans.jpg)
95
+
96
+ ## Flujo basico
97
+ 1. Perfilar un script: `python pytraceflow.py -s tu_script.py -o pft.json`
98
+ 2. Generar visor: `python pytraceflow_visual.py -i pft.json -o pft.html`
99
+ 3. Abrir `pft.html` y navegar:
100
+ - Buscar trminos: abre el nodo coincidente en panel flotante.
101
+ - Expandir/colapsar nodos; abrir calls.
102
+ - Controles para mostrar/ocultar badges, internals de Python, idioma (es/en) y modo claro/oscuro.
103
+
104
+ ### Pasar argumentos al script
105
+ - Cualquier argumento que `pytraceflow.py` no reconoce se reenvia al script perfilado (no hace falta `--`).
106
+ - Ejemplo con opciones: `python pytraceflow.py -s samples/basic/basic_sample.py --customer "ana maria" --tier gold --coffee 3`
107
+ - Ejemplo con posicionales: `python pytraceflow.py -s samples/basic/basic_positional_sample.py "juan perez" silver 1 2 0 0.18`
108
+
109
+ ## Caracteristicas
110
+ - Captura inputs/outputs, caller, modulo, duracion y errores.
111
+ - Agrupa instancias y llamadas anidadas preservando jerarquia.
112
+ - Buscador con resaltado y paneles flotantes; opcion para ocultar internals de Python.
113
+ - Modo oscuro por defecto, controles rapidos y multilenguaje.
114
+ - Ajustes de performance: `--flush-interval` (segundos, <=0 desactiva flush en background), `--flush-every-call` (modo anterior, mas lento), `--log-flushes` (stderr).
115
+ - Controles de overhead: memoria viene desactivada por defecto; `--with-memory` la habilita (psutil + tracemalloc), combinable con `--no-tracemalloc` / `--no-memory`. `--skip-inputs` evita serializar args/kwargs.
116
+ - La llamada raiz registra el tiempo total; se imprime en STDERR `[FlowTrace] Profiling finished in X.XXXs (script=...)`.
117
+ - Export de trazas existentes a OTLP/Jaeger con `export_otlp.py`; los spans incluyen módulo e id de instancia para distinguir llamadas anidadas en Jaeger.
118
+
119
+ ## Opciones CLI
120
+ - `-s/--script` (obligatorio): ruta del script a perfilar.
121
+ - `-o/--output`: ruta del JSON de salida (por defecto `pft.json`).
122
+ - `--flush-interval`: segundos entre flushes en background; `<=0` desactiva el hilo (por defecto `1.0`).
123
+ - `--flush-every-call`: fuerza flush en cada evento (lento; legado).
124
+ - `--log-flushes`: loguea cada flush a stderr.
125
+ - `--with-memory`: habilita snapshots de memoria (psutil + tracemalloc). Por defecto está apagado; al activarlo las ejecuciones serán más lentas.
126
+ - `--no-memory`: desactiva snapshots de memoria.
127
+ - `--no-tracemalloc`: deja psutil pero omite tracemalloc.
128
+ - `--skip-inputs`: no serializa inputs/outputs de las llamadas.
129
+ - Export OTLP (opcional, requiere `opentelemetry-*`): `--export-otlp-endpoint http://localhost:4318/v1/traces`, `--export-otlp-service miapp`, headers extra con `--export-otlp-header clave=valor` (repetible).
130
+ - Cualquier otro argumento se reenvía al script perfilado.
131
+
132
+ ### Ejemplos de uso
133
+ - Ejecución rápida por defecto: `python pytraceflow.py -s samples/basic/basic_sample.py -o pft.json`
134
+ - Con métricas de memoria: `python pytraceflow.py -s samples/basic/basic_sample.py --with-memory --flush-interval 2.0`
135
+ - Overhead mínimo: `python pytraceflow.py -s samples/basic/basic_sample.py --flush-interval 0 --skip-inputs`
136
+ - Flush por llamada con logs: `python pytraceflow.py -s samples/basic/basic_sample.py --flush-every-call --log-flushes`
137
+ - Solo psutil (sin tracemalloc): `python pytraceflow.py -s samples/basic/basic_sample.py --with-memory --no-tracemalloc`
138
+ - Export a OTLP/HTTP: `python pytraceflow.py -s samples/basic/basic_sample.py --export-otlp-endpoint http://localhost:4318/v1/traces --export-otlp-service pytraceflow-sample`
139
+ - Exportar un JSON ya capturado a Jaeger (OTLP/HTTP, puerto 4318): `python export_otlp.py -i pft.json --endpoint http://localhost:4318/v1/traces --service pytraceflow-sample`
140
+ - Exportar con cabeceras extra (auth/tenant): `python export_otlp.py -i pft.json --endpoint http://localhost:4318/v1/traces --service pytraceflow-sample --header Authorization=Bearer_TOKEN --header X-Tenant=acme`
141
+
142
+ ## Ejemplos incluidos
143
+ - `script.py` ejemplo basico.
144
+ - `complex_app.py` con modulos `demo/...` (precios, impuestos, descuentos).
145
+ - `conc_demo.py` con CPU-bound (multiproceso) e IO-bound (hilos) para ver trazas concurrentes.
146
+ - `basic_positional_sample.py` muestra el mismo flujo usando argumentos posicionales.
147
+ - `error_sample.py` falla adrede (config faltante) para ver el badge de error.
148
+ - `error_internal_sample.py` falla dentro de una llamada interna (`validate_config`) para ver errores anidados.
149
+
150
+ ## Pendientes / ideas
151
+ - Filtros por mdulo/clase/tiempo.
152
+ - Export de vistas filtradas.
153
+ - Integracin con spans/telemetra.
154
+
155
+ ## Licencia
156
+ MIT License. Ver `LICENSE` para el texto completo.
157
+
@@ -0,0 +1,104 @@
1
+ import argparse
2
+ import json
3
+ import sys
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+
8
+ def parse_headers(values: list[str]) -> dict[str, str]:
9
+ headers: dict[str, str] = {}
10
+ for h in values:
11
+ if "=" not in h:
12
+ sys.stderr.write(f"[export-otlp] Ignoring header '{h}' (expected key=value)\n")
13
+ continue
14
+ k, v = h.split("=", 1)
15
+ headers[k.strip()] = v.strip()
16
+ return headers
17
+
18
+
19
+ def load_root(path: Path) -> dict[str, Any]:
20
+ with path.open("r", encoding="utf-8") as f:
21
+ data = json.load(f)
22
+ if not data or not isinstance(data, list):
23
+ raise ValueError("Invalid FlowTrace JSON: expected list with root node")
24
+ return data[0]
25
+
26
+
27
+ def emit_tree(tracer, node: dict[str, Any], parent_ctx=None) -> None:
28
+ from opentelemetry import trace
29
+ from opentelemetry.trace import Status, StatusCode
30
+
31
+ callable_name = node.get("callable")
32
+ called_name = node.get("called")
33
+
34
+ if callable_name and called_name and callable_name != called_name:
35
+ span_name = f"{called_name}.{callable_name}"
36
+ else:
37
+ span_name = callable_name or called_name or "unknown"
38
+
39
+ if node.get("module"):
40
+ span_name = f"{node['module']}::{span_name}"
41
+ if node.get("instance_id") is not None:
42
+ span_name += f"#{node['instance_id']}"
43
+
44
+ with tracer.start_as_current_span(span_name, context=parent_ctx) as span:
45
+ span.set_attribute("flowtrace.module", node.get("module", ""))
46
+ span.set_attribute("flowtrace.called", node.get("called", ""))
47
+ if node.get("duration_ms") is not None:
48
+ span.set_attribute("flowtrace.duration_ms", node.get("duration_ms"))
49
+ if node.get("instance_id") is not None:
50
+ span.set_attribute("flowtrace.instance_id", node.get("instance_id"))
51
+ if node.get("inputs"):
52
+ span.set_attribute("flowtrace.inputs_present", True)
53
+ if node.get("error"):
54
+ span.record_exception(Exception(str(node.get("error"))))
55
+ span.set_status(Status(StatusCode.ERROR))
56
+ child_ctx = trace.set_span_in_context(span)
57
+ for child in node.get("calls", []):
58
+ emit_tree(tracer, child, child_ctx)
59
+
60
+
61
+ def export_otlp(json_path: Path, endpoint: str, service_name: str | None, headers: dict[str, str]) -> None:
62
+ try:
63
+ from opentelemetry import trace
64
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
65
+ from opentelemetry.sdk.resources import Resource
66
+ from opentelemetry.sdk.trace import TracerProvider
67
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
68
+ except Exception as exc: # pragma: no cover
69
+ sys.stderr.write(f"[export-otlp] Missing dependency opentelemetry-* ({exc})\n")
70
+ sys.exit(1)
71
+
72
+ root = load_root(json_path)
73
+ resource = Resource.create({"service.name": service_name or json_path.stem})
74
+ provider = TracerProvider(resource=resource)
75
+ exporter = OTLPSpanExporter(endpoint=endpoint, headers=headers)
76
+ provider.add_span_processor(BatchSpanProcessor(exporter))
77
+ trace.set_tracer_provider(provider)
78
+ tracer = trace.get_tracer("flowtrace.exporter")
79
+
80
+ try:
81
+ emit_tree(tracer, root)
82
+ finally:
83
+ provider.shutdown()
84
+
85
+
86
+ def main():
87
+ parser = argparse.ArgumentParser(description="Export FlowTrace JSON to OTLP/HTTP")
88
+ parser.add_argument("-i", "--input", default="flowtrace.json", help="Path to FlowTrace JSON")
89
+ parser.add_argument("--endpoint", required=True, help="OTLP/HTTP endpoint (e.g. http://localhost:4318/v1/traces)")
90
+ parser.add_argument("--service", default=None, help="service.name value (defaults to JSON filename)")
91
+ parser.add_argument(
92
+ "--header",
93
+ action="append",
94
+ default=[],
95
+ help="Extra OTLP header key=value (repeatable)",
96
+ )
97
+ args = parser.parse_args()
98
+
99
+ headers = parse_headers(args.header)
100
+ export_otlp(Path(args.input), args.endpoint, args.service, headers)
101
+
102
+
103
+ if __name__ == "__main__":
104
+ main()
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pytraceflow"
7
+ version = "0.1.0"
8
+ description = "Post-mortem Python trace profiler that emits JSON plus an HTML viewer and OTLP exporter."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "FlowTrace maintainers" }]
13
+ keywords = ["tracing", "profiling", "debugging", "observability", "otlp", "jaeger"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3 :: Only",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Software Development :: Debuggers",
23
+ "Topic :: System :: Monitoring"
24
+ ]
25
+ dependencies = []
26
+
27
+ [project.optional-dependencies]
28
+ memory = ["psutil>=5.9"]
29
+ otlp = ["opentelemetry-sdk>=1.22.0", "opentelemetry-exporter-otlp>=1.22.0"]
30
+ all = ["psutil>=5.9", "opentelemetry-sdk>=1.22.0", "opentelemetry-exporter-otlp>=1.22.0"]
31
+
32
+ [project.scripts]
33
+ pytraceflow = "pytraceflow:main"
34
+ pytraceflow-visual = "pytraceflow_visual:main"
35
+ pytraceflow-export-otlp = "export_otlp:main"
36
+
37
+ [tool.setuptools]
38
+ py-modules = ["pytraceflow", "pytraceflow_visual", "export_otlp"]
39
+ license-files = ["LICENSE"]