Journalisation et traçage pour Streamlit in Snowflake

Streamlit in Snowflake prend en charge la journalisation pour les runtimes des entrepôts et des conteneurs. Les runtimes d’entrepôt utilisent le cadre de télémétrie Snowflake pour capturer les messages du journal et les événements de trace dans une table d’événements. Les runtimes de conteneurs capturent les journaux que votre application émet en sortie standard et en erreur standard, les stockent dans la table d’événements du compte et fournissent à la fois les journaux de la console en direct et les vues des journaux historiques dans Snowsight.

Les deux runtimes stockent les journaux dans la table des événements au niveau du compte. Un administrateur de compte doit installer et configurer cette table d’événements avant de pouvoir capturer des journaux. Pour obtenir des instructions, voir Aperçu de la table d’événements.

  • Pour trouver la table d’événements configurée pour votre compte, exécutez :

    SHOW PARAMETERS LIKE 'event_table' IN ACCOUNT;
    
    Copy

Le tableau suivant compare la prise en charge de la journalisation et du traçage par runtime :

Fonctionnalité

Runtime d’entrepôt

Runtime de conteneur (prévisualisation)

Journalisation de la table d’événements

Pris en charge

Pris en charge

Traçage

Pris en charge

Non pris en charge

Journaux de console en direct dans Snowsight

Non pris en charge

Pris en charge

Journaux historiques dans Snowsight

Non pris en charge

Pris en charge

Journalisation du runtime de conteneur

Les applications Streamlit en mode conteneur s’exécutent dans un conteneur Snowpark Container Services. Snowflake capture automatiquement tout ce que votre application émet en sortie standard et en erreur standard, et le stocke dans la table d’événements du compte. Vous pouvez voir ces journaux sur Snowsight ou les interroger avecSQL.

Module de journalisation de Python

Utilisez le module logging intégré de Python pour émettre des messages de journal à partir de votre application. L’exemple suivant configure un journaliseur qui écrit les messages de niveau INFO et supérieur vers la sortie standard :

import logging
import sys

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
    stream=sys.stdout,
)

LOGGER = logging.getLogger("my_app")
Copy

Par ordre croissant de gravité, Python dispose des niveaux de journalisation suivants :

  • DEBUG

  • INFO

  • WARNING

  • ERROR

Définir le niveau sur INFO capture les messages INFO,WARNING et ERROR, mais pas les messages DEBUG.

Note

Par défaut, le module logging de Python écrit dans la sortie d’erreur standard (sys.stderr). Snowflake capture à la fois la sortie standard et les erreurs standard, de sorte que vos journaux sont capturés quel que soit le flux que vous utilisez. Le définition du flux sur sys.stdout est facultative mais recommandée, car l’erreur standard est classiquement réservée à la sortie d’erreur.

Après avoir configuré le journaliseur, vous pouvez l’utiliser pour enregistrer des messages dans le code de votre application. Il est courant de définir un journaliseur dans un module distinct et de l’importer ensuite dans le code de votre application :

source_directory/
├── my_logger.py
├── pyproject.toml
└── streamlit_app.py
Copy
import streamlit as st
from my_logger import LOGGER

LOGGER.info("Home page loaded")
st.title("My App")

if st.button("Run analysis"):
    LOGGER.info("Analysis button clicked")
    try:
        result = run_analysis()
        LOGGER.info("Analysis completed successfully")
    except Exception as e:
        LOGGER.error("Analysis failed: %s", e)
        st.error("Analysis failed: %s", e)
Copy

Journaux en direct dans Snowsight

Lorsque vous modifiez une application de runtime de conteneur dans Snowsight, un volet de journaux apparaît sous l’éditeur. Ce volet diffuse les messages du journal en temps réel au fur et à mesure que votre application les émet. Un bref historique des journaux les plus récents s’affiche lors de la première connexion.

Chaque entrée du journal affiche les informations suivantes :

Colonne

Description

Source

APP pour les journaux de votre processus Streamlit et les journaliseurs configurés par l’utilisateur, ou MANAGER pour les journaux du processus système qui gère le conteneur.

Level

Le niveau de gravité du message du journal (DEBUG, INFO, WARNING, ERROR).

Message

Contenu du message du journal.

Actions de journalisation en direct disponibles

Dans le coin supérieur droit du volet des journaux, vous pouvez rechercher et filtrer les journaux pour vous aider à trouver les informations dont vous avez besoin. Cela inclut la recherche de texte, le filtrage par source et le filtrage par niveau de gravité. Dans le menu à trois points, vous pouvez télécharger les journaux actuels, accéder aux journaux historiques ou effacer le volet de journalisation en direct. Lorsque vous videz le volet, les journaux actuels sont supprimés de votre vue actuelle mais pas de la table d’événements. Le rechargement immédiat de la page restaure les journaux les plus récents.

Comprendre les sources du journal

Les journaux des runtimes de conteneurs ont l’une des deux sources suivantes :

  • MANAGER : Le processus système à l’intérieur du conteneur qui prépare et exécute votre application. Les journaux du gestionnaire contiennent des messages concernant le téléchargement des fichiers de votre application depuis la zone de préparation, l’installation des dépendances Python et le démarrage du processus du serveur Streamlit. Si vous mettez à jour les fichiers de dépendances de votre application pendant que celle-ci est en cours d’exécution, le processus du gestionnaire réinstalle les dépendances et produit des journaux de gestionnaire supplémentaires.

  • APP : Journaux du processus du serveur Streamlit en cours d’exécution. Cela inclut les messages de vos journaliseurs Python configurés par l’utilisateur, du journaliseur intégré de Streamlit et de toute autre sortie que votre application écrit sur la sortie standard ou l’erreur standard.

La limite entre les sources est la commande streamlit run. Tout ce que fait le conteneur avant de démarrer le processus Streamlit produit des journaux MANAGER. Après le démarrage du processus Streamlit, la sortie de ce processus produit des journaux APP.

Afficher les journaux historiques dans Snowsight

Les étapes suivantes ne s’appliquent qu’aux applications de runtime de conteneur. Les applications de runtime d’entrepôt n’ont pas de volet de journaux.

  1. Connectez-vous à Snowsight.

  2. Dans le menu de navigation, sélectionnez Projects » Streamlit, puis sélectionnez votre application.

  3. Dans le coin supérieur droit de la page, sélectionnez Edit.

  4. Dans le coin supérieur droit du volet des journaux, sélectionnez le menu à trois points (Other actions) » Historical logs.

Cela ouvre la page de surveillance Snowpark Container Services du service qui s’exécute derrière votre application. La table des journaux affiche les colonnes suivantes :

Colonne

Description

Horodatage

L’horodatage du message du journal.

Instance ID

L’identificateur de l’instance de conteneur. C’est toujours 0 pour les applications Streamlit.

Conteneur

L’identificateur de l’instance de conteneur.

Flux

Si le journal a été émis sur la sortie standard (stdout) ou erreur standard (stderr).

Valeur

Le message de journal au format JSON qui comprend les champs "level", "message", "source" et "timestamp".

Pour plus d’informations sur la page de surveillance, voir Snowpark Container Services : services de surveillance.

Journaux de requêtes avec SQL

Vous pouvez interroger directement la table des événements pour analyser les journaux de votre application runtime de conteneur. La requête suivante récupère les journaux d’une application Streamlit spécifique :

SELECT
    TIMESTAMP,
    RECORD['severity_text']::VARCHAR AS level,
    VALUE::VARCHAR AS message,
    RESOURCE_ATTRIBUTES['snow.database.name']::VARCHAR AS database_name,
    RESOURCE_ATTRIBUTES['snow.schema.name']::VARCHAR AS schema_name,
    RESOURCE_ATTRIBUTES['snow.executable.name']::VARCHAR AS app_name,
    RECORD_ATTRIBUTES['log.iostream']::VARCHAR AS stream
FROM <event_table>
WHERE RESOURCE_ATTRIBUTES['snow.database.name'] = '<database_name>'
  AND RESOURCE_ATTRIBUTES['snow.schema.name'] = '<schema_name>'
  AND RESOURCE_ATTRIBUTES['snow.executable.name'] = '<app_name>'
  AND RECORD_TYPE = 'LOG'
  AND TIMESTAMP > DATEADD(hour, -1, CURRENT_TIMESTAMP())
ORDER BY TIMESTAMP DESC
LIMIT 100;
Copy

Remplacez <event_table> avec le nom de la table d’événements renvoyée par la commande SHOW PARAMETERS, et remplacez <database_name>, <schema_name> et <app_name> avec les valeurs de votre application Streamlit.

Astuce

Incluez un filtre TIMESTAMP dans vos requêtes de tables d’événements pour améliorer les performances. Les tables d’événements peuvent contenir un grand volume de données provenant de divers composants Snowflake.

Pour plus d’informations sur les colonnes de la table d’événements, voir Colonnes de table d’événements.

Journalisation du runtime d’entrepôt

Pour les applications Streamlit qui utilisent les runtimes d’entrepôt, vous pouvez capturer les messages de journal et suivre les événements de votre code d’application Streamlit au fur et à mesure de son exécution, puis analyser les résultats avec SQL, par exemple, pour analyser les erreurs. Pour plus d’informations, voir Journalisation, traçage et métriques.

Les runtimes d’entrepôt nécessitent que les niveaux de journalisation et de trace soient définis sur la base de données contenant votre application :

-- Set the log level for the database containing your app
ALTER DATABASE <database_name> SET LOG_LEVEL = INFO;

-- Set the trace level for the database containing your app
ALTER DATABASE <database_name> SET TRACE_LEVEL = ON_EVENT;
Copy

Exemple : Journalisation à partir d’une application de runtime d’entrepôt

import logging
import streamlit as st

logger = logging.getLogger("simple_logger")

# Write directly to the app
st.title("Simple Logging Example")

# Get the current credentials
session = st.connection('snowflake').session()

def get_log_messages_query() -> str:
    return """
            SELECT
                TIMESTAMP,
                RECORD:"severity_text"::VARCHAR AS SEVERITY,
                RESOURCE_ATTRIBUTES:"db.user"::VARCHAR AS USER,
                VALUE::VARCHAR AS VALUE
            FROM
                SAMPLE_EVENTS
            WHERE
                SCOPE:"name" = 'simple_logger'
            ORDER BY
                TIMESTAMP DESC;
            """

button = st.button("Log a message")

if button:
    try:
        logger.info("Logging an info message through Streamlit App.")
        st.success('Logged a message')
    except Exception as e:
        logger.error("Logging an error message through Streamlit App: %s",e)
        st.error('Logged an error')

sql = get_log_messages_query()

df = session.sql(sql).to_pandas()

with st.expander("**Show All Messages**"):
     st.dataframe(df, use_container_width=True)
Copy

Traçage (runtimes d’entrepôt uniquement)

Le traçage est pris en charge pour les runtimes d’entrepôt uniquement. Vous pouvez émettre des événements de trace à partir de votre application Streamlit, puis interroger la table des événements pour les analyser.

Note

L’exemple suivant nécessite l’installation du paquet snowflake-telemetry-python. Pour plus d’informations, voir Ajout d’une prise en charge du paquet de télémétrie.

import streamlit as st
import time
import random
from snowflake import telemetry

def sleep_function() -> int:
    random_time = random.randint(1, 10)
    time.sleep(random_time)
    return random_time

def get_trace_messages_query() -> str:
    return """
            SELECT
                TIMESTAMP,
                RESOURCE_ATTRIBUTES :"db.user" :: VARCHAR AS USER,
                RECORD_TYPE,
                RECORD_ATTRIBUTES
            FROM
                SAMPLE_EVENTS
            WHERE
                RECORD :"name" :: VARCHAR = 'tracing_some_data'
                OR RECORD_ATTRIBUTES :"logging_demo.tracing" :: VARCHAR = 'begin_span'
            ORDER BY
                TIMESTAMP DESC;
            """

def trace_message() -> None:
    execution_time = sleep_function()
    telemetry.set_span_attribute("logging_demo.tracing", "begin_span")
    telemetry.add_event(
        "tracing_some_data",
        {"function_name": "sleep_function", "execution_time": execution_time},
    )

# Write directly to the app
st.title("Simple Tracing Example")

# Get the current credentials
session = st.connection('snowflake').session()

button = st.button("Add trace event")

if button:
    with st.spinner("Executing function..."):
        trace_message()
        st.toast("Successfully log a trace message!", icon="✅")

sql = get_trace_messages_query()

df = session.sql(sql).to_pandas()

with st.expander("**Show All Trace Messages**"):
     st.dataframe(df, use_container_width=True)
Copy