{ config, lib, pkgs, ... }: let cfg = config.services.below; cfgContents = lib.concatStringsSep "\n" ( lib.mapAttrsToList (n: v: ''${n} = "${v}"'') ( lib.filterAttrs (_k: v: v != null) { log_dir = cfg.dirs.log; store_dir = cfg.dirs.store; cgroup_filter_out = cfg.cgroupFilterOut; } ) ); mkDisableOption = n: lib.mkOption { type = lib.types.bool; default = true; description = "Whether to enable ${n}."; }; optionalType = ty: x: lib.mkOption ( x // { description = x.description; type = (lib.types.nullOr ty); default = null; } ); optionalPath = optionalType lib.types.path; optionalStr = optionalType lib.types.str; optionalInt = optionalType lib.types.int; in { options = { services.below = { enable = lib.mkEnableOption "'below' resource monitor"; cgroupFilterOut = optionalStr { description = "A regexp matching the full paths of cgroups whose data shouldn't be collected"; example = "user.slice.*"; }; collect = { diskStats = mkDisableOption "dist_stat collection"; ioStats = lib.mkEnableOption "io.stat collection for cgroups"; exitStats = mkDisableOption "eBPF-based exitstats"; }; compression.enable = lib.mkEnableOption "data compression"; retention = { size = optionalInt { description = '' Size limit for below's data, in bytes. Data is deleted oldest-first, in 24h 'shards'. ::: {.note} The size limit may be exceeded by at most the size of the active shard, as: - the active shard cannot be deleted; - the size limit is only enforced when a new shard is created. ::: ''; }; time = optionalInt { description = '' Retention time, in seconds. ::: {.note} As data is stored in 24 hour shards which are discarded as a whole, only data expired by 24h (or more) is guaranteed to be discarded. ::: ::: {.note} If `retention.size` is set, data may be discarded earlier than the specified time. ::: ''; }; }; dirs = { log = optionalPath { description = "Where to store below's logs"; }; store = optionalPath { description = "Where to store below's data"; example = "/var/lib/below"; }; }; }; }; config = lib.mkIf cfg.enable { environment.systemPackages = [ pkgs.below ]; # /etc/below.conf is also refered to by the `below` CLI tool, # so this can't be a store-only file whose path is passed to the service environment.etc."below/below.conf".text = cfgContents; systemd = { packages = [ pkgs.below ]; services.below = { # Workaround for https://github.com/NixOS/nixpkgs/issues/81138 wantedBy = [ "multi-user.target" ]; restartTriggers = [ cfgContents ]; serviceConfig.ExecStart = [ "" ( "${lib.getExe pkgs.below} record " + (lib.concatStringsSep " " ( lib.optional (!cfg.collect.diskStats) "--disable-disk-stat" ++ lib.optional cfg.collect.ioStats "--collect-io-stat" ++ lib.optional (!cfg.collect.exitStats) "--disable-exitstats" ++ lib.optional cfg.compression.enable "--compress" ++ lib.optional (cfg.retention.size != null) "--store-size-limit ${toString cfg.retention.size}" ++ lib.optional (cfg.retention.time != null) "--retain-for-s ${toString cfg.retention.time}" )) ) ]; }; }; }; meta.maintainers = with lib.maintainers; [ nicoo ]; }