{ lib, pkgs, ... }: with import <stockholm/lib>; let default-host-colors = pkgs.writeJSON "logf.default-host-colors.json" { }; default-prio-colors = pkgs.writeJSON "logf.default-prio-colors.json" { "0" = 196; # emerg "1" = 160; # alert "2" = 124; # crit "3" = 009; # err "4" = 011; # warning "5" = 255; # notice "6" = 250; # info "7" = 139; # debug "-" = 005; # undefined priority }; default-urgent = pkgs.writeJSON "logf.default-urgent.json" [ ]; in pkgs.writeDashBin "logf" '' export LOGF_HOST_COLORS LOGF_PRIO_COLORS LOGF_URGENT LOGF_HOST_COLORS=$(cat "''${LOGF_HOST_COLORS-${default-host-colors}}") LOGF_PRIO_COLORS=$(cat "''${LOGF_PRIO_COLORS-${default-prio-colors}}") LOGF_URGENT=$(cat "''${LOGF_URGENT-${default-urgent}}") printf '%s\0' "$@" \ | ${pkgs.findutils}/bin/xargs -0 -P 0 -n 1 ${pkgs.writeDash "logf-remote" '' target=$1 target_host=$(echo "$1" | sed 's/^.*@//;s/\..*//') exec 3>&1 2>&1 1>&3 ssh "$target" -T \ -o PreferredAuthentications=publickey \ -o StrictHostKeyChecking=yes \ exec journalctl -af -n 0 -o json \ | stdbuf -oL jq -Rcf ${pkgs.writeJq "logf-remote-error.jq" '' { PRIORITY: "4", MESSAGE: ., SYSLOG_IDENTIFIER: env.target_host, } ''} sleep 10m exec "$0" "$@" ''} \ | ${pkgs.jq}/bin/jq -Rrf ${pkgs.writeJq "logf-filter.jq" '' (env.LOGF_HOST_COLORS | fromjson) as $host_colors | (env.LOGF_PRIO_COLORS | fromjson) as $prio_colors | (env.LOGF_URGENT | fromjson | map("(\(.))") | join("|")) as $urgent_regex | def when(c; f): if c then f else . end; # anaphoric gsub def agsub(re; f): # Don't try empty regex: https://github.com/stedolan/jq/issues/1206 when(re != ""; gsub("(?<it>\(re))"; .it | f)); # :: [int] -> sgr def sgr: "\u001b[\(map(tostring) | join(";"))m"; # :: sgr def rst: [] | sgr; # :: int -> sgr def fg(i): [38,5,i]|sgr; # TODO def fg(r;g;b): [38,2,r,g,b]|sgr; # http://cvs.schmorp.de/rxvt-unicode/src/command.C?revision=1.570&view=markup&sortby=log&sortdir=down # (sgr; sgr) | (null; any) :: str -> str def col(a; b): when(a != null; a + . + b); def col(a): col(a; rst); def p_time: ._SOURCE_REALTIME_TIMESTAMP | if . != null then . | fromjson | . / 1000000 else now end | gmtime | todateiso8601 | col(fg(237)); def p_host: ._HOSTNAME | if . != null then . else "-" end | col($host_colors[.]|when(. != null; fg(.))); def p_ident: if .SYSLOG_IDENTIFIER != null then .SYSLOG_IDENTIFIER else ._COMM end | col(fg(244)); def p_message: fg($prio_colors[if has("PRIORITY") then .PRIORITY else "-" end]) as $prio_c | .MESSAGE | sub("\r$"; "") | agsub($urgent_regex; "\(.)\u0007" | col(fg(219); $prio_c)) | col($prio_c); try fromjson catch { _SOURCE_REALTIME_TIMESTAMP: now | tostring | sub("[.]"; ""), SYSLOG_IDENTIFIER: "logf/journalctl", MESSAGE: ., } | [ p_time , p_host , p_ident , p_message ] | join(" ") ''} ''