5

使用Python解析Nginx日志

 8 months ago
source link: https://www.biaodianfu.com/python-parse-nginx-logs.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

最近看了眼Nginx的日志,发现个人博客的日志上有很多爬虫在抓数据,还有在扫描漏洞的,搞不明白普普通通的一个个人博客得罪了谁。于是决定做一个简单的Nginx日志解析工具来稍微分析下数据。

Nginx的格式

Nginx日志对于统计、系统服务排错很有用。Nginx日志主要分为两种:access_log(访问日志)和error_log(错误日志)。通过访问日志我们可以得到用户的IP地址、浏览器的信息,请求的处理时间等信息。错误日志记录了访问出错的信息,可以帮助我们定位错误的原因。本文将详细描述一下如何配置Nginx日志。

nginx-log-files.jpg

access_log

访问日志主要记录客户端的请求。客户端向Nginx服务器发起的每一次请求都记录在这里。客户端IP,浏览器信息,referer,请求处理时间,请求URL等都可以在访问日志中得到。当然具体要记录哪些信息,你可以通过log_format指令定义。

Nginx预定义了名为combined日志格式,如果没有明确指定日志格式默认使用该格式:

log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

如果不想使用Nginx预定义的格式,可以通过log_format指令来自定义。下面是log_format指令中常用的一些变量:

字段 含义 示例
占位符
body_bytes_sent 响应body字节数 3650
bytes_sent 响应总字节数 175
host IP或域名(不包括端口) 10.10.10.14
http_host IP或域名(包括端口) 10.10.10.14:81
http_referer referer信息 http://10.10.10.14/
http_user_agent UA信息 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36
http_x_forwarded_for XFF信息 192.168.1.1
remote_addr 客户端地址 10.10.10.1
remote_user 客户端认证用户名 admin
request 请求URI和协议 GET /favicon.ico HTTP/1.1
request_body 请求的body
request_length 请求长度 571
request_method 请求方法 GET
request_time 请求处理时间 0.000
response_body 返回的body
response_header_data 响应头数据
schema 协议 http
server_name 虚拟主机名称
server_port 服务器端口
server_protocol 服务器协议
ssl_cipher 交换数据中的算法
ssl_protocol SSL协议版本
status 返回状态码 404
time_local 时间戳 16/Jun/2019:23:29:50 -0400
upstream_addr 后端提供服务地址
upstream_connect_time 与服务器连接所花费的时间
upstream_response_time 后端处理时间
upstream_status upstream状态 200

以下为一个默认记录格式下的请求记录:

123.6.49.9 - - [28/Dec/2023:00:26:02 +0800] "GET /aic-bic.html HTTP/2.0" 200 16923 "http://baidu.com/" "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36"
123.6.49.9 - - [28/Dec/2023:00:26:02 +0800] "GET /aic-bic.html HTTP/2.0" 200 16923 "http://baidu.com/" "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36"

error_log

Nginx 的 error.log 记录了服务器在运行过程中遇到的错误和异常信息,这些信息对于管理员来说是非常重要的调试和排查工具。error.log 中包含的信息通常可以帮助管理员了解服务器的状态和运行情况,以及定位问题所在。下面是 error.log 中常见的几类信息:

  • 启动和关闭信息:log 中会记录服务器的启动和关闭信息,包括启动时间、运行时间、关闭原因等。这些信息对于管理员来说是了解服务器状态的重要参考。
  • 请求处理错误:如果服务器在处理请求时出现错误,log 会记录相关的错误信息,包括请求的 URL、客户端 IP、错误类型等。这些信息可以帮助管理员定位问题所在,如请求超时、请求大小超出限制等。
  • 连接和网络错误:如果服务器在处理连接或网络时出现错误,log 会记录相关的错误信息,如不能连接到后端服务器、连接超时等。这些信息可以帮助管理员找到网络问题所在。
  • 服务器错误:如果服务器本身出现错误,如内存不足、磁盘满了等,log 会记录相应的错误信息。这些信息可以帮助管理员及时发现并解决服务器问题。
  • 安全问题:如果服务器受到攻击或发现安全问题,log 也会记录相关信息,如恶意请求、攻击尝试等。这些信息对于管理员来说是保护服务器安全的重要参考。

需要注意的是,error.log 记录的信息可能会包含敏感信息,如客户端 IP、请求 URL 等,因此需要妥善保护日志文件的访问权限,以免被未授权的用户查看或利用。

Python解析Nginx日志

这次要解析的主要是针对access.log进行解析,解析日志代码如下:

import re
import pandas as pd
with open('data/www.biaodianfu.com.access.log', 'r') as file:
logs = file.readlines()
log_pattern = r'(\d+\.\d+\.\d+\.\d+) - (.+?) \[(.+?)\] "(.+?)" (\d+) (\d+) "(.+?)" "(.+?)"'
parsed_logs = []
for log in logs:
match = re.match(log_pattern, log)
if match:
parsed_logs.append(match.groups())
df = pd.DataFrame(parsed_logs, columns=['ip', 'remote_user', 'time_local', 'request', 'status', 'body_bytes_sent',
'http_referer', 'user_agent'])
df['time_local'] = pd.to_datetime(df['time_local'], format='%d/%b/%Y:%H:%M:%S %z').dt.tz_localize(None)
df[['method', 'path', 'protocol']] = df['request'].str.split(' ', n=2, expand=True)
def judge_request_type(path):
if path:
path = path.split('?')[0]
if path.endswith(('.jpg', '.jpeg', '.png', '.gif', '.svg')):
return '图片'
elif path.endswith(('/feed')):
return 'Feed'
elif path.endswith(('.html', '.php')):
return '网页'
elif path.endswith('.js'):
return 'JavaScript文件'
elif path.endswith('.css'):
return 'CSS文件'
elif path.endswith(('.woff2', '.ttf')):
return '字体'
else:
return '其他'
else:
return '首页'
df['request_type'] = df['path'].apply(judge_request_type)
# 调整显示设置
# pd.set_option('display.max_columns', 10)
# pd.set_option('display.max_rows', 50)
# pd.set_option('display.width', 1000)
# pd.set_option('display.max_colwidth', 50)
# print(df.head())
df.to_excel('nginx-log.xlsx')
import re
import pandas as pd

with open('data/www.biaodianfu.com.access.log', 'r') as file:
    logs = file.readlines()
    log_pattern = r'(\d+\.\d+\.\d+\.\d+) - (.+?) \[(.+?)\] "(.+?)" (\d+) (\d+) "(.+?)" "(.+?)"'
    parsed_logs = []
    for log in logs:
        match = re.match(log_pattern, log)
        if match:
            parsed_logs.append(match.groups())
    df = pd.DataFrame(parsed_logs, columns=['ip', 'remote_user', 'time_local', 'request', 'status', 'body_bytes_sent',
                                            'http_referer', 'user_agent'])
    df['time_local'] = pd.to_datetime(df['time_local'], format='%d/%b/%Y:%H:%M:%S %z').dt.tz_localize(None)
    df[['method', 'path', 'protocol']] = df['request'].str.split(' ', n=2, expand=True)


    def judge_request_type(path):
        if path:
            path = path.split('?')[0]
            if path.endswith(('.jpg', '.jpeg', '.png', '.gif', '.svg')):
                return '图片'
            elif path.endswith(('/feed')):
                return 'Feed'
            elif path.endswith(('.html', '.php')):
                return '网页'
            elif path.endswith('.js'):
                return 'JavaScript文件'
            elif path.endswith('.css'):
                return 'CSS文件'
            elif path.endswith(('.woff2', '.ttf')):
                return '字体'
            else:
                return '其他'
        else:
            return '首页'


    df['request_type'] = df['path'].apply(judge_request_type)

    # 调整显示设置
    # pd.set_option('display.max_columns', 10)
    # pd.set_option('display.max_rows', 50)
    # pd.set_option('display.width', 1000)
    # pd.set_option('display.max_colwidth', 50)
    # print(df.head())

    df.to_excel('nginx-log.xlsx')

这段代码的主要逻辑是定义了一个正则表达式 log_pattern,用于匹配 Nginx 日志的各个字段,并将它们作为一个元组添加到 parsed_logs 列表中。然后将数据转换为Pandas的DataFrame,再进行一些逻辑上的处理。然后保存到Excel中。由于数据量较少后续统计在Excel中进行。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK