A Python library for building page analytics in Streamlit applications through logging.
The solution instruments all st.<widget> with a wrapper function which emits logs,
these logs can then be processed to derive analytics information.
This solution is inspired from jrieke/streamlit-analytics idea.
Events are emitted as JSON objects (one object per log record) via Python’s logging API, using the configured logger and log level (default INFO).
Each line is a single JSON object produced from a UserEvent (or equivalent) after enrichment:
| Field | Meaning |
|---|---|
session_id |
From StreamlitPageAnalytics (constructor or set_session_id) |
user_id |
From StreamlitPageAnalytics (constructor or set_user_id) |
page_name |
Present when page tracking is active and set |
action |
Event type (string); see table below |
widget |
Present when the event targets a widget (id, type, label, values, etc.); omitted when empty |
extra |
Additional payload (form field snapshots, notices, etc.); omitted when empty |
Empty or null values are typically omitted from the JSON (clean_values).
action values (see UserEventAction in code): start_tracking, form_instrumentation_notice, click, change, submit, other.
Use the releases to download assets:
To run it locally, Download the wheel(.whl) file and install or
install from git:
pip install \
git+https://github.com/Snowflake-Labs/streamlit-page-analytics@latestDownload the streamlit_page_analytics-<version>.zip file and copy it to a
Snowflake Internal Stage for use in SiS and run the following command:
ALTER STREAMLIT <your-streamlit-name> SET
IMPORTS = ('@<your-internal-stage>/<path/to/your/zip/file>');
import streamlit as st
from streamlit_page_analytics import StreamlitPageAnalytics
with StreamlitPageAnalytics.track(
name="my-app", session_id=f"{session_id}", user_id=f"{user_id}"
):
st.title("My Awesome App")
st.button('my awesome button')or
import streamlit as st
from streamlit_page_analytics import StreamlitPageAnalytics
page_analytics = StreamlitPageAnalytics(
name="my-app", session_id=f"{session_id}", user_id=f"{user_id}"
)
page_analytics.start_tracking()
st.title("My Awesome App")
st.button('my awesome button')
page_analytics.stop_tracking()Track page visits in multi-page Streamlit applications by passing a page_name to start_tracking(). A start_tracking event is logged only when the user navigates to a different page, avoiding duplicate logs on page reruns.
import streamlit as st
from streamlit_page_analytics import StreamlitPageAnalytics
page_analytics = StreamlitPageAnalytics(
name="my-app", session_id=f"{session_id}", user_id=f"{user_id}"
)
# Pass the current page name - logs only when page changes
page_analytics.start_tracking(page_name="Home")
st.title("Home Page")
st.button("Click me")
page_analytics.stop_tracking()When the user navigates between pages:
start_tracking(page_name="Home")- Logs (first visit)start_tracking(page_name="Home")- No log (same page, e.g., rerun)start_tracking(page_name="Settings")- Logs (different page)start_tracking(page_name="Home")- Logs (navigated back)
If page_name is not provided or is empty, no page tracking event is logged.
Streamlit does not allow on_change / on_click on widgets inside a form except on st.form_submit_button. This library therefore does not attach per-widget analytics callbacks to inputs inside st.form(). Instead:
- One-time notice (
form_instrumentation_notice) — The first time a tracked widget is created inside a form in a session, the library logs a single event through the samelog_eventpath as every other analytics event, sosession_id,user_id, and optionalpage_namematch the rest of the stream. The human-readable explanation is inextra.message. There is usually nowidgeton this object after serialization (same as other events with no element). - Submit (
submit) — When the user pressesst.form_submit_button, asubmitevent is logged. The submit button appears underwidget. Registered field values at submit time are in top-levelextra:extra.form_idandextra.form_fields(each entry: id, type, label, value). Masking options apply to text inputs and text areas in that snapshot the same way as elsewhere. - If you pass your own
on_changeoron_clickon a widget inside a form, Streamlit may reject it or it conflicts with form rules; this package does not forward those developer callbacks for form widgets. Use callbacks onst.form_submit_buttonif you need custom logic on submit.
For privacy-sensitive applications, you can mask the values of text_input and text_area widgets in the logs by setting mask_text_input_values=True. When enabled, the actual input values will be replaced with "[REDACTED]" in the log output.
import streamlit as st
from streamlit_page_analytics import StreamlitPageAnalytics
with StreamlitPageAnalytics.track(
name="my-app",
session_id=f"{session_id}",
user_id=f"{user_id}",
mask_text_input_values=True, # Enable masking for text inputs
):
st.text_input("Password") # Value will be logged as "[REDACTED]"
st.text_area("Notes") # Value will be logged as "[REDACTED]"
st.selectbox("Option", ["A", "B"]) # Not affected, logs actual valueThe following Streamlit widgets are currently supported:
st.button- Click eventsst.form_submit_button- Submit events; combined snapshot of tracked form fields inextra.form_fieldswhen used insidest.form()st.checkbox- Change eventsst.radio- Change eventsst.selectbox- Change eventsst.multiselect- Change eventsst.slider- Change eventsst.select_slider- Change eventsst.text_input- Change eventsst.number_input- Change eventsst.text_area- Change eventsst.date_input- Change eventsst.time_input- Change eventsst.file_uploader- Change eventsst.color_picker- Change events
Use the sample analytics script to setup analytics on top of the logs can be quickly built using dynamic tables.
- DEVELOPMENT.md — Workflow, tasks, and how integration tests assert on JSON logs.
- LINTING.md — Linter configuration and commands.
License: Apache 2.0
This is not an official Snowflake product or feature.