アクセスログのIPアドレスを逆引き
Posted feedbacks - Common Lisp
とりあえず、ipaddr-to-hostname の解決だけ並列化。まぁ、そもそもこのコードだと resolver 自体が並列動作できないけれど…そこまでやる場合はロックやキューを resolver 内で作って、process-run-function のパラメータとして渡せばいいと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | (defpackage :doukaku-39 (:use :cl))
(in-package :doukaku-39)
(defvar *endp* nil)
(defvar *lock* (mp:make-process-lock))
(defvar *reader-queue* (make-instance 'mp:queue))
(defvar *writer-queue* (make-instance 'mp:queue))
(defmacro with-dequeue ((elem queue lock) &body body)
"キューに要素があるなら、取り出す。なければ別スレッドへ切り換え"
`(let ((,elem (mp:with-process-lock (,lock)
(unless (mp:queue-empty-p ,queue)
(mp:dequeue ,queue)))))
(if (null ,elem)
(mp:process-allow-schedule)
(progn ,@body))))
(defun process/resolver ()
"ひたすら名前解決"
(loop
(when (and (mp:queue-empty-p *reader-queue*) *endp*) (return))
;; キューが空なら待ち、要素があるなら読み出して処理し、結果を書き込みキューに入れる
(with-dequeue (line *reader-queue* *lock*)
(multiple-value-bind (match? whole ip rest)
(excl:match-re "(\\d+.\\d+.\\d+.\\d+)(.*)" line)
(declare (ignorable match? whole))
;; socket:ipaddr-to-hostname は Lisp レベルで名前のキャッシュを行ってる
(mp:enqueue *writer-queue*
(concatenate 'string
(or (socket:ipaddr-to-hostname ip) ip)
rest))))))
(defun process/writer (output-file)
"ひたすら結果を出力"
(with-open-file (out output-file :direction :output :if-exists :supersede)
(loop
(when (and *endp* (mp:queue-empty-p *writer-queue*)
(mp:queue-empty-p *reader-queue*))
(return))
(with-dequeue (line *writer-queue* *lock*)
(write-line line out)))))
(defun resolver (input-file output-file &aux processes)
"ブロックする可能性のある名前解決をスレッド化して高速化"
(with-open-file (in input-file :direction :input)
;; 並列化
(setf *endp* nil)
(loop for i from 1 to 5 do
(push (mp:process-run-function (format nil "Resolver Process ~D" i)
#'process/resolver) processes))
(push (mp:process-run-function "Writer Process"
#'process/writer output-file) processes)
;; ファイルからの読み込み
(loop for line = (read-line in nil :eof)
until (eql line :eof)
do (mp:enqueue *reader-queue* line) ; 読み込んだ行をリゾルバ用キューに入れる
finally (mp:with-process-lock (*lock*) (setf *endp* t)))
;; 全プロセスの終了待ち
(loop until (some #'mp:process-active-p processes)
do (mp:process-allow-schedule))
*endp*))
|

沢渡 みかげ
#3395()
Rating1/1=1.00
アクセスログの各行の先頭にIPアドレスがあります.そのIPアドレスを逆引き結果のFQDNで置き換えてください.
逆引きが出来なかった場合は,IPアドレスのまま残します. IPアドレス以外の部分は,そのまま加工せずに残してください.
----
例)192.168.7.1 が逆引きできない場合
210.166.209.71 - - [26/Jul/2007:22:32:47 +0900] "GET / HTTP/1.1" 403 283 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5"
192.168.7.1 - - [26/Jul/2007:22:32:48 +0900] "GET /favicon.ico HTTP/1.1" 404 290 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5"
↓
mikage.to - - [26/Jul/2007:22:32:47 +0900] "GET / HTTP/1.1" 403 283 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5"
192.168.7.1 - - [26/Jul/2007:22:32:48 +0900] "GET /favicon.ico HTTP/1.1" 404 290 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5"
----
アクセスログは膨大な量があるため,現実的な時間で処理できるよう,以下の条件をつけます.
・メモリに入りきらないような巨大なログも処理できるようにしてください.(ファイル全体をメモリに読み込むのはNG)
・十分な速度で処理できるよう,並列化する等の工夫をしてください.
・DNSサーバに大量のリクエストが行かないよう,結果をキャッシュしてDNSサーバへのアクセスを削減してください. なお,DNSのTTLは無視して結果をキャッシュしてかまいません. (ログの記録された時間の逆引きするタイミングがずれているため,正確な逆引きは元々無理なので)
名前解決はgethostbyaddrを利用しても良いですし,再帰的に名前解決が出来るDNSサーバと直接通信してもかまいません.
ログを順次読み取り処理する部分を,データを共有しつついかに並列化するか,という部分を問うのが目的です.
このお題は沢渡みかげさんの投稿です。ご投稿ありがとうございます。
[ reply ]