查询日志时,查询结果内容过多会影响显示速度和查询体验。通过API接口进行分页查询可以指定查询结果分页显示,控制每次返回的数据量。

日志服务提供了分页的功能,不仅可以分页读取原始日志内容,也可以把SQL的计算结果分页读取到本地。开发者可以通过日志服务提供的SDK或者CLI,通过读数据接口分页读取日志。

分页方式

日志服务查询和分析功能支持在查询分析语句中同时实现关键字查询和查询结果的SQL分析。GetLogstoreLogs接口是日志服务提供的日志查询入口,您既可以根据关键字查询日志原始内容,也可以进行SQL计算,获取分析结果。查询分析语句中的查询部分和分析部分使用不同的分页方式。
  • 查询语句:使用关键字查询,获取原始日志内容。通过API中的参数offset和lines来分页获取所有内容。
  • 分析语句:使用SQL对查询结果进行分析,获取统计结果。通过SQL的limit语法来达到分页效果。

查询结果分页

在GetLogStoreLogs API中,参数offset和lines可以用来设置分页。
  • offset:指定从第一行开始读取日志。
  • lines:指定当前的请求读取多少行,该参数最大可设置为100,如果设置该参数大于100,则仍然返回100行。

在分页读取时,不停的增大offset,直到读取到某个offset后,获取的结果行数为0,并且结果的progress为complete状态,则认为读取到了全部数据。

查询分页代码示例

  • 分页的伪代码:
    offset = 0                           // 从第0行开始读取
    lines = 100                          //每次读取100行
    query = "status:200"                 //查询status字段包含200的所有日志
    while True:
         response = get_logstore_logs(query, offset, lines) // 执行读取请求
         process (response)                                 //调用自定义逻辑,处理返回的结果
         如果 response.get_count() == 0 && response.is_complete()   
             则读取结束,跳出当前循环
         否则
            offset += 100                           offset增加100,读取下一个100行
  • Python分页读取:
    详细案例请参考Python SDK
        endpoint = ''# 选择与上面步骤创建Project所属区域匹配的Endpoint
        accessKeyId = ''# 使用您的阿里云访问密钥AccessKeyId
        accessKey = ''# 使用您的阿里云访问密钥AccessKeySecret
        project = ''# 上面步骤创建的项目名称
        logstore = ''# 上面步骤创建的日志库名称
        client = LogClient(endpoint, accessKeyId, accessKey)
        topic = ""
        query = "index"
        From = int(time.time()) - 600
        To = int(time.time())
        log_line = 100
        offset = 0
        while True:
            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,每次获取100行数据。若需要读取更多数据,请使用offset分页。offset和lines只对关键字查询有效,若使用SQL查询,则无效。在SQL查询中返回更多数据,请使用limit语法。
            while (true) {
                GetLogsResponse res4 = null;
                // 对于每个 log offset,一次读取 10 行 log,如果读取失败,最多重复读取 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;
                }
    
            }
    					

分析结果分页

GetLogStoreLogs API参数中的offset和lines不支持用于SQL分析。如果按照上文分页读取原始内容的方式,遍历offset分页,那么每次SQL执行的结果都是一样的。如果在一次调用中获取全部的计算结果,可能会由于结果集太大而产生以下问题:

  • 网络上传输大量数据延时比较高。
  • 客户端的内存要保存大量的结果以供进一步处理,影响内存占用率。
为了解决SQL分页的问题,日志服务提供了标准SQL的limit分页语法
limit Offset, Line
  • Offset表示从第几行开始读取结果。
  • Line表示读取多少行。Line没有大小限制,但是如果一次读取太多,会影响网络延时和客户端的处理速度。

例如,分析语句* | selectcount(1) , url group by url 返回2000条日志,可以通过分页指定每次读取500行,共4次读取完成:

* | 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//每次读取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                        //offset增加100,读取下一个500行
  • Python程序代码:
        endpoint = ''# 选择与上面步骤创建Project所属区域匹配的Endpoint
        accessKeyId = ''# 使用您的阿里云访问密钥AccessKeyId
        accessKey = ''# 使用您的阿里云访问密钥AccessKeySecret
        project = ''# 上面步骤创建的项目名称
        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 = 0
        while True:
            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;
                // 对于每个 log offset,一次读取 500 行 log,如果读取失败,最多重复读取 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;
                }
    
            }