tlaternet-server/configuration/nginx/logging.nix

140 lines
3.9 KiB
Nix

{
flake-inputs,
pkgs,
config,
lib,
...
}:
let
hostNames = lib.attrNames config.services.nginx.virtualHosts;
logPath = name: "/var/log/nginx/${name}/access.log";
logFormat = lib.concatStringsSep " " [
"$remote_addr - $remote_user [$time_local]"
''"$request" $status $body_bytes_sent''
''"$http_referer" "$http_user_agent"''
''rt=$request_time uct="$upstream_connect_time"''
''uht="$upstream_header_time" urt="$upstream_response_time"''
];
in
{
# Extend the default configuration for nginx virtual hosts; we'd
# like to create log files for each of them, so that the prometheus
# nginxlog exporter can process per-host logs.
options.services.nginx.virtualHosts = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule (
{ name, ... }:
{
config.extraConfig = ''
access_log ${logPath name} upstream_time;
'';
}
)
);
};
config = {
# Create directories for host-specific logs with systemd tmpfiles
systemd.tmpfiles.settings."10-nginx-logs" = lib.listToAttrs (
map (
name:
lib.nameValuePair "/var/log/nginx/${name}" {
d = {
inherit (config.services.nginx) user group;
mode = "0750";
};
}
) hostNames
);
services = {
# Set the nginx-wide log format
nginx.commonHttpConfig = ''
log_format upstream_time '${logFormat}';
'';
# Set up nginxlog to read the file and log format defined above
# for each virtual host
prometheus.exporters.nginxlog = {
enable = true;
listenAddress = "127.0.0.1";
group = "nginx";
settings.namespaces = map (name: {
inherit name;
metrics_override.prefix = "nginxlog";
namespace_label = "vhost";
format = logFormat;
source.files = [ (logPath name) ];
}) hostNames;
};
logrotate.settings = {
# Override the nginx module default, just keep fewer logs
nginx.rotate = 6;
# Configure logrotate for host-specific logs
nginxVirtualHosts = {
files = map logPath hostNames;
frequency = "daily";
rotate = 2;
compress = true;
delaycompress = true;
su = "${config.services.nginx.user} ${config.services.nginx.group}";
postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`";
};
};
};
serviceTests =
let
testHostConfig =
{ config, ... }:
{
imports = [
./.
../../modules/serviceTests/mocks.nix
];
networking.firewall.allowedTCPPorts = [ 80 ];
services.nginx = {
domain = "testHost";
virtualHosts."${config.services.nginx.domain}".locations."/".return = "200 ok";
};
};
in
{
nginxMetricsWork = pkgs.testers.runNixOSTest {
name = "nginx-metrics-work";
node.specialArgs = { inherit flake-inputs; };
nodes = {
testHost = testHostConfig;
client =
{ pkgs, ... }:
{
environment.systemPackages = [ pkgs.curl ];
};
};
testScript = ''
import time
start_all()
testHost.wait_for_unit("nginx.service")
client.succeed("curl --max-time 10 http://testHost")
# Wait a bit for the prometheus exporter to scrape our logs
time.sleep(5)
res = testHost.succeed("curl localhost:${builtins.toString config.services.prometheus.exporters.nginxlog.port}/metrics")
assert 'nginxlog_http_response_count_total{method="GET",status="200",vhost="testHost"} 1' in res, res
'';
};
};
};
}