After tediously removing logs because of a full disk many times, I started looking into log collection, rotation and analysis solutions for servers. After not so long i found syslog-ng to be a nice fit for my setup. It even has a discord drain!
Grafana this, Prometheus that. You need Loki my man. These are some enterprise grade solutions which even for enterprise maybe are not the right choice. Not long into reading about syslog-ng I found that it is a quite configurable piece of software with its own DSL. It even has threading support for each logging pipe you create. So without further ado, let me present to you the way i set it up for my stuff!
As may be known (or not) i run multiple services on my homeserver, which generate tons of logs which fill up my disk with (mostly) garbage. One method is to just keep them in RAM and drop them afterwards the other is to persist them somewhere and rotate them.
Some tinkering and reading about the capabilities of syslog-ng a picture was made using plantuml, behold:
Where docker.log and everything.log are stored in
tmpfs.
First and foremost (as most of my services are running in docker) i added the following configuration to /etc/docker/daemon.json:
{
"log-driver": "syslog",
"log-opts": {
"syslog-address": "unixgram:///var/log/syslog-ng.sock",
"syslog-facility": "local7",
"syslog-format": "rfc5424",
"tag": "{{.ImageName}};{{.Name}}"
}
}
Now to the core of the configuration. This is now a lot of regex work, to clean up the output of the different docker services, because some output the service names themselves, some don’t and it was a bit tedious. So advice to you would be to learn regex.
source src {
system();
internal();
program("sshd");
};
source s_unix {
unix-dgram("/var/log/syslog-ng.sock", flags(syslog-protocol));
};
rewrite r_docker_cleaner {
subst( '(\[[0-9]{2}\/[A-Za-z]{3}\/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2} \+[0-9]{4}\]) (?P<message>.*)' "${message}", value("MESSAGE") );
subst( "([0-9]{4}\/[0-9]{2}\/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}|[a-zA-Z]{3} [0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}|[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{6}\+[0-9]{2}:[0-9]{2}) (?P<message>.*)" "${message}", value("MESSAGE") );
};
source sc_docker_cleaner {
channel {
source (s_unix);
rewrite(r_docker_cleaner);
};
};
destination d_checkthis { file("/var/log/checkthis.log"); };
destination d_everything { file("/var/log/everything.log" logrotate(enable(yes), size(1G), rotations(1))); };
destination d_kernel { file("/var/log/kernel.log"); };
destination d_auth { file("/var/log/auth.log"); };
destination d_docker { file("/var/log/docker.log" logrotate(enable(yes), size(1G), rotations(1))); };
destination d_mvalheim { discord( url("https://discord.com/api/webhooks/REDACTED") username("REDACTED") template("${MESSAGE:-[empty message]}") ); };
destination d_mfactorio { discord( url("https://discord.com/api/webhooks/REDACTED") username("REDACTED") template("${MESSAGE:-[empty message]}") ); };
filter f_mvalheim_up { match(""); };
rewrite r_mvalheim_up { "Join server with ${JOINCODE}." };
filter f_mvalheim_message { match(""); };
rewrite r_mvalheim_message { "${USER} said ${MESSAGE}." };
filter f_mfactorio_join { match(""); };
rewrite r_mfactorio_join { "${USER} started automating." };
filter f_mfactorio_leave { match(""); };
rewrite r_mfactorio_leave { "${USER} left to do chores." };
filter f_mfactorio_chat { match(""); };
rewrite r_mfactorio_chat { "${USER}: ${MESSAGE}" };
log { source(src); source(sc_docker_cleaner); filter{ level(warn..emerg); }; destination(d_checkthis); };
log { source(src); source(sc_docker_cleaner); filter{ facility(kern); }; destination(d_kernel); };
log { source(src); source(sc_docker_cleaner); filter{ facility(4) or facility(10) or facility(13); }; destination(d_auth); };
log { source(src); source(sc_docker_cleaner); filter(f_auth_fail); destination(d_auth); };
log { source(src); source(sc_docker_cleaner); destination(d_everything); };
log { source(sc_docker_cleaner); destination(d_docker); };
log { source(sc_docker_cleaner); filter(f_mvalheim_up); rewrite(r_mvalheim_up); destination(d_mvalheim); };
log { source(sc_docker_cleaner); filter(f_mvalheim_message); rewrite(r_mvalheim_message); destination(d_mvalheim); };
log { source(sc_docker_cleaner); filter(f_mfactorio_join); rewrite(r_mfactorio_join); destination(d_mfactorio); };
log { source(sc_docker_cleaner); filter(f_mfactorio_chat); rewrite(r_mfactorio_chat); destination(d_mfactorio); };
log { source(sc_docker_cleaner); filter(f_mfactorio_leave); rewrite(r_mfactorio_leave); destination(d_mfactorio); };