Example: Log Management
Log Management
Configure centralized logging with rsyslog forwarding to a remote server, structured log formats, and logrotate policies.
Directory Structure
cookbooks/
logging/
default.rb
templates/
rsyslog-forward.conf.erb
logrotate-app.erb
journald.conf.erb
nodes/
app01.json
Node Attributes
{
"logging": {
"remote_host": "logs.example.com",
"remote_port": 514,
"protocol": "tcp",
"app_name": "myapp",
"app_log_dir": "/var/log/myapp",
"app_user": "deploy",
"rotate_count": 14,
"rotate_size": "100M",
"journal_max_size": "500M",
"journal_max_age": "30d"
}
}
Recipe
# cookbooks/logging/default.rb
log = node['logging']
# --- rsyslog for remote forwarding ---
package 'rsyslog' do
action :install
end
directory '/etc/rsyslog.d' do
owner 'root'
group 'root'
mode '0755'
end
template '/etc/rsyslog.d/50-remote.conf' do
source 'templates/rsyslog-forward.conf.erb'
owner 'root'
group 'root'
mode '0644'
variables(
remote_host: log['remote_host'],
remote_port: log['remote_port'],
protocol: log['protocol'],
app_name: log['app_name']
)
notifies :restart, 'service[rsyslog]'
end
service 'rsyslog' do
action [:enable, :start]
end
# --- Application log directory ---
directory log['app_log_dir'] do
owner log['app_user']
group log['app_user']
mode '0755'
end
# --- Logrotate for application logs ---
template "/etc/logrotate.d/#{log['app_name']}" do
source 'templates/logrotate-app.erb'
owner 'root'
group 'root'
mode '0644'
variables(
app_log_dir: log['app_log_dir'],
rotate_count: log['rotate_count'],
rotate_size: log['rotate_size'],
app_user: log['app_user']
)
end
# --- Journald size limits ---
template '/etc/systemd/journald.conf.d/size.conf' do
source 'templates/journald.conf.erb'
owner 'root'
group 'root'
mode '0644'
variables(
max_size: log['journal_max_size'],
max_age: log['journal_max_age']
)
notifies :restart, 'service[systemd-journald]'
end
directory '/etc/systemd/journald.conf.d' do
owner 'root'
group 'root'
mode '0755'
end
service 'systemd-journald' do
action [:enable, :start]
end
Templates
rsyslog-forward.conf.erb
# Forward application logs to remote syslog server
<% if @protocol == 'tcp' %>
*.* @@<%= @remote_host %>:<%= @remote_port %>
<% else %>
*.* @<%= @remote_host %>:<%= @remote_port %>
<% end %>
# Tag application messages
if $programname == '<%= @app_name %>' then {
action(type="omfwd"
target="<%= @remote_host %>"
port="<%= @remote_port %>"
protocol="<%= @protocol %>"
template="RSYSLOG_SyslogProtocol23Format")
stop
}
logrotate-app.erb
<%= @app_log_dir %>/*.log {
daily
rotate <%= @rotate_count %>
size <%= @rotate_size %>
compress
delaycompress
missingok
notifempty
create 0644 <%= @app_user %> <%= @app_user %>
sharedscripts
postrotate
systemctl reload rsyslog 2>/dev/null || true
endscript
}
journald.conf.erb
[Journal]
SystemMaxUse=<%= @max_size %>
MaxRetentionSec=<%= @max_age %>
Compress=yes
ForwardToSyslog=yes
Running
itamae ssh -j nodes/app01.json -h app01.example.com cookbooks/logging/default.rb