Streamlit in Snowflake 用のログおよびトレース

|sis|は、ウェアハウスとコンテナランタイムの両方のログ記録をサポートしています。ウェアハウスランタイムは、Snowflakeテレメトリフレームワークを使用してログメッセージをキャプチャし、イベントをトレースしてイベントテーブルに記録します。コンテナランタイムは、アプリが標準出力と標準エラーに出力するログをキャプチャし、アカウントのイベントテーブルに保存します。また、|sf-web-interface|のライブコンソールログと履歴ログビューの両方を提供します。

どちらのランタイムもアカウントレベルのイベントテーブルにログを保存します。ログをキャプチャするには、アカウント管理者がこのイベントテーブルを設定し、構成する必要があります。手順については、 イベントテーブルの概要 をご参照ください。

  • アカウント用に構成されたイベントテーブルを見つけるには、次を実行します。

    SHOW PARAMETERS LIKE 'event_table' IN ACCOUNT;
    
    Copy

次のテーブルは、ランタイム別にログ記録とトレースのサポートを比較したものです。

機能

ウェアハウスランタイム

コンテナランタイム(プレビュー)

イベントテーブルのログ

サポート対象

サポート対象

トレース

サポート対象

サポート対象外

|sf-web-interface|のライブコンソールログ

サポート対象外

サポート対象

|sf-web-interface|の履歴ログ

サポート対象外

サポート対象

コンテナランタイムのログ

コンテナランタイムのStreamlitアプリは|SPCS|コンテナ内で実行されます。Snowflakeは、アプリが標準出力と標準エラーに出力するものを自動的にキャプチャし、アカウントのイベントテーブルに格納します。これらのログは|sf-web-interface|で表示したりSQLでクエリしたりできます。

Pythonのログ記録モジュール

Pythonの組み込み``logging``モジュールを使用して、アプリからログメッセージを出力します。次の例では、INFOレベル以上のメッセージを標準出力に書き込むロガーを構成しています。

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

重大度が低いものから高いものの順に、Pythonには以下のログレベルがあります。

  • DEBUG

  • INFO

  • WARNING

  • ERROR

レベルをINFOに設定すると、INFO、WARNING、ERRORメッセージがキャプチャされますが、DEBUGメッセージはキャプチャされません。

注釈

デフォルトでは、Pythonの``logging``モジュールは標準エラー(sys.stderr)に書き込みます。Snowflakeは標準出力と標準エラーの両方をキャプチャするため、使用するストリームに関係なくログがキャプチャされます。ただし、標準エラーは慣例的にエラー出力用に予約されているため、ストリームを``sys.stdout``に設定することがオプションとして推奨されます。

ロガーを構成したら、アプリコード全体でメッセージをログに記録するために使用できます。ロガーは別のモジュールで定義し、アプリコードにインポートするのが一般的です。

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

|sf-web-interface|のライブログ

|sf-web-interface|でコンテナランタイムアプリを編集する場合、エディターの下にログペインが表示されます。このペインには、アプリがログメッセージを出力すると、リアルタイムでログメッセージがストリーミングされます。最初に接続したときに、最新のログの簡単な履歴が表示されます。

各ログエントリは、次の情報を示します。

説明

Source

``APP``(Streamlitプロセスおよびユーザー構成のロガーからのログ)または``MANAGER``(コンテナを管理するシステムプロセスからのログ)。

Level

ログメッセージの重大度レベル(DEBUG、INFO、WARNING、ERROR)。

Message

ログメッセージのコンテンツ。

利用可能なライブログアクション

ログペインの右上隅で、ログを検索およびフィルターして、必要な情報を見つけることができます。これには、テキスト検索、ソースによるフィルタリング、重大度レベルによるフィルタリングが含まれます。3つのドットメニューで、現在のログをダウンロードしたり、過去のログに移動したり、ライブログペインをクリアしたりできます。ペインをクリアすると、現在のログは現在のビューから削除されますが、イベントテーブルからは削除されません。ページをすぐにリロードすると、最新のログが復元されます。

ログソースの理解

コンテナランタイムアプリからのログには、2つのソースのいずれかがあります。

  • MANAGER:アプリを準備して実行するコンテナ内のシステムプロセス。マネージャーログには、ステージからのアプリファイルのダウンロード、Python依存関係のインストール、Streamlitサーバープロセスの開始に関するメッセージが含まれます。アプリの実行中にアプリの依存関係ファイルを更新すると、マネージャープロセスは依存関係を再インストールし、追加のマネージャーログを生成します。

  • APP:実行中のStreamlitサーバープロセスからのログ。これには、ユーザー設定のPythonロガー、Streamlitの組み込みロガーからのメッセージ、およびアプリが標準出力や標準エラーに書き込むその他の出力が含まれます。

ソース間の境界は``streamlit run``コマンドです。Streamlitプロセスを開始する前にコンテナが行うすべてのタスクは、``MANAGER``ログを生成します。Streamlitプロセスが開始されると、そのプロセスからの出力は``APP``ログを生成します。

|sf-web-interface|での履歴ログの表示

次のステップは、コンテナランタイムアプリにのみ適用されます。ウェアハウスランタイムアプリにはログペインがありません。

  1. Snowsight にサインインします。

  2. ナビゲーションメニューで、 Projects » Streamlit を選択してから、アプリを選択します。

  3. ページの右上隅で、 Edit を選択します。

  4. ログペインの右上隅で、3つのドットメニュー(Other actions)|raa| :ui:`Historical logs`を選択します。

これにより、アプリの背後で実行されているサービスの|SPCS|モニタリングページが開きます。ログテーブルには、次の列が表示されます。

説明

タイムスタンプ

ログメッセージのタイムスタンプ。

インスタンス ID

コンテナインスタンスの識別子。Streamlitアプリではこれは常に``0``になります。

コンテナー

コンテナインスタンスの識別子。

ストリーム

ログが標準出力(stdout)に出力されたか、標準エラー(stderr)に出力されたか。

"level""message""source"``"timestamp"``フィールドを含むJSON形式のログメッセージ。

モニタリングページの詳細については、:doc:`/developer-guide/snowpark-container-services/monitoring-services`をご参照ください。

SQLでのログのクエリ

イベントテーブルを直接クエリして、コンテナランタイムアプリのログを分析できます。以下のクエリは、特定のStreamlitアプリからログを取得します。

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

<event_table>``をSHOW PARAMETERSコマンドで返されたイベントテーブル名に置き換え、``<database_name><schema_name>``<app_name>``をStreamlitアプリの値に置き換えます。

Tip

パフォーマンスを向上させるには、イベントテーブルのクエリにTIMESTAMPフィルターを含めます。イベントテーブルには、さまざまなSnowflakeコンポーネントからの大量のデータが含まれる場合があります。

イベントテーブル列の詳細については、:doc:`/developer-guide/logging-tracing/event-table-columns`をご参照ください。

ウェアハウスランタイムのログ記録

ウェアハウスランタイムを使用するStreamlitアプリの場合、Streamlitアプリコードの実行時にログメッセージやトレースイベントをキャプチャし、その結果をSQLで分析することで、例えばエラーを分析することができます。詳細については、 ロギング、トレース、メトリクス をご参照ください。

ウェアハウスのランタイムでは、アプリを含むデータベースにログとトレースのレベルを設定する必要があります。

-- 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

例:ウェアハウスランタイムアプリからのログ記録

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

トレース(ウェアハウスランタイムのみ)

トレースはウェアハウスランタイムでのみサポートされています。Streamlitアプリからトレースイベントを発行し、イベントテーブルをクエリして分析することができます。

注釈

以下の例では、 snowflake-telemetry-python パッケージをインストールする必要があります。詳細については、 テレメトリパッケージのサポートの追加 をご参照ください。

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