|
6 | 6 | 2. Automatic logging of reactive value updates |
7 | 7 | 3. How `otel.suppress` suppresses both Shiny's internal spans and value logs |
8 | 8 |
|
9 | | -Run with: |
10 | | - SHINY_OTEL_COLLECT=all python app.py |
| 9 | +Run under OpenTelemetry zero-code auto-instrumentation (installed with |
| 10 | +`pip install "shiny[otel]"`), printing spans and log events to the console: |
11 | 11 |
|
12 | | -Or with shinylive: |
13 | | - shinylive export . site |
14 | | - python -m http.server --directory site 8008 |
| 12 | + opentelemetry-instrument --traces_exporter console --logs_exporter console \\ |
| 13 | + --metrics_exporter none shiny run app.py |
| 14 | +
|
| 15 | +To see how the global collect level interacts with `otel.suppress` / `otel.collect`, |
| 16 | +lower it with `SHINY_OTEL_COLLECT` (the app displays the active level): |
| 17 | +
|
| 18 | + SHINY_OTEL_COLLECT=session opentelemetry-instrument --traces_exporter console \\ |
| 19 | + --logs_exporter console --metrics_exporter none shiny run app.py |
| 20 | +
|
| 21 | +At `session` level the "Normal" card stops producing spans, but the |
| 22 | +`otel.collect` card still does -- see the comment on `with otel.collect():` |
| 23 | +below for why. |
15 | 24 |
|
16 | 25 | Watch the console for spans and log events as you interact with the app. |
17 | 26 | """ |
18 | 27 |
|
19 | | -# Set up OpenTelemetry logging to the console |
20 | | -from opentelemetry import trace |
21 | | -from opentelemetry._logs import set_logger_provider |
22 | | -from opentelemetry.sdk._logs import LoggerProvider |
23 | | -from opentelemetry.sdk._logs.export import ( |
24 | | - ConsoleLogRecordExporter, |
25 | | - SimpleLogRecordProcessor, |
26 | | -) |
27 | | -from opentelemetry.sdk.trace import TracerProvider |
28 | | -from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor |
29 | | - |
30 | 28 | from shiny import App, otel, reactive, render, ui |
31 | 29 |
|
32 | | -# Set up debug OpenTelemetry tracing/logging to the console |
33 | | -# NOT recommended for production use |
34 | | -trace_provider = TracerProvider() |
35 | | -trace_provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter())) |
36 | | -trace.set_tracer_provider(trace_provider) |
37 | | -log_provider = LoggerProvider() |
38 | | -log_provider.add_log_record_processor( |
39 | | - SimpleLogRecordProcessor(ConsoleLogRecordExporter()) |
40 | | -) |
41 | | -set_logger_provider(log_provider) |
42 | | - |
43 | | -# # Typical setup behavior: |
44 | | -# import logfire |
45 | | -# logfire.configure( |
46 | | -# token="fake-token-for-demo", |
47 | | -# service_name="shiny-open-telemetry-demo", |
48 | | -# environment="development", |
49 | | -# ) |
| 30 | +# This app intentionally contains NO OpenTelemetry setup code. Instrumentation |
| 31 | +# is an operational concern: apply it at launch with `opentelemetry-instrument` |
| 32 | +# (see the module docstring above) and configure exporters via its CLI flags or |
| 33 | +# standard `OTEL_*` environment variables. Configuring providers inside the app |
| 34 | +# (`trace.set_tracer_provider(...)`) is discouraged -- providers can only be |
| 35 | +# installed once per process, so code-based setup conflicts with (and is ignored |
| 36 | +# under) external instrumentation. |
50 | 37 |
|
51 | 38 | app_ui = ui.page_fluid( |
52 | 39 | ui.h2("OpenTelemetry: Collection Control & Value Logging"), |
53 | | - ui.markdown(""" |
| 40 | + ui.markdown(f""" |
54 | 41 | This demo shows how `otel.suppress` controls **Shiny's internal spans and value logs**. |
55 | 42 |
|
| 43 | + Global collect level: **`{otel.get_level().name.lower()}`** |
| 44 | + (set via `SHINY_OTEL_COLLECT`; defaults to `all`) |
| 45 | +
|
56 | 46 | Watch the console to see: |
57 | 47 | - **Spans**: Session lifecycle, reactive execution |
58 | 48 | - **Logs**: Reactive value updates with source references |
|
88 | 78 | ui.markdown(""" |
89 | 79 | **Telemetry:** ✅ Shiny spans + value logs |
90 | 80 | Uses `@otel.collect` to re-enable Shiny telemetry inside |
91 | | - a broad `otel.suppress()` block. |
| 81 | + a broad `otel.suppress()` block — even when the global |
| 82 | + `SHINY_OTEL_COLLECT` level is lower. |
92 | 83 | """), |
93 | 84 | ), |
94 | 85 | ), |
@@ -147,8 +138,17 @@ def private_counter_display(): |
147 | 138 | counter_val = private_counter() |
148 | 139 | return f"Slider: {slider_val}\nCounter: {counter_val}\n\n❌ No telemetry" |
149 | 140 |
|
| 141 | + # Demonstrates re-enabling telemetry within a suppressed context. |
| 142 | + # |
| 143 | + # Note: `otel.collect` is an ABSOLUTE per-object setting, not a relative |
| 144 | + # one. Reactive objects created inside this block capture level ALL at |
| 145 | + # creation time, which takes precedence over the global level -- so |
| 146 | + # `output collect_counter_display` still produces spans even when |
| 147 | + # SHINY_OTEL_COLLECT is set lower (e.g. `session`). `otel.suppress` is |
| 148 | + # symmetric: it forces telemetry off even when the global level is |
| 149 | + # `all`. Use `collect` to keep a few critical outputs observable while |
| 150 | + # the rest of the app runs quiet. |
150 | 151 | with otel.collect(): |
151 | | - # Demonstrates re-enabling telemetry within a suppressed context |
152 | 152 | collect_counter = reactive.value(0) |
153 | 153 |
|
154 | 154 | @reactive.effect |
|
0 commit comments