大量の結果を返すクエリが実行されると、実行結果の表示に時間がかかり、ユーザーエクスペリエンスに大きく影響します。 この問題を解決するには、API を使用してページ分割クエリを実行します。1 度に返される結果の数を指定して、複数のページに分けて結果が返されるようにします。

Log Service のページ分割機能を使用すると、生ログの読み込みが複数ページに分けて読み込まれます。また、SQL 処理結果も複数ページに分けてローカルクライアントに読み込まれます。 開発者が API を使ってログデータを複数ページに分けてページごとに読み込むには、Log Service に用意されている SDK または CLI を使用します。

ページ分割の方法

Log Service の「検索/分析」機能では、キーワードでログをクエリ、また、クエリ結果に対して分析ステートメントを実行することができます。 GetLogStoreLogs は、Log Service に用意されている、ログをクエリするための API です。 本 API を使用して、生ログをキーワードでクエリ、SQL を実行、また、分析結果を取得することができます。 クエリ分析ステートメントでも、クエリステートメントと分析ステートメントでは、ページを分割する方法が異なります。
  • クエリステートメント: キーワードでログをクエリ、また、生ログを取得します。 クエリステートメントに GetLogStoreLogs の offset および lines パラメータを設定し、ログを複数ページに分けて取得します。
  • 分析ステートメント: SQL ステートメントでクエリ結果を分析、また、分析結果を取得します。 分析ステートメントの分析結果を複数ページに分けて取得するには、SQL のLIMIT 構文を使用します。

クエリ結果をページ分割

ページ分割を設定するには、GetLogStoreLogs の offset および lines パラメータを指定します。
  • offset: ログの読み込みを開始する行を指定
  • lines: クエリリクエストの読み込む行数を指定 (指定可能な最大値: 100、 100 以上に指定した場合は、100 行のみが返されます)

GetLogStoreLogs でページごとにデータを読み込む場合、GetLogStoreLogs がすべてのデータが読み込んだと判断されるまで(読み込み行数が 0 になり、結果読み込みの進行ステータスが完了となるまで)offset パラメータ値は増加し続けます。

クエリ結果をページ分割するサンプルコード

  • クエリ結果をページ分割する疑似コード
    offset = 0// ログを 0 行目から読み込む
    lines = 100// クエリごとに 100 行読み込む
    query = "status:200"// whileTrue 繰り返し処理を使用してステータスフィールドの値が 200 であるログすべてをクエリ
         response = get_logstore_logs(query, offset, lines) // 読み込みリクエストを実行
         process (response)  // 返された結果を処理するカスタムロジックの呼び出し
         次の条件に合致した場合、繰り返し処理から抜けて読み込み処理が終了する   
             response.get_count() == 0 && response.is_complete()
         条件に一致しない場合は、offset パラメータ値に 100 加算され、次の 100 行が読み込まれる
            offset += 100
  • クエリ結果をページ分割する Python コード
    詳細は、「Python SDK」をご参照ください。
    endpoint = '' # 前の手順で作成したプロジェクトの属しているリージョンのエンドポイントを指定
        accessKeyId = '' # お客様の Alibaba Cloud アカウントの AccessKeyId を指定
        accessKey = '' # お客様の Alibaba Cloud アカウントの AccessKeySecret を指定
        project = '' # 前の手順で作成したプロジェクトの名前を指定
        logstore = '' # 前の手順で作成した Logstore の名前を指定
        client = LogClient(endpoint, accessKeyId, accessKey)
        topic = ""
        query = "index"
        From = int(time.time()) - 600
        To = int(time.time())
        log_line = 100
        offset = 0whileTrue:
            res4 = Nonefor retry_time in range(0, 3):
                req4 = GetLogsRequest(project, logstore, From, To, topic, query, log_line, offset, False)
                res4 = client.get_logs(req4)
                if res4 isnotNoneand res4.is_completed():
                    break
                time.sleep(1)
            offset += 100if res4.is_completed() && res4.get_count() == 0:
                  break;
            if res4 isnotNone:
                res4.log_print() # クエリ結果を処理
  • クエリ結果をページ分割する Java コード

    詳細は、「Java SDK」をご参照ください。

            int log_offset = 0;
            int log_line = 100;// log_line に指定可能な最大値: 100 (1 度に最大 100 行読み込むことができる。 さらにデータを読み込むには、ページ分割の offset を使用する。 offset および lines パラメータは、キーワードによるクエリでのみ有効であり、SQL ステートメントによるクエリには無効です。 SQL ステートメントによるクエリでさらにデータを読み込むには、LIMIT 構文を使用します。
            while (true) {
                GetLogsResponse res4 = null;
                // 各ログの offset は 1 度に 10 行読み込まれます。 読み込みに失敗した場合、最大 3 回まで読み込みが再試行されます。
                for (int retry_time = 0; retry_time < 3; retry_time++) {
                    GetLogsRequest req4 = new GetLogsRequest(project, logstore, from, to, topic, query, log_offset,
                            log_line, false);
                    res4 = client.GetLogs(req4);
    
                    if (res4 ! = null && res4. IsCompleted()) {
                        break;
                    }
                    Thread.sleep(200);
                }
                System.out.println("Read log count:" + String.valueOf(res4. GetCount()));
                log_offset += log_line;
                if (res4. IsCompleted() && res4. GetCount() == 0) {
                            break;
                }
    
            }
    						

分析結果をページ分割

SQL 分析には、GetLogStoreLogs の offset および lines パラメータは対応していません。 上記の方法と同様に、ページ単位で生ログを読み込むためにoffset パラメータを指定しても、SQL 分析で返される結果は毎回すべて同じものになります。 とはいえ、分析ステートメントの実行結果がいっぺんに返されると、結果が多い場合に、以下の問題が発生する可能性があります。

  • 結果が多い場合、送信に時間がかかり、遅延も大きくなります。
  • 分析結果は、クライアントのメモリに保存され、後続処理に使用されます。したがって、メモリ使用率は高くなります。
Log Service で SQL 分析結果をページ分割するには、標準 SQL の LIMIT 構文を使用します。
limit Offset, Line
  • Offset: 読み込みを開始する行
  • Line: ステートメントで読み込まれる行数 (指定可能な値: 制限なし。 ただし、分析ステートメントで大量の行が読み込まれると、遅延は大きく、処理速度も遅くなります)

たとえば、分析ステートメント* | selectcount(1) , url group by url を実行した結果、2,000 ログが返されるとします。次のステートメントでは、2,000 ログが 4 ページに分けて読み込まれます。1 ページにつき、500 行が読み込まれます。

* | selectcount(1) , url  group by url  limit 0, 500
* | selectcount(1) , url  group by url  limit 500, 500
* | selectcount(1) , url  group by url  limit 1000, 500
* | selectcount(1) , url  group by url  limit 1500, 500

分析結果をページ分割するサンプルコード

  • SQL 分析結果をページ分割する擬似コード
    offset = 0// ログの 0 行目から読み込む
    lines = 500// 1 度に 500 行読み込む
    query = "* | select count(1) , url  group by url  limit "whileTrue:
         real_query = query + offset + "," +  lines
         response = get_logstore_logs(real_query) // 読み込みリクエストを実行
         process (response)  // 返された結果を処理するカスタムロジックの呼び出し
         次の条件に合致した場合、繰り返し処理から抜けて読み込み処理が完了する   
             response.get_count() == 0
         条件に合致しない場合、offset パラメータ値に 500 が加算され、次の 500 行が読み込まれる
            offset += 500
  • 分析結果をページ分割する Python コード
    endpoint = '' # 前の手順で作成したプロジェクトの属しているリージョンのエンドポイントを指定
        accessKeyId = '' # お客様の Alibaba Cloud アカウントの AccessKeyId を指定
        accessKey = '' # お客様の Alibaba Cloud アカウントの AccessKeySecret を指定
        project = '' # 前の手順で作成したプロジェクトの名前を指定
        logstore = '' # 前の手順で作成した Logstore の名前を指定
        client = LogClient(endpoint, accessKeyId, accessKey)
        topic = ""
        origin_query = "* | select count(1) , url  group by url  limit "
        From = int(time.time()) - 600
        To = int(time.time())
        log_line = 100
        offset = 0whileTrue:
            res4 = None
            query = origin_query + str(offset) + " , " + str(log_line)
            for retry_time in range(0, 3):
                req4 = GetLogsRequest(project, logstore, From, To, topic, query)
                res4 = client.get_logs(req4)
                if res4 isnotNoneand res4.is_completed():
                    break
                time.sleep(1)
            offset += 100if res4.is_completed() && res4.get_count() == 0: 
                  break;
            if res4 isnotNone:
                res4.log_print() # 分析結果を処理
  • 分析結果をページ分割する Java コード
    int log_offset = 0;
            int log_line = 500;
            String origin_query = "* | select count(1) , url  group by url  limit "while (true) {
                GetLogsResponse res4 = null;
                // 各 offset につき、500 行のログが読み込まれる (ログの読み込みに失敗した場合、最大 3 回まで再試行)
                query = origin_query + log_offset + "," + log_line;
                for (int retry_time = 0; retry_time < 3; retry_time++) {
                    GetLogsRequest req4 = new GetLogsRequest(project, logstore, from, to, topic, query);
                    res4 = client.GetLogs(req4);
    
                    if (res4 ! = null && res4. IsCompleted()) {
                        break;
                    }
                    Thread.sleep(200);
                }
                System.out.println("Read log count:" + String.valueOf(res4. GetCount()));
                log_offset += log_line;
                if (res4. GetCount() == 0) {
                            break;
                }
    
            }