logshippers: add victorialogsend script
it's only very lightly tested but it seems to work. the _stream may be quite wrong, or perhaps that's a local admin decision anyway
This commit is contained in:
@@ -11,6 +11,14 @@
|
||||
}:
|
||||
let
|
||||
name = "logshippers";
|
||||
luafy = name : source :
|
||||
writeFennel name {
|
||||
packages = [ anoia lualinux fennel ];
|
||||
macros = [ anoia.dev ];
|
||||
mainFunction = "run";
|
||||
} source;
|
||||
incz = luafy name ./incz.fnl;
|
||||
victorialogsend = luafy name ./victorialogsend.fnl;
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
inherit name;
|
||||
@@ -21,22 +29,13 @@ stdenv.mkDerivation {
|
||||
|
||||
buildPhase = ''
|
||||
fennelrepl --test ./incz.fnl
|
||||
cp -p ${
|
||||
writeFennel name {
|
||||
packages = [
|
||||
anoia
|
||||
lualinux
|
||||
fennel
|
||||
];
|
||||
macros = [
|
||||
anoia.dev
|
||||
];
|
||||
mainFunction = "run";
|
||||
} ./incz.fnl
|
||||
} ${name}
|
||||
fennelrepl --test ./victorialogsend.fnl
|
||||
cp -p ${incz} incz
|
||||
cp -p ${victorialogsend} victorialogsend
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
install -D ${name} $out/bin/${name}
|
||||
install -D incz $out/bin/incz
|
||||
install -D victorialogsend $out/bin/victorialogsend
|
||||
'';
|
||||
}
|
||||
|
83
pkgs/logshippers/victorialogsend.fnl
Normal file
83
pkgs/logshippers/victorialogsend.fnl
Normal file
@@ -0,0 +1,83 @@
|
||||
(local { : base64 : assoc } (require :anoia))
|
||||
(local tai64 (require :anoia.tai64))
|
||||
(local ll (require :lualinux))
|
||||
(import-macros { : expect= : define-tests } :anoia.assert)
|
||||
|
||||
(local crlf "\r\n")
|
||||
|
||||
(fn chunk [str]
|
||||
(let [len (# str)]
|
||||
(string.format "%x%s%s%s" len crlf str crlf)))
|
||||
|
||||
(fn parse-url [str]
|
||||
;; this is a very poor parser as it won't recognise
|
||||
;; credentials in the authority and it lumps query-string/fragment
|
||||
;; into the path
|
||||
(let [(scheme host path)
|
||||
(string.match str "(.-)://(.-)(/.+)")]
|
||||
{ : scheme : host : path }))
|
||||
|
||||
(define-tests
|
||||
(expect= (parse-url "https://www.example.com/stairway/to/heaven")
|
||||
{ :scheme "https"
|
||||
:host "www.example.com"
|
||||
:path "/stairway/to/heaven"
|
||||
}))
|
||||
|
||||
(fn parse-args [args]
|
||||
(case args
|
||||
["--basic-auth" auth & rest]
|
||||
(assoc (parse-args rest) :auth auth)
|
||||
|
||||
[url] { :url (parse-url url) }
|
||||
_ (error "invalid args")))
|
||||
|
||||
|
||||
(fn http-header [host path auth]
|
||||
(let [b64 (base64 :url)
|
||||
authstr
|
||||
(if auth
|
||||
(string.format "Authorization: basic %s\n" (b64:encode auth))
|
||||
"")]
|
||||
(string.format
|
||||
"POST %s HTTP/1.1\r
|
||||
Host: %s\
|
||||
%sTransfer-Encoding: chunked\r
|
||||
\r
|
||||
"
|
||||
path host authstr)))
|
||||
|
||||
(fn format-timestamp-rfc3339 [timestamp prec]
|
||||
(let [(sec nano) (-> timestamp tai64.from-timestamp tai64.to-utc)
|
||||
subsec (string.sub (string.format "%09d" nano) 1 prec)]
|
||||
(.. (os.date "!%FT%T" sec)
|
||||
"." subsec
|
||||
"Z")))
|
||||
|
||||
(define-tests
|
||||
(expect= (format-timestamp-rfc3339 "@4000000068e2f0d3257dc09b" 9)
|
||||
"2025-10-05T22:26:54.628998299Z"))
|
||||
|
||||
|
||||
|
||||
(fn process-line [line]
|
||||
(let [(timestamp hostname service msg) (string.match line "(@%x+) (%g+) (%g+) (.+)$")]
|
||||
(->
|
||||
(string.format
|
||||
"{%q:%q,%q:%q,%q:%q,%q:%q}\n"
|
||||
:_time (format-timestamp-rfc3339 timestamp)
|
||||
:service service
|
||||
:_msg msg
|
||||
:host hostname)
|
||||
chunk)))
|
||||
|
||||
(fn run []
|
||||
(let [{ : auth : url } (parse-args arg)]
|
||||
(ll.write 1 (http-header url.host url.path auth))
|
||||
(while (case (io.stdin:read "l")
|
||||
line (ll.write 1 (process-line line))))
|
||||
(ll.write 1 (chunk "")))
|
||||
(io.stderr:write (ll.read 0)))
|
||||
|
||||
|
||||
{ : run }
|
Reference in New Issue
Block a user