diff --git a/pkgs/default.nix b/pkgs/default.nix index fc0df57..8e72b59 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -71,6 +71,7 @@ in { hi = callPackage ./hi { }; ifwait = callPackage ./ifwait { }; initramfs-peek = callPackage ./initramfs-peek { }; + incz = callPackage ./incz { }; json-to-fstree = callPackage ./json-to-fstree { }; kernel-backport = callPackage ./kernel-backport { }; kmodloader = callPackage ./kmodloader { }; diff --git a/pkgs/incz/default.nix b/pkgs/incz/default.nix new file mode 100644 index 0000000..237dc5b --- /dev/null +++ b/pkgs/incz/default.nix @@ -0,0 +1,38 @@ +{ + fetchurl, + writeFennel, + fennel, + fennelrepl, + runCommand, + lua, + anoia, + lualinux, + stdenv +}: +let name = "incz"; +in stdenv.mkDerivation { + inherit name; + src = ./.; + + buildInputs = [lua]; + nativeBuildInputs = [fennelrepl]; + + buildPhase = '' + fennelrepl --test ./incz.fnl + cp -p ${writeFennel name { + packages = [ + anoia + lualinux + fennel + ]; + macros = [ + anoia.dev + ]; + mainFunction = "run"; + } ./incz.fnl } ${name} + ''; + + installPhase = '' + install -D ${name} $out/bin/${name} + ''; +} diff --git a/pkgs/incz/incz.fnl b/pkgs/incz/incz.fnl new file mode 100644 index 0000000..45b113e --- /dev/null +++ b/pkgs/incz/incz.fnl @@ -0,0 +1,58 @@ +(local { : base64 } (require :anoia)) +(local ll (require :lualinux)) + +(fn index [str indexname] + (string.format "{\"index\":{\"_index\":%q}}\n%s" indexname str)) + +(local crlf "\r\n") + +(fn chunk [str] + (let [len (# str)] + (string.format "%x%s%s%s" len crlf str crlf))) + +(fn http-header [host path auth] + (string.format + "POST %s HTTP/1.1\r +Host: %s\ +Authorization: basic %s +Transfer-Encoding: chunked\r +\r +" + path host + (let [b64 (base64 :url)] (b64:encode auth)))) + +(fn format-timestamp [timestamp] + ;; I can't make zincsearch understand any epoch-based timestamp + ;; formats, so we are formatting dates as iso-8601 and damn the leap + ;; seconds :-( + (let [secs (- (tonumber (string.sub timestamp 1 16) 16) + (lshift 1 62)) + nano (tonumber (string.sub timestamp 16 24) 16) + ts (+ (* secs 1000) (math.floor (/ nano 1000000)))] + (.. (os.date "!%FT%T" secs) "." nano "Z"))) + +(fn process-line [indexname hostname line] + (let [(timestamp service msg) (string.match line "@(%x+) (%g+) (.+)$")] + (-> + (string.format + "{%q:%q,%q:%q,%q:%q,%q:%q}\n" + "@timestamp" (format-timestamp timestamp) + :service service + :message msg + :host hostname) + (index indexname) + chunk))) + +(fn run [] + (let [myhostname (with-open [h (io.popen "hostname" :r)] (h:read "l")) + (filename loghost credentials indexname) (table.unpack arg)] + + (with-open [infile (assert (io.open filename :r))] + (ll.write 1 (http-header loghost "/api/_bulk" credentials)) + (while (case (infile:read "l") + line (ll.write 1 (process-line indexname myhostname line)))) + (ll.write 1 (chunk ""))) + (io.stderr:write (ll.read 0)))) + + +{ : run }