Было упражнение, где нужно в файле логов веб-сервера Apache (или любого другого веб-сервера) посчитать, сколько запросов пришло с локального хоста localhost (IP 127.0.0.1) и сколько из других мест.

Лог-файл выглядит вот так:

examples/data/apache_access.log

127.0.0.1 - - [10/Apr/2007:10:39:11 +0300] "GET / HTTP/1.1" 500 606 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.1 - - [10/Apr/2007:10:39:11 +0300] "GET /favicon.ico HTTP/1.1" 200 766 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
139.12.0.2 - - [10/Apr/2007:10:40:54 +0300] "GET / HTTP/1.1" 500 612 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
139.12.0.2 - - [10/Apr/2007:10:40:54 +0300] "GET /favicon.ico HTTP/1.1" 200 766 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.1 - - [10/Apr/2007:10:53:10 +0300] "GET / HTTP/1.1" 500 612 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.1 - - [10/Apr/2007:10:54:08 +0300] "GET / HTTP/1.0" 200 3700 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.1 - - [10/Apr/2007:10:54:08 +0300] "GET /style.css HTTP/1.1" 200 614 "http://machine.local/" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.1 - - [10/Apr/2007:10:54:08 +0300] "GET /img/machine-round.jpg HTTP/1.1" 200 17524 "http://machine.local/" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.11 - - [10/Apr/2007:10:54:21 +0300] "GET /unix_sysadmin.html HTTP/1.1" 200 3880 "http://machine.local/" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
217.0.22.3 - - [10/Apr/2007:10:54:51 +0300] "GET / HTTP/1.1" 200 34 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
217.0.22.3 - - [10/Apr/2007:10:54:51 +0300] "GET /favicon.ico HTTP/1.1" 200 11514 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
217.0.22.3 - - [10/Apr/2007:10:54:53 +0300] "GET /cgi/machine.pl HTTP/1.1" 500 617 "http://contact.local/" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.1 - - [10/Apr/2007:10:54:08 +0300] "GET / HTTP/0.9" 200 3700 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
217.0.22.3 - - [10/Apr/2007:10:58:27 +0300] "GET / HTTP/1.1" 200 3700 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
217.0.22.3 - - [10/Apr/2007:10:58:34 +0300] "GET /unix_sysadmin.html HTTP/1.1" 200 3880 "http://machine.local/" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
217.0.22.3 - - [10/Apr/2007:10:58:45 +0300] "GET /talks/Fundamentals/read-excel-file.html HTTP/1.1" 404 311 "http://machine.local/unix_sysadmin.html" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"

Алгоритм:

Нам нужно два счетчика: один для подсчета хитов с 127.0.0.1 и один для хитов с других адресов. Затем нужно пройтись по всем строкам, извлечь IP-адрес, и на его основе увеличить один из счетчиков.

Мы предполагаем, что программа будет использовать вот так: ruby apache_localhost.rb data/apache_access.log. Таким образом, мы предполагаем, что пользователь укажет имя файла с логом в качестве аргумента команды.

Тем не менее, в первых строках мы проверяем, передал ли пользователь вообще имя файла с помощью количества элементов в ARGV. Если количество полученных аргументов не равно 1, то мы говорим пользователю, как пользоваться нашей программой и завершаем работу (exit).

Затем мы копируем имя файла из ARGV во внутреннюю переменную filename. В основном для наглядности кода.

Затем мы создаем два счетчика и устанавливаем их в 0.

Затем мы открываем файл на чтение и читаем строку за строкой с помощью each. В каждой итерации переменная line будет содержать текущую строку из файла.

IP-адрес это первое значение в строке до пробела. Есть несколько способов получить это значение из строки. В этом случае мы используем метод index объекта line, передавая ему пробел. Метод вернет позицию первого пробела в line. Так как нумерация начинается с 0, то этот номер будет также и длиной IP-адреса. Поэтому мы записали полученный результат в переменную length.

Мы можем использовать эту переменную для извлечения подстроки из line, которая начинается с 0 и включает length символов. Для этого нужно передать индекс начала подстроки и длину подстроки, которую мы хотим извлечь. Это и будет IP-адрес из текущей строки.

Осталость только проверить, что полученное значение совпадает с 127.0.0.1 (localhost) и увеличить соответствующий счетчик.

examples/ruby/apache_localhost.rb


if ARGV.length != 1 then
    puts "We need the name of the log file"
    exit
end

filename = ARGV[0]

local = 0
remote = 0

fh = open filename
fh.each do |line|
    length = line.index(' ')
    ip = line[0, length]
    if ip == '127.0.0.1' then
        local = local+1
    else
        remote = remote+1
    end
end

puts "Number of remote requests is #{remote}. Number of local requests was #{local}"