git.fiddlerwoaroof.com
Raw Blame History
(defpackage :access-log-reader
  (:shadowing-import-from :multi-fun :rest)
  (:shadowing-import-from :data-lens :pick :defun-ct)
  (:use :cl :multi-fun :data-lens :alexandria :fw.lu)
  (:export ))
(in-package :access-log-reader)

(defun make-timestamp (record)
  (let ((local-time:*default-timezone* local-time:+utc-zone+))
    (destructuring-bind (day month year time) record
      (local-time:with-decoded-timestamp (:hour hour :minute minute :sec second :nsec nsec) time
        (local-time:encode-timestamp nsec second minute hour day month year)))))

(defun extract-query-params (s)
  (serapeum:mapply 'cons
                   (map 'list 
                        (serapeum:op (coerce (fwoar.string-utils:split #\= _ :count 2) 'list))
                        (fwoar.string-utils:split #\& s))))
(defparameter +access-log-parser+
  (list (delimited-field #\:)
        (delimited-field #\space) 
        (as (delimited-field #\space) (lambda (s) (subseq s 1 (1- (length s)))))
        (as (delimited-field #\space) (lambda (s) (unless (equal s "-") (parse-integer s))))
        (as (delimited-field #\space) (lambda (s) (unless (equal s "-") (parse-integer s))))
        (as (delimited-field #\space) #'parse-integer)
        (as (delimited-field #\space) (lambda (s) (unless (equal s "-") (parse-integer s))))
        (as (subformat (v (ignore-char #\[)
                          (as (delimited-field #\/) #'parse-integer)
                          (as (delimited-field #\/) (serapeum:op (position _ local-time:+short-month-names+ :test 'equal)))
                          (as (delimited-field #\:) #'parse-integer)
                          (as (delimited-field #\]) (lambda (x) (local-time:parse-timestring (remove #\space x) :allow-missing-date-part t)))))
            'make-timestamp)
        (whitespace)
        (as (delimited-field #\space) (lambda (s) (subseq s 1)))
        (splat-result (as (delimited-field #\space)
                          (alexandria:compose
                           (data-lens:juxt 'quri:uri-path
                                           (alexandria:compose 'extract-query-params 'quri:uri-query))
                           'quri:uri)))
        (delimited-field #\")
        (delimited-field #\space)
        (delimited-field #\space)
        (delimited-field #\space)
        (ignore-char #\")
        (as (delimited-field #\") 'quri:uri)
        (whitespace)
        (rest)))

(defun parse-log (f)
  (parse-file +access-log-parser+ f))