@@weblog
syslog-ng is often referred as a very flexible application when
it comes to processing logs. Over the years however, I began to
feel that some things are a bit more difficult to achieve in the
configuration language than it should be. For instance it is
sometimes too rigid when you need a combination of parsers
(patterndb with db-parser) and rewrite rules to achieve the goal
you wanted. Parsers and rewrite rules are distinct part of the
configuration, it is not possible to combine them into a single
functionality. Also, declaring objects first and then
referencing them later, makes the configuration easy to read,
however sometimes it is quite cumbersome, when you only need to
invert the result of an already existing filter.
To solve this situation, I’ve set out to implement an idea I had
on mind for some time now. It is quite difficult to describe the
feature in clear and concise words, as it is a combination of
various changes that together makes syslog-ng configuration more
flexible and easier to use, without sacrificing readability.
Curious? Please read on.
In-line objects
Perhaps the simplest of all features is that you can now define
the contents of a given object right on the spot, without having
to use a separate statement. For example, earlier you had to
write:
log {
source(s_local);
filter(f_postfix);
destination(d_postfix);
};
Sometimes, f_postfix filter is only used once and is trivial.
This can now be written as:
log {
source(s_local);
filter { program("^postfix/"); };
destination(d_postfix);
};
Furthermore both the source() and destination() options can be
written in-line, you simply use braces instead of parentheses.
The same functionality applies to everything: sources,
destinations, filters, parsers and rewrite rules.
Junctions
A limited form of junctions has been supported since syslog-ng
3.0 in the form of “embedded log statements”, which has been
generalized now. Within syslog-ng, when a message is received it
is dispatched to a log processing path or pipeline, which
carries out the task at hand. A junction is a point in the log
processing path where the processing is performed on multiple
independent branches, each doing its own specific thing with the
message.
The limited functionality in 3.0 only allowed the processing
tree to split (or fork) into independent branches, each of the
branches was a “sink”, where processing also ended.
Configuration example:
log {
source(s_all); filter(f);
log { filter(f1); destination(d1); };
log { filter(f2); destination(d2); };
};
This sample forks the processing path into two branches starting
with the “log” keyword within the top-level log statement. The
first branch evaluates the filter f1 and the writes matching
messages to the d1 destination, effectively sending all messages
that match (f AND f1) to d1. Likewise, d2 receives all messages
that match (f AND f2).
The limitation of the embedded log statement concept was simple:
it could only be listed at the very end of a log statement, and
the end-result of the branches couldn’t be processed further.
Effectively the message at the end of each branch “fell off”.
Junctions on the other hand makes it possible to do things to
messages once the branches converge to the same point again.
Repeating the sample above, it is now possible to write:
log {
source(s_all); filter(f);
junction {
log { filter(f1); destination(d1); };
log { filter(f2); destination(d2); };
};
destination(d_all);
};
The new thing is that you can now add processing after
the branches finish their processing. A bit more useful example
would be:
log {
source(s_apache_files);
source(s_syslog);
junction {
log { filter(f_apache_files); rewrite(r_apache_remove_file_header); parser(p_apache); flags(final); };
log { filter(f_apache_syslog); parser(p_apache); flags(final); };
};
destination(d_files);
};
This example does an alternative processing of incoming logs
based on where the message came from.
Everything is a log expression
This feature is probably the most complicated, however provides
very nice properties and expressiveness to the configuration.
From now on, not just the well known log statement allows the
specification of log processing rules, but all the objects in
the syslog-ng configuration file can use the same expressive
power.
It is now possible to use embedded log statements, junctions and
in-line object definitions within source, destination, filter,
rewrite and parser definitions. Huh, you could ask: what does it
bring to me as a benefit? Well, until now, objects of different
types were separate entities, connected using log statements,
with this change a source can also specify a rewrite rule and
that combination used as a log source in a log statement.
For instance, a usual source definition looked like this:
source s_apache {
file("/var/log/apache/error.log");
};
If you wanted to process this log file in a specific way, you
needed to define the accompanying processing rules (parsers and
rewrite expressions) and combine them in a log statement. But
how about this:
source s_apache {
log {
source { file("/var/log/apache/error.log"); };
parser(p_apache_parser); };
};
};
log { source(s_apache); ... };