使用命令统计nginx日志access.log中某个接口的QPS

2023-11-13 17:37:34 浏览数 (1)

问题

我们在平时的工作和开发中,会经常遇到这个问题:

从nginx的日志access.log中统计getVideoInfo接口的QPS。日志格式为:2019-07-12T11:16:23 0800|127.0.0.1|-|-|GET|http|127.0.0.1|/getVideoInfo?id=1

日志内容大致为:

代码语言:txt复制
2019-07-12T11:16:23 0800|127.0.0.1|-|-|GET|http|127.0.0.1|/getVideoInfo?id=1
2019-07-12T11:16:45 0800|127.0.0.1|-|-|GET|http|127.0.0.1|/getVideoInfo?id=1
2019-07-12T11:16:58 0800|127.0.0.1|-|-|GET|http|127.0.0.1|/getVideoInfo?id=1
2019-07-12T11:17:01 0800|127.0.0.1|-|-|GET|http|127.0.0.1|/getVideoInfo?id=1
2019-07-12T11:17:20 0800|127.0.0.1|-|-|GET|http|127.0.0.1|/getVideoInfo?id=1

解决思路

首先nginx的日志是按照时间顺序的。因此计算QPS,只需要先统计条数,再计算时间差,二者相除就可以得到。

思路一:使用wc命令

  1. 第一步: 使用wc命令获取条数
代码语言:shell复制
wc -l access.log | awk '{print $1}'
  1. 统计第一条和最后一条的时间并格式化成时间戳
代码语言:shell复制
// 第一条日志时间戳
date -d "$(head -n 1 access.log | awk -F "|" '/getVideoInfo/ {print $1}')"  %s

// 最后一条日志时间戳
date -d "$(tail -n 1 access.log | awk -F "|" '/getVideoInfo/ {print $1}')"  %s
  1. 计算QPS完整命令
代码语言:shell复制
count=$(wc -l access.log | awk '{print $1}'); start=$(date -d "$(head -n 1 access.log | awk -F "|" '/getVideoInfo/ {print $1}')"  %s); end=$(date -d "$(tail -n 1 access.log | awk -F "|" '/getVideoInfo/ {print $1}')"  %s); t=$(($end-$start));  qps=$(echo "scale=2; $count/$t" | bc); printf "%.2fn" $qps

思路二: 使用awk命令

  1. 第一步,遍历统计条数
代码语言:shell复制
awk -F "|" '$8=="/getVideoInfo?id=1" {count  } END {print count}' access.log
  1. 加入统计第一条和最后一条时间计算
代码语言:shell复制
-v start="$(date -d "$(head -n 1 access.log | grep "getVideoInfo" | cut -d "|" -f 1)"  %s)" -v end="$(date -d "$(tail -n 1 access.log | grep "getVideoInfo" | cut -d "|" -f 1)"  %s)"
  1. 计算QPS完整命令
代码语言:shell复制
awk -F "|" -v start="$(date -d "$(head -n 1 access.log | grep "getVideoInfo" | cut -d "|" -f 1)"  %s)" -v end="$(date -d "$(tail -n 1 access.log | grep "getVideoInfo" | cut -d "|" -f 1)"  %s)" '$8=="/getVideoInfo?id=1" {count  } END {qps=count/(end-start); print qps}' access.log

使用脚本处理

使用Python脚本

代码语言:python代码运行次数:0复制
import re
from datetime import datetime

log_file = "access.log"
target_api = "/getVideoInfo"

# 统计时间范围
start_time = datetime.strptime("2019-07-12T00:00:00 0800", "%Y-%m-%dT%H:%M:%S%z")
end_time = datetime.strptime("2019-07-12T23:59:59 0800", "%Y-%m-%dT%H:%M:%S%z")

# 统计请求数
qps = 0

with open(log_file, "r") as file:
    for line in file:
        # 解析日志行
        parts = line.split("|")
        timestamp = datetime.strptime(parts[0], "%Y-%m-%dT%H:%M:%S%z")
        request_method = parts[4]
        request_url = parts[7]

        # 判断是否为目标接口的 GET 请求
        if request_method == "GET" and request_url.startswith(target_api):
            # 判断时间范围
            if start_time <= timestamp <= end_time:
                qps  = 1

print("QPS for", target_api, ":", qps)

在上述示例中,我们首先定义了日志文件路径(log_file)和目标接口路径(target_api)。然后,我们指定了统计的时间范围(start_time 和 end_time)。

接下来,我们打开日志文件并逐行解析每个日志条目。我们使用 "|" 分隔符将每行日志拆分为不同的字段,并提取时间戳、请求方法和请求URL。

然后,我们检查请求方法是否为 "GET",并且请求URL是否以目标接口路径开头。如果满足条件,我们进一步检查时间戳是否在指定的时间范围内,并将符合条件的请求计数加1。

最后,我们打印出统计结果,即目标接口的 QPS。

使用go来实现

代码语言:go复制
package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
    "regexp"
    "strings"
    "time"
)

func main() {
    logFile := "access.log"
    targetAPI := "/getVideoInfo"

    // 统计时间范围
    startTime, _ := time.Parse("2006-01-02T15:04:05-0700", "2019-07-12T00:00:00 0800")
    endTime, _ := time.Parse("2006-01-02T15:04:05-0700", "2019-07-12T23:59:59 0800")

    // 统计请求数
    qps := 0

    file, err := os.Open(logFile)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        line := scanner.Text()

        // 解析日志行
        parts := strings.Split(line, "|")
        timestamp, _ := time.Parse("2006-01-02T15:04:05-0700", parts[0])
        requestMethod := parts[4]
        requestURL := parts[7]

        // 判断是否为目标接口的 GET 请求
        if requestMethod == "GET" && strings.HasPrefix(requestURL, targetAPI) {
            // 判断时间范围
            if timestamp.After(startTime) && timestamp.Before(endTime) {
                qps  
            }
        }
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }

    fmt.Println("QPS for", targetAPI, ":", qps)
}

使用shell脚本实现

代码语言:shell复制
#!/bin/bash

log_file="access.log"
target_api="/getVideoInfo"

# 统计时间范围
start_time=$(date -d "2019-07-12 00:00:00"  %s)
end_time=$(date -d "2019-07-12 23:59:59"  %s)

# 统计请求数
qps=0

while IFS= read -r line
do
    # 解析日志行
    timestamp=$(echo "$line" | awk -F '|' '{print $1}')
    request_method=$(echo "$line" | awk -F '|' '{print $5}')
    request_url=$(echo "$line" | awk -F '|' '{print $8}')

    # 判断是否为目标接口的 GET 请求
    if [[ "$request_method" == "GET" && "$request_url" == *"$target_api"* ]]; then
        # 判断时间范围
        timestamp=$(date -d "$timestamp"  %s)
        if [[ "$timestamp" -ge "$start_time" && "$timestamp" -le "$end_time" ]]; then
            ((qps  ))
        fi
    fi
done < "$log_file"

echo "QPS for $target_api: $qps"

使用PHP实现

代码语言:php复制
<?php

$logFile = "access.log";
$targetAPI = "/getVideoInfo";

// 统计时间范围
$startTime = strtotime("2019-07-12 00:00:00");
$endTime = strtotime("2019-07-12 23:59:59");

// 统计请求数
$qps = 0;

$file = fopen($logFile, "r");
if ($file) {
    while (($line = fgets($file)) !== false) {
        // 解析日志行
        $parts = explode("|", $line);
        $timestamp = strtotime($parts[0]);
        $requestMethod = $parts[4];
        $requestURL = $parts[7];

        // 判断是否为目标接口的 GET 请求
        if ($requestMethod == "GET" && strpos($requestURL, $targetAPI) !== false) {
            // 判断时间范围
            if ($timestamp >= $startTime && $timestamp <= $endTime) {
                $qps  ;
            }
        }
    }

    fclose($file);
} else {
    echo "Failed to open the log file.";
    exit;
}

echo "QPS for $targetAPI: $qpsn";

0 人点赞