杭州文章资讯

vbs源码之的IIS日志分析工具

2026-04-08 22:02:01 浏览次数:0
详细信息

' IISLogAnalyzer.vbs
' 用法:cscript IISLogAnalyzer.vbs "日志文件路径" [输出文件路径]

Option Explicit

' 主函数
Sub Main()
    Dim logPath, outputPath
    Dim fso, logFile, outputFile
    Dim line, fields, header
    Dim stats, i

    ' 获取命令行参数
    If WScript.Arguments.Count < 1 Then
        WScript.Echo "用法: cscript IISLogAnalyzer.vbs ""日志文件路径"" [输出文件路径]"
        WScript.Quit(1)
    End If

    logPath = WScript.Arguments(0)

    If WScript.Arguments.Count > 1 Then
        outputPath = WScript.Arguments(1)
    Else
        outputPath = "IISLogAnalysis_" & Replace(Replace(Now(), ":", ""), "/", "") & ".txt"
    End If

    ' 创建文件系统对象
    Set fso = CreateObject("Scripting.FileSystemObject")

    ' 检查日志文件是否存在
    If Not fso.FileExists(logPath) Then
        WScript.Echo "错误: 日志文件不存在 - " & logPath
        WScript.Quit(1)
    End If

    ' 初始化统计对象
    Set stats = CreateObject("Scripting.Dictionary")
    stats.CompareMode = 1 ' 文本比较(不区分大小写)

    ' 初始化统计数据
    InitializeStats stats

    WScript.Echo "正在分析 IIS 日志文件: " & logPath
    WScript.Echo "输出文件: " & outputPath

    ' 读取日志文件
    Set logFile = fso.OpenTextFile(logPath, 1, False, -1) ' 1=ForReading, -1=TristateUseDefault

    ' 读取文件头信息(前几行注释)
    Do While Not logFile.AtEndOfStream
        line = logFile.ReadLine
        If Left(line, 1) = "#" Then
            ' 解析字段行
            If InStr(line, "Fields:") > 0 Then
                header = Mid(line, InStr(line, "Fields:") + 8)
                stats("Fields") = Split(header)
            End If
        Else
            ' 后退一行,开始数据行处理
            logFile.Skip(-Len(line) - 2) ' -2 for CRLF
            Exit Do
        End If
    Loop

    ' 处理数据行
    Dim lineCount : lineCount = 0
    Do While Not logFile.AtEndOfStream
        line = logFile.ReadLine
        lineCount = lineCount + 1

        ' 显示进度
        If lineCount Mod 1000 = 0 Then
            WScript.Echo "已处理 " & lineCount & " 行..."
        End If

        ProcessLogLine line, stats
    Loop

    logFile.Close

    WScript.Echo "分析完成,共处理 " & lineCount & " 行日志"

    ' 生成报告
    GenerateReport outputPath, stats, lineCount

    WScript.Echo "报告已生成: " & outputPath
End Sub

' 初始化统计数据结构
Sub InitializeStats(ByRef stats)
    ' 总请求数
    stats("TotalRequests") = 0

    ' HTTP 方法统计
    stats("Methods") = CreateObject("Scripting.Dictionary")

    ' HTTP 状态码统计
    stats("StatusCodes") = CreateObject("Scripting.Dictionary")

    ' IP 地址统计(客户端)
    stats("ClientIPs") = CreateObject("Scripting.Dictionary")

    ' URL 统计
    stats("URLs") = CreateObject("Scripting.Dictionary")

    ' 用户代理统计
    stats("UserAgents") = CreateObject("Scripting.Dictionary")

    ' 流量统计
    stats("TotalBytesSent") = 0
    stats("TotalBytesReceived") = 0

    ' 时间统计
    stats("HourlyRequests") = CreateObject("Scripting.Dictionary")
    For i = 0 To 23
        stats("HourlyRequests")(CStr(i)) = 0
    Next

    ' 响应时间统计
    stats("ResponseTimes") = CreateObject("Scripting.Dictionary")
    stats("ResponseTimes")("0-100ms") = 0
    stats("ResponseTimes")("101-500ms") = 0
    stats("ResponseTimes")("501-1000ms") = 0
    stats("ResponseTimes")("1001-5000ms") = 0
    stats("ResponseTimes")("5000+ms") = 0
End Sub

' 处理单行日志
Sub ProcessLogLine(ByVal line, ByRef stats)
    Dim fields, i
    Dim dateTime, method, uri, status, clientIP, userAgent, bytesSent, timeTaken

    ' 分割字段(IIS 日志使用空格分隔,但字段值可能包含空格,所以用引号处理)
    fields = SplitLogLine(line)

    If Not IsArray(stats("Fields")) Then
        ' 如果没有字段定义,使用默认顺序(根据 IIS 默认格式)
        ' date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username 
        ' c-ip cs(User-Agent) sc-status sc-substatus sc-win32-status time-taken
        If UBound(fields) >= 13 Then
            dateTime = fields(0) & " " & fields(1)
            clientIP = fields(8) ' c-ip
            method = fields(4)   ' cs-method
            uri = fields(5)     ' cs-uri-stem
            status = fields(10) ' sc-status
            userAgent = fields(9) ' cs(User-Agent)
            bytesSent = fields(12) ' sc-bytes? 实际需要根据字段位置调整
            timeTaken = fields(13) ' time-taken
        End If
    Else
        ' 根据字段定义解析(简化处理)
        For i = 0 To UBound(stats("Fields"))
            Select Case LCase(stats("Fields")(i))
                Case "date"
                    dateTime = fields(i) & " " & dateTime
                Case "time"
                    dateTime = dateTime & " " & fields(i)
                Case "c-ip"
                    clientIP = fields(i)
                Case "cs-method"
                    method = fields(i)
                Case "cs-uri-stem"
                    uri = fields(i)
                Case "sc-status"
                    status = fields(i)
                Case "cs(user-agent)"
                    userAgent = fields(i)
                Case "sc-bytes"
                    bytesSent = fields(i)
                Case "time-taken"
                    timeTaken = fields(i)
            End Select
        Next
    End If

    ' 更新统计
    stats("TotalRequests") = stats("TotalRequests") + 1

    ' 统计 HTTP 方法
    If method <> "" Then
        If stats("Methods").Exists(method) Then
            stats("Methods")(method) = stats("Methods")(method) + 1
        Else
            stats("Methods")(method) = 1
        End If
    End If

    ' 统计状态码
    If status <> "" Then
        If stats("StatusCodes").Exists(status) Then
            stats("StatusCodes")(status) = stats("StatusCodes")(status) + 1
        Else
            stats("StatusCodes")(status) = 1
        End If
    End If

    ' 统计客户端 IP
    If clientIP <> "" Then
        If stats("ClientIPs").Exists(clientIP) Then
            stats("ClientIPs")(clientIP) = stats("ClientIPs")(clientIP) + 1
        Else
            stats("ClientIPs")(clientIP) = 1
        End If
    End If

    ' 统计 URL(只统计前100个不同的URL)
    If uri <> "" Then
        If stats("URLs").Count < 100 Then
            If stats("URLs").Exists(uri) Then
                stats("URLs")(uri) = stats("URLs")(uri) + 1
            Else
                stats("URLs")(uri) = 1
            End If
        End If
    End If

    ' 统计用户代理
    If userAgent <> "" Then
        ' 简化用户代理字符串
        Dim shortUA
        shortUA = GetShortUserAgent(userAgent)

        If stats("UserAgents").Exists(shortUA) Then
            stats("UserAgents")(shortUA) = stats("UserAgents")(shortUA) + 1
        Else
            stats("UserAgents")(shortUA) = 1
        End If
    End If

    ' 统计每小时请求量
    If dateTime <> "" Then
        Dim hour
        hour = GetHourFromDateTime(dateTime)
        If hour >= 0 And hour <= 23 Then
            stats("HourlyRequests")(CStr(hour)) = stats("HourlyRequests")(CStr(hour)) + 1
        End If
    End If

    ' 统计响应时间
    If IsNumeric(timeTaken) Then
        Dim timeNum
        timeNum = CLng(timeTaken)

        If timeNum <= 100 Then
            stats("ResponseTimes")("0-100ms") = stats("ResponseTimes")("0-100ms") + 1
        ElseIf timeNum <= 500 Then
            stats("ResponseTimes")("101-500ms") = stats("ResponseTimes")("101-500ms") + 1
        ElseIf timeNum <= 1000 Then
            stats("ResponseTimes")("501-1000ms") = stats("ResponseTimes")("501-1000ms") + 1
        ElseIf timeNum <= 5000 Then
            stats("ResponseTimes")("1001-5000ms") = stats("ResponseTimes")("1001-5000ms") + 1
        Else
            stats("ResponseTimes")("5000+ms") = stats("ResponseTimes")("5000+ms") + 1
        End If
    End If
End Sub

' 分割日志行(处理带引号的字段)
Function SplitLogLine(ByVal line)
    Dim result(), count, i, char, inQuotes, currentField

    ReDim result(50) ' 假设最多50个字段
    count = 0
    inQuotes = False
    currentField = ""

    For i = 1 To Len(line)
        char = Mid(line, i, 1)

        If char = """" Then
            inQuotes = Not inQuotes
        ElseIf char = " " And Not inQuotes Then
            If currentField <> "" Then
                result(count) = currentField
                count = count + 1
                currentField = ""
            End If
        Else
            currentField = currentField & char
        End If
    Next

    ' 添加最后一个字段
    If currentField <> "" Then
        result(count) = currentField
        count = count + 1
    End If

    ' 调整数组大小
    ReDim Preserve result(count - 1)
    SplitLogLine = result
End Function

' 获取简化用户代理字符串
Function GetShortUserAgent(ByVal userAgent)
    Dim shortUA
    shortUA = userAgent

    ' 截断过长的用户代理
    If Len(userAgent) > 50 Then
        shortUA = Left(userAgent, 50) & "..."
    End If

    GetShortUserAgent = shortUA
End Function

' 从日期时间字符串获取小时
Function GetHourFromDateTime(ByVal dateTime)
    Dim hour
    hour = -1

    ' 尝试解析时间格式,如 "2023-01-01 14:30:45"
    If Len(dateTime) >= 13 Then
        Dim timePart
        timePart = Mid(dateTime, InStr(dateTime, " ") + 1)
        If Len(timePart) >= 2 Then
            hour = CInt(Left(timePart, 2))
        End If
    End If

    GetHourFromDateTime = hour
End Function

' 生成报告
Sub GenerateReport(ByVal outputPath, ByRef stats, ByVal totalLines)
    Dim fso, outFile
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set outFile = fso.CreateTextFile(outputPath, True)

    outFile.WriteLine "IIS 日志分析报告"
    outFile.WriteLine "生成时间: " & Now()
    outFile.WriteLine "=========================================="
    outFile.WriteLine

    outFile.WriteLine "基本信息"
    outFile.WriteLine "---------"
    outFile.WriteLine "总日志行数: " & totalLines
    outFile.WriteLine "总请求数: " & stats("TotalRequests")
    outFile.WriteLine

    ' HTTP 方法统计
    outFile.WriteLine "HTTP 方法统计"
    outFile.WriteLine "--------------"
    WriteDictionary outFile, stats("Methods"), stats("TotalRequests")
    outFile.WriteLine

    ' HTTP 状态码统计
    outFile.WriteLine "HTTP 状态码统计"
    outFile.WriteLine "----------------"
    WriteDictionary outFile, stats("StatusCodes"), stats("TotalRequests")
    outFile.WriteLine

    ' 客户端 IP 统计(前20个)
    outFile.WriteLine "客户端 IP 统计(前20个)"
    outFile.WriteLine "----------------------"
    WriteTopItems outFile, stats("ClientIPs"), 20, stats("TotalRequests")
    outFile.WriteLine

    ' URL 统计(前20个)
    outFile.WriteLine "URL 统计(前20个)"
    outFile.WriteLine "-----------------"
    WriteTopItems outFile, stats("URLs"), 20, stats("TotalRequests")
    outFile.WriteLine

    ' 用户代理统计(前10个)
    outFile.WriteLine "用户代理统计(前10个)"
    outFile.WriteLine "--------------------"
    WriteTopItems outFile, stats("UserAgents"), 10, stats("TotalRequests")
    outFile.WriteLine

    ' 小时请求分布
    outFile.WriteLine "小时请求分布"
    outFile.WriteLine "-------------"
    Dim hour
    For hour = 0 To 23
        Dim hourStr, count
        hourStr = Right("0" & CStr(hour), 2) & ":00-" & Right("0" & CStr(hour), 2) & ":59"
        count = stats("HourlyRequests")(CStr(hour))
        Dim percentage
        percentage = FormatNumber((count / stats("TotalRequests")) * 100, 2)
        outFile.WriteLine hourStr & ": " & count & " 请求 (" & percentage & "%)"
    Next
    outFile.WriteLine

    ' 响应时间分布
    outFile.WriteLine "响应时间分布"
    outFile.WriteLine "-------------"
    WriteDictionary outFile, stats("ResponseTimes"), stats("TotalRequests")
    outFile.WriteLine

    outFile.WriteLine "=========================================="
    outFile.WriteLine "报告结束"

    outFile.Close
End Sub

' 写入字典内容
Sub WriteDictionary(ByRef outFile, ByRef dict, ByVal total)
    Dim key, count, percentage
    Dim keys
    keys = dict.Keys

    For Each key In keys
        count = dict(key)
        If total > 0 Then
            percentage = FormatNumber((count / total) * 100, 2)
            outFile.WriteLine key & ": " & count & " (" & percentage & "%)"
        Else
            outFile.WriteLine key & ": " & count
        End If
    Next
End Sub

' 写入前N个项目
Sub WriteTopItems(ByRef outFile, ByRef dict, ByVal topN, ByVal total)
    Dim items(), i, key, count
    Dim keys
    keys = dict.Keys

    ' 转换为数组便于排序
    ReDim items(dict.Count - 1, 1)
    i = 0
    For Each key In keys
        items(i, 0) = key
        items(i, 1) = dict(key)
        i = i + 1
    Next

    ' 简单冒泡排序(按数量降序)
    Dim j, tempKey, tempCount
    For i = 0 To UBound(items, 1) - 1
        For j = i + 1 To UBound(items, 1)
            If items(i, 1) < items(j, 1) Then
                tempKey = items(i, 0)
                tempCount = items(i, 1)
                items(i, 0) = items(j, 0)
                items(i, 1) = items(j, 1)
                items(j, 0) = tempKey
                items(j, 1) = tempCount
            End If
        Next
    Next

    ' 输出前N个
    Dim outputCount
    outputCount = topN
    If UBound(items, 1) + 1 < outputCount Then
        outputCount = UBound(items, 1) + 1
    End If

    For i = 0 To outputCount - 1
        key = items(i, 0)
        count = items(i, 1)
        If total > 0 Then
            Dim percentage
            percentage = FormatNumber((count / total) * 100, 2)
            outFile.WriteLine key & ": " & count & " (" & percentage & "%)"
        Else
            outFile.WriteLine key & ": " & count
        End If
    Next
End Sub

' 程序入口
Main

使用说明:

1. 基本用法:

cscript IISLogAnalyzer.vbs "C:\logs\iis\logfile.log"

2. 指定输出文件:

cscript IISLogAnalyzer.vbs "C:\logs\iis\logfile.log" "report.txt"

3. 功能特点:

4. 扩展建议:

增强版本可以添加以下功能:

' 添加错误统计
Sub AddErrorStats()
    ' 404错误统计
    ' 500错误统计
    ' 慢请求分析
End Sub

' 添加图表生成
Sub GenerateChart()
    ' 使用HTML输出带图表的报告
End Sub

' 添加多文件处理
Sub ProcessMultipleFiles()
    ' 批量处理多个日志文件
End Sub

' 添加时间范围过滤
Sub FilterByTimeRange()
    ' 按时间过滤日志记录
End Sub

5. 注意事项:

这个工具提供了基本的 IIS 日志分析功能,可以根据需要进行扩展和定制。

相关推荐