How to compare ${HOST} and ${HOST_FROM} (or why ${HOST} != ${HOST})

syslog-ng

I have a setup which can be summarized as follow:

service --> syslog-ng relay --> syslog-ng server

On the syslog-ng server, I would have the log organized as follow:

/var/log/ ----> syslog-ng server local log files...
           \--> ${FROM_HOST}/ 
                 \---> Syslog-ng relay host log files...
                 \---> ${HOST}/
                        \---> Service log files...
  • Logs generated locally on the syslog-ng server located in their default location, /var/log.
  • Logs generated on a relay in a subdirectory bearing the relay name, /var/log/${FROM_HOST}.
  • Logs generated by a service in a subdirectory of its associated relay, /var/log/${FROM_HOST}/${HOST}

I created the following configuration on the syslog-ng server host:

@version: 3.7
@include "scl.conf"

options {
    dns-cache-hosts("/etc/hosts");
    keep-hostname(yes);
    use-dns(persist_only);
};

...(removed)...

# syslog-ng server local logs, works fine.
log {
    source(local_src);
    local_file();
};

# syslog-ng relays, never matches.
log {
    source(remote_src);
    filter { host("${HOST_FROM}", type(string)); };
    local_file(subdir("${HOST_FROM}/"));
};

# Services, works fine but also matches syslog-ng relays.
log {
    source(remote_src);
    filter { not host("${HOST_FROM}", type(string)); };
    local_file(subdir("${HOST_FROM}/$(sanitize $HOST)/"));
};

For information local_file() is a custom log block taking as parameter the subdirectory where to store logs entries.

The host() filter is described as comparing its parameter with ${HOST}. My idea was there to use this to check ${HOST} (the author of the syslog message) and ${HOST_FROM} (the last hop of the syslog message):

  • If they are equal, the message has been generated by the syslog-ng relay.
  • If they are different, the message has been generated by a service.

However, the test host("${HOST_FROM}", type(string)) always fails, even though the generated path does not show any difference between the values of ${HOST} and ${HOST_FROM} (for instance, given a relay named therelay, relays logs will be stored below /var/log/therelay/therelay/ (expansion of /var/log/${HOST_FROM}/$(sanitize $HOST)/).

I tried to remove sanitization, to interpret the pattern as a regexp instead of a raw string, add .* at the beginning and the end of the pattern while a regexp, remade all these tests while replacing host("${HOST_FROM}") by match("${HOST_FROM}", value(HOST)), but nothing seems to work.

Actually, even host("${HOST}") (comparing ${HOST} with itself) seems to never match.

Replacing the filter with filter { host(".*"); }; however correctly forces this entry to match.

Is this the expected behavior? How can I compare the content of ${HOST} and ${HOST_FROM}?

Best Answer

I'm not sure if this is the best answer so I leave my question open for now.

It seems that macros are not expanded in filter functions, most likely due a speed optimization as the use of a "somewhat slower" specific syntax seems required in such case.

host("${HOST_FROM}") will only match against a host literally named ${HOST_FROM}, which is not likely to happen under normal circumstances.

The right test to do seems to be the filter defined below:

filter { "${HOST}" == "${HOST_FROM}" };

While this syntax may be less efficient, I think this is only way to compare the content of two macros.

Related Topic