Nim 清理 Nginx access log,删除不需的行

现在人工智能兴起,网站的爬虫多起来了,并且有的爬虫如 Claude 不讲“武德”,不断更换访问网站的 IP 地址

海云青飞 官方网站 https://www.tuenhai.com 用功能简单但速度很快的 visitors 统计网站访问数据,它不能很好地把爬虫的访问识别出来,因此它报告的访客数据就会不准确

以前,我用 vi 删除网站 Nginx access.log 中的某些行,操作如下:

首先 ssh 登录服务器,然后执行命令:

sudo vi /home/tuenhai.com/logs/tuenhai_access.log
:g/Bot\|pider\|rawler\|bot\/\|bot\"\|bot\-\|\.php\| 404 /d
ZZ

再用 visitors 统计网站访问数据

目前 海云青飞 在学习 Nim 编程语言,我提倡在实践中学习,能用 Nim 做的事情尽量让 Nim 去做。今天我写了个清理 web log 的 Nim 程序。在写这个程序中,我注重以下几点:

  • 指明使用的符号从哪个库导入

    Nim 的标准库丰富、强大,只是我以前很少写 Nim 程序,因此对标准库并不怎么熟悉。我在程序开头导入符号的做法有助于我熟悉 Nim 标准库 API

  • 符号的命名尽量做到自解释。就是人或机器人看到这个名字就能大概猜到它是干什么的

  • 还得写上简洁的明文注释


现在,我只要在服务器上执行命令:

sudo clean_web_log

就能依据 clean_web_log.json 中的配置迅速把网站访问记录 Nginx access_log 清理一遍,比以前我用 vi 手动删除行要快速、方便 N 倍

clean_web_log.nim

#[

# 海云青飞 https://www.tuenhai.com
# 2024-09-21

 Nim 编程语言 2.0

批量删除网站存取日志中不需要的行

用法:

- 在可执行文件同目录创建 .json 配置文件,文件名同可执行文件。示例:

   json:
   {
     "logEnds": "access.log",
     "webLogDir": "/home/tuenhai.com/log",
     "spamMarking" : ["Bot", "bot/", "bot\"", "bot-", ".php", " 404 ", "pider", "rawler"]
   }

- 运行程序
]#

import json
from os import splitFile, walkDir, getAppFilename, dirExists, `/`
from std/syncio import writeFile
from sequtils import mapIt
from strutils import endsWith, splitLines, contains, join
# "import doAssert" is unnecessary?
from std/assertions import doAssert

let
  json_key_logEnds = "logEnds"
  json_key_webLogDir = "webLogDir"
  json_key_spamMarking = "spamMarking"

# .json 格式配置文件与可执行文件同目录且同名,只是后缀不同
proc configFilePath(): string =
  let (dir, name, _) = splitFile(getAppFilename())
  dir / name & ".json"

# 得到指定目录下符合条件的 log 文件绝对路径
proc listDirFiles(path: string, fileEnds: string): seq[string] =
  for _, filePath in walkDir(path, relative=false):
    if filePath.endsWith(fileEnds):
      result.add(filePath)

# 用 .json 配置文件中 json_key_spamMarking 列出的标记检测某行 web log, 如符合就返回 true
proc isSpam(line: string, marks: seq[string]): bool =
  for mark in marks:
    if line.contains(mark):
      return true
  false

# main proc
proc cleanLogs() =
  let
    jsonNode = configFilePath().readFile().parseJson()

    # .json 配置文件中,json_key_logEnds 指定要处理的 web log 文件的结尾字符串
    logEnds = jsonNode[json_key_logEnds].getStr()
    # .json 配置文件中,json_key_webLogDir 指定 web log 目录的绝对路径
    webLogDir = jsonNode[json_key_webLogDir].getStr()
    # .json 配置文件中,json_key_spamMarking 指定要删除的 web log lines 的标记
    spamMarking = jsonNode[json_key_spamMarking].getElems().mapIt(it.getStr())

  doAssert(webLogDir.dirExists(), "Not exists: " & webLogDir)
  doAssert(spamMarking.len > 0, "Please set spamMarking")

  let logFiles = listDirFiles(webLogDir, logEnds)
  doAssert(logfiles.len > 0, "There are no log files to clean, please check your settings")

  for i, log in logFiles:
    let logContent = readFile(log)
    # 要保留的 web log lines, 将覆盖原 log 文件
    var cleanedContent: seq[string] = @[]

    var spamLines = 0
    for line in splitLines(logContent):
      if not isSpam(line, spamMarking):
        cleanedContent.add(line)
      else:
        spamLines += 1

    if spamLines > 0:
      writeFile(log, join(cleanedContent, "\n"))
      echo i + 1, " - ", log
      echo "Removed ", $spamLines, " lines\n"

cleanLogs()

2024-09-21


独立思考最难得,赞赏支持是美德!(微信扫描下图)