mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Improve logging framework doc
Reorganized the content to be easier to follow, added a few more examples, fixed some ugly formatting (removed scrollbars that make the examples difficult to read).
This commit is contained in:
parent
e02ad1711c
commit
7cf04c9f3a
2 changed files with 371 additions and 251 deletions
|
@ -67,8 +67,8 @@ that are present in the ASCII log files::
|
||||||
'id.orig_p' integer,
|
'id.orig_p' integer,
|
||||||
...
|
...
|
||||||
|
|
||||||
Note that the ASCII ``conn.log`` will still be created. To disable the ASCII writer for a
|
Note that the ASCII ``conn.log`` will still be created. To prevent this file
|
||||||
log stream, you can remove the default filter:
|
from being created, you can remove the default filter:
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
|
|
|
@ -19,195 +19,128 @@ Terminology
|
||||||
|
|
||||||
Bro's logging interface is built around three main abstractions:
|
Bro's logging interface is built around three main abstractions:
|
||||||
|
|
||||||
Log streams
|
Streams
|
||||||
A stream corresponds to a single log. It defines the set of
|
A log stream corresponds to a single log. It defines the set of
|
||||||
fields that a log consists of with their names and fields.
|
fields that a log consists of with their names and types.
|
||||||
Examples are the ``conn`` for recording connection summaries,
|
Examples are the ``conn`` stream for recording connection summaries,
|
||||||
and the ``http`` stream for recording HTTP activity.
|
and the ``http`` stream for recording HTTP activity.
|
||||||
|
|
||||||
Filters
|
Filters
|
||||||
Each stream has a set of filters attached to it that determine
|
Each stream has a set of filters attached to it that determine
|
||||||
what information gets written out. By default, each stream has
|
what information gets written out. By default, each stream has
|
||||||
one default filter that just logs everything directly to disk
|
one default filter that just logs everything directly to disk.
|
||||||
with an automatically generated file name. However, further
|
However, additional filters can be added to record only a subset
|
||||||
filters can be added to record only a subset, split a stream
|
of the log records, write to different outputs, or set a custom
|
||||||
into different outputs, or to even duplicate the log to
|
rotation interval. If all filters are removed from a stream,
|
||||||
multiple outputs. If all filters are removed from a stream,
|
then output is disabled for that stream.
|
||||||
all output is disabled.
|
|
||||||
|
|
||||||
Writers
|
Writers
|
||||||
A writer defines the actual output format for the information
|
Each filter has a writer. A writer defines the actual output
|
||||||
being logged. At the moment, Bro comes with only one type of
|
format for the information being logged. The default writer is
|
||||||
writer, which produces tab separated ASCII files. In the
|
the ASCII writer, which produces tab-separated ASCII files. Other
|
||||||
future we will add further writers, like for binary output and
|
writers are available, like for binary output or direct logging
|
||||||
direct logging into a database.
|
into a database.
|
||||||
|
|
||||||
Basics
|
There are several different ways to customize Bro's logging: you can create
|
||||||
======
|
a new log stream, you can extend an existing log with new fields, you
|
||||||
|
can apply filters to an existing log stream, or you can customize the output
|
||||||
|
format by setting log writer options. All of these approaches are
|
||||||
|
described in this document.
|
||||||
|
|
||||||
The data fields that a stream records are defined by a record type
|
Streams
|
||||||
specified when it is created. Let's look at the script generating Bro's
|
=======
|
||||||
connection summaries as an example,
|
|
||||||
:doc:`/scripts/base/protocols/conn/main.bro`. It defines a record
|
|
||||||
:bro:type:`Conn::Info` that lists all the fields that go into
|
|
||||||
``conn.log``, each marked with a ``&log`` attribute indicating that it
|
|
||||||
is part of the information written out. To write a log record, the
|
|
||||||
script then passes an instance of :bro:type:`Conn::Info` to the logging
|
|
||||||
framework's :bro:id:`Log::write` function.
|
|
||||||
|
|
||||||
By default, each stream automatically gets a filter named ``default``
|
In order to log data to a new log stream, all of the following needs to be
|
||||||
that generates the normal output by recording all record fields into a
|
done:
|
||||||
single output file.
|
|
||||||
|
|
||||||
In the following, we summarize ways in which the logging can be
|
- A :bro:type:`record` type must be defined which consists of all the
|
||||||
customized. We continue using the connection summaries as our example
|
fields that will be logged (by convention, the name of this record type is
|
||||||
to work with.
|
usually "Info").
|
||||||
|
- A log stream ID (an :bro:type:`enum` with type name "Log::ID") must be
|
||||||
|
defined that uniquely identifies the new log stream.
|
||||||
|
- A log stream must be created using the :bro:id:`Log::create_stream` function.
|
||||||
|
- When the data to be logged becomes available, the :bro:id:`Log::write`
|
||||||
|
function must be called.
|
||||||
|
|
||||||
Filtering
|
In the following example, we create a new module "Foo" which creates
|
||||||
---------
|
a new log stream.
|
||||||
|
|
||||||
To create a new output file for an existing stream, you can add a
|
|
||||||
new filter. A filter can, e.g., restrict the set of fields being
|
|
||||||
logged:
|
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
event bro_init()
|
module Foo;
|
||||||
|
|
||||||
|
export {
|
||||||
|
# Create an ID for our new stream. By convention, this is
|
||||||
|
# called "LOG".
|
||||||
|
redef enum Log::ID += { LOG };
|
||||||
|
|
||||||
|
# Define the record type that will contain the data to log.
|
||||||
|
# By convention, the type is called "Info".
|
||||||
|
# All fields to log must have the &log attribute.
|
||||||
|
# If a field might not contain any data, use the
|
||||||
|
# &optional attribute.
|
||||||
|
type Info: record {
|
||||||
|
ts: time &log;
|
||||||
|
id: conn_id &log;
|
||||||
|
service: string &log &optional;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optionally, we can add a new field to the connection record so that
|
||||||
|
# the data we are logging (our "Info" record) will be easily
|
||||||
|
# accessible in a variety of event handlers.
|
||||||
|
# By convention, the name of this new field is the lowercase name of
|
||||||
|
# the module.
|
||||||
|
redef record connection += {
|
||||||
|
foo: Info &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
# This event is handled at a priority higher than zero so that if
|
||||||
|
# users modify this stream in another script, they can do so at the
|
||||||
|
# default priority of zero.
|
||||||
|
event bro_init() &priority=5
|
||||||
{
|
{
|
||||||
# Add a new filter to the Conn::LOG stream that logs only
|
# Create the stream. This adds a default filter automatically.
|
||||||
# timestamp and originator address.
|
Log::create_stream(Foo::LOG, [$columns=Info, $path="foo"]);
|
||||||
local filter: Log::Filter = [$name="orig-only", $path="origs", $include=set("ts", "id.orig_h")];
|
|
||||||
Log::add_filter(Conn::LOG, filter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Note the fields that are set for the filter:
|
At this point, the only thing missing is a call to the :bro:id:`Log::write`
|
||||||
|
function to send data to the logging framework. The actual event handler
|
||||||
``name``
|
where this should take place will depend on where your data becomes available.
|
||||||
A mandatory name for the filter that can later be used
|
In this example, the connection_established event provides our data, and we
|
||||||
to manipulate it further.
|
also store a copy of the data being logged into the :bro:type:`connection`
|
||||||
|
record:
|
||||||
``path``
|
|
||||||
The filename for the output file, without any extension (which
|
|
||||||
may be automatically added by the writer). Default path values
|
|
||||||
are generated by taking the stream's ID and munging it slightly.
|
|
||||||
:bro:enum:`Conn::LOG` is converted into ``conn``,
|
|
||||||
:bro:enum:`PacketFilter::LOG` is converted into
|
|
||||||
``packet_filter``, and :bro:enum:`Known::CERTS_LOG` is
|
|
||||||
converted into ``known_certs``.
|
|
||||||
|
|
||||||
``include``
|
|
||||||
A set limiting the fields to the ones given. The names
|
|
||||||
correspond to those in the :bro:type:`Conn::Info` record, with
|
|
||||||
sub-records unrolled by concatenating fields (separated with
|
|
||||||
dots).
|
|
||||||
|
|
||||||
Using the code above, you will now get a new log file ``origs.log``
|
|
||||||
that looks like this::
|
|
||||||
|
|
||||||
#separator \x09
|
|
||||||
#path origs
|
|
||||||
#fields ts id.orig_h
|
|
||||||
#types time addr
|
|
||||||
1128727430.350788 141.42.64.125
|
|
||||||
1128727435.450898 141.42.64.125
|
|
||||||
|
|
||||||
If you want to make this the only log file for the stream, you can
|
|
||||||
remove the default filter (which, conveniently, has the name
|
|
||||||
``default``):
|
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
event bro_init()
|
event connection_established(c: connection)
|
||||||
{
|
{
|
||||||
# Remove the filter called "default".
|
local rec: Foo::Info = [$ts=network_time(), $id=c$id];
|
||||||
Log::remove_filter(Conn::LOG, "default");
|
|
||||||
|
# Store a copy of the data in the connection record so other
|
||||||
|
# event handlers can access it.
|
||||||
|
c$foo = rec;
|
||||||
|
|
||||||
|
Log::write(Foo::LOG, rec);
|
||||||
}
|
}
|
||||||
|
|
||||||
An alternate approach to "turning off" a log is to completely disable
|
When you are developing scripts that add data to the :bro:type:`connection`
|
||||||
the stream:
|
record, care must be given to when and how long data is stored.
|
||||||
|
Normally data saved to the connection record will remain there for the
|
||||||
|
duration of the connection and from a practical perspective it's not
|
||||||
|
uncommon to need to delete that data before the end of the connection.
|
||||||
|
|
||||||
.. code:: bro
|
|
||||||
|
|
||||||
event bro_init()
|
Add Fields to a Log
|
||||||
{
|
-------------------
|
||||||
Log::disable_stream(Conn::LOG);
|
|
||||||
}
|
|
||||||
|
|
||||||
If you want to skip only some fields but keep the rest, there is a
|
You can add additional fields to a log by extending the record
|
||||||
corresponding ``exclude`` filter attribute that you can use instead of
|
type that defines its content, and setting a value for the new fields
|
||||||
``include`` to list only the ones you are not interested in.
|
before each log record is written.
|
||||||
|
|
||||||
A filter can also determine output paths *dynamically* based on the
|
Let's say we want to add a boolean field ``is_private`` to
|
||||||
record being logged. That allows, e.g., to record local and remote
|
:bro:type:`Conn::Info` that indicates whether the originator IP address
|
||||||
connections into separate files. To do this, you define a function
|
is part of the :rfc:`1918` space:
|
||||||
that returns the desired path:
|
|
||||||
|
|
||||||
.. code:: bro
|
|
||||||
|
|
||||||
function split_log(id: Log::ID, path: string, rec: Conn::Info) : string
|
|
||||||
{
|
|
||||||
# Return "conn-local" if originator is a local IP, otherwise "conn-remote".
|
|
||||||
local lr = Site::is_local_addr(rec$id$orig_h) ? "local" : "remote";
|
|
||||||
return fmt("%s-%s", path, lr);
|
|
||||||
}
|
|
||||||
|
|
||||||
event bro_init()
|
|
||||||
{
|
|
||||||
local filter: Log::Filter = [$name="conn-split", $path_func=split_log, $include=set("ts", "id.orig_h")];
|
|
||||||
Log::add_filter(Conn::LOG, filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
Running this will now produce two files, ``local.log`` and
|
|
||||||
``remote.log``, with the corresponding entries. One could extend this
|
|
||||||
further for example to log information by subnets or even by IP
|
|
||||||
address. Be careful, however, as it is easy to create many files very
|
|
||||||
quickly ...
|
|
||||||
|
|
||||||
.. sidebar:: A More Generic Path Function
|
|
||||||
|
|
||||||
The ``split_log`` method has one draw-back: it can be used
|
|
||||||
only with the :bro:enum:`Conn::LOG` stream as the record type is hardcoded
|
|
||||||
into its argument list. However, Bro allows to do a more generic
|
|
||||||
variant:
|
|
||||||
|
|
||||||
.. code:: bro
|
|
||||||
|
|
||||||
function split_log(id: Log::ID, path: string, rec: record { id: conn_id; } ) : string
|
|
||||||
{
|
|
||||||
return Site::is_local_addr(rec$id$orig_h) ? "local" : "remote";
|
|
||||||
}
|
|
||||||
|
|
||||||
This function can be used with all log streams that have records
|
|
||||||
containing an ``id: conn_id`` field.
|
|
||||||
|
|
||||||
While so far we have seen how to customize the columns being logged,
|
|
||||||
you can also control which records are written out by providing a
|
|
||||||
predicate that will be called for each log record:
|
|
||||||
|
|
||||||
.. code:: bro
|
|
||||||
|
|
||||||
function http_only(rec: Conn::Info) : bool
|
|
||||||
{
|
|
||||||
# Record only connections with successfully analyzed HTTP traffic
|
|
||||||
return rec$service == "http";
|
|
||||||
}
|
|
||||||
|
|
||||||
event bro_init()
|
|
||||||
{
|
|
||||||
local filter: Log::Filter = [$name="http-only", $path="conn-http", $pred=http_only];
|
|
||||||
Log::add_filter(Conn::LOG, filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
This will result in a log file ``conn-http.log`` that contains only
|
|
||||||
traffic detected and analyzed as HTTP traffic.
|
|
||||||
|
|
||||||
Extending
|
|
||||||
---------
|
|
||||||
|
|
||||||
You can add further fields to a log stream by extending the record
|
|
||||||
type that defines its content. Let's say we want to add a boolean
|
|
||||||
field ``is_private`` to :bro:type:`Conn::Info` that indicates whether the
|
|
||||||
originator IP address is part of the :rfc:`1918` space:
|
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
|
@ -218,9 +151,21 @@ originator IP address is part of the :rfc:`1918` space:
|
||||||
is_private: bool &default=F &log;
|
is_private: bool &default=F &log;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
As this example shows, when extending a log stream's "Info" record, each
|
||||||
|
new field must always be declared either with a ``&default`` value or
|
||||||
|
as ``&optional``. Furthermore, you need to add the ``&log`` attribute
|
||||||
|
or otherwise the field won't appear in the log file.
|
||||||
|
|
||||||
Now we need to set the field. A connection's summary is generated at
|
Now we need to set the field. Although the details vary depending on which
|
||||||
the time its state is removed from memory. We can add another handler
|
log is being extended, in general it is important to choose a suitable event
|
||||||
|
in which to set the additional fields because we need to make sure that
|
||||||
|
the fields are set before the log record is written. Sometimes the right
|
||||||
|
choice is the same event which writes the log record, but at a higher
|
||||||
|
priority (in order to ensure that the event handler that sets the additional
|
||||||
|
fields is executed before the event handler that writes the log record).
|
||||||
|
|
||||||
|
In this example, since a connection's summary is generated at
|
||||||
|
the time its state is removed from memory, we can add another handler
|
||||||
at that time that sets our field correctly:
|
at that time that sets our field correctly:
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
@ -232,31 +177,56 @@ at that time that sets our field correctly:
|
||||||
}
|
}
|
||||||
|
|
||||||
Now ``conn.log`` will show a new field ``is_private`` of type
|
Now ``conn.log`` will show a new field ``is_private`` of type
|
||||||
``bool``.
|
``bool``. If you look at the Bro script which defines the connection
|
||||||
|
log stream :doc:`/scripts/base/protocols/conn/main.bro`, you will see
|
||||||
|
that the connection log record is written in an event handler for the
|
||||||
|
same event as used in this example to set the additional fields, but at a
|
||||||
|
lower priority than the one used in this example.
|
||||||
|
|
||||||
Notes:
|
For extending logs this way, one needs a bit of knowledge about how
|
||||||
|
the script that creates the log stream is organizing its state
|
||||||
|
keeping. Most of the standard Bro scripts attach their log state to
|
||||||
|
the :bro:type:`connection` record where it can then be accessed, just
|
||||||
|
like ``c$conn`` above. For example, the HTTP analysis adds a field
|
||||||
|
``http`` of type :bro:type:`HTTP::Info` to the :bro:type:`connection`
|
||||||
|
record.
|
||||||
|
|
||||||
- For extending logs this way, one needs a bit of knowledge about how
|
|
||||||
the script that creates the log stream is organizing its state
|
|
||||||
keeping. Most of the standard Bro scripts attach their log state to
|
|
||||||
the :bro:type:`connection` record where it can then be accessed, just
|
|
||||||
as the ``c$conn`` above. For example, the HTTP analysis adds a field
|
|
||||||
``http`` of type :bro:type:`HTTP::Info` to the :bro:type:`connection`
|
|
||||||
record. See the script reference for more information.
|
|
||||||
|
|
||||||
- When extending records as shown above, the new fields must always be
|
Define a Logging Event
|
||||||
declared either with a ``&default`` value or as ``&optional``.
|
----------------------
|
||||||
Furthermore, you need to add the ``&log`` attribute or otherwise the
|
|
||||||
field won't appear in the output.
|
|
||||||
|
|
||||||
Hooking into the Logging
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Sometimes it is helpful to do additional analysis of the information
|
Sometimes it is helpful to do additional analysis of the information
|
||||||
being logged. For these cases, a stream can specify an event that will
|
being logged. For these cases, a stream can specify an event that will
|
||||||
be generated every time a log record is written to it. All of Bro's
|
be generated every time a log record is written to it. To do this, we
|
||||||
default log streams define such an event. For example, the connection
|
need to modify the example module shown above to look something like this:
|
||||||
log stream raises the event :bro:id:`Conn::log_conn`. You
|
|
||||||
|
.. code:: bro
|
||||||
|
|
||||||
|
module Foo;
|
||||||
|
|
||||||
|
export {
|
||||||
|
redef enum Log::ID += { LOG };
|
||||||
|
|
||||||
|
type Info: record {
|
||||||
|
ts: time &log;
|
||||||
|
id: conn_id &log;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Define a logging event. By convention, this is called
|
||||||
|
# "log_<stream>".
|
||||||
|
global log_foo: event(rec: Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
event bro_init() &priority=5
|
||||||
|
{
|
||||||
|
# Here we specify both the "Info" record type and the "log_foo"
|
||||||
|
# event.
|
||||||
|
Log::create_stream(Foo::LOG, [$columns=Info, $ev=log_foo,
|
||||||
|
$path="foo"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
All of Bro's default log streams define such an event. For example, the
|
||||||
|
connection log stream raises the event :bro:id:`Conn::log_conn`. You
|
||||||
could use that for example for flagging when a connection to a
|
could use that for example for flagging when a connection to a
|
||||||
specific destination exceeds a certain duration:
|
specific destination exceeds a certain duration:
|
||||||
|
|
||||||
|
@ -281,15 +251,193 @@ externally with Perl scripts. Much of what such an external script
|
||||||
would do later offline, one may instead do directly inside of Bro in
|
would do later offline, one may instead do directly inside of Bro in
|
||||||
real-time.
|
real-time.
|
||||||
|
|
||||||
Rotation
|
Disable a Stream
|
||||||
--------
|
----------------
|
||||||
|
|
||||||
By default, no log rotation occurs, but it's globally controllable for all
|
One way to "turn off" a log is to completely disable the stream. For
|
||||||
filters by redefining the :bro:id:`Log::default_rotation_interval` option:
|
example, the following example will prevent the conn.log from being written:
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
redef Log::default_rotation_interval = 1 hr;
|
event bro_init()
|
||||||
|
{
|
||||||
|
Log::disable_stream(Conn::LOG);
|
||||||
|
}
|
||||||
|
|
||||||
|
Note that this must run after the stream is created, so the priority
|
||||||
|
of this event handler must be lower than the priority of the event handler
|
||||||
|
where the stream was created.
|
||||||
|
|
||||||
|
|
||||||
|
Filters
|
||||||
|
=======
|
||||||
|
|
||||||
|
A stream has one or more filters attached to it (a stream without any filters
|
||||||
|
will not produce any log output). When a stream is created, it automatically
|
||||||
|
gets a default filter attached to it. This default filter can be removed
|
||||||
|
or replaced, or other filters can be added to the stream. This is accomplished
|
||||||
|
by using either the :bro:id:`Log::add_filter` or :bro:id:`Log::remove_filter`
|
||||||
|
function. This section shows how to use filters to do such tasks as
|
||||||
|
rename a log file, split the output into multiple files, control which
|
||||||
|
records are written, and set a custom rotation interval.
|
||||||
|
|
||||||
|
Rename Log File
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Normally, the log filename for a given log stream is determined when the
|
||||||
|
stream is created, unless you explicitly specify a different one by adding
|
||||||
|
a filter.
|
||||||
|
|
||||||
|
The easiest way to change a log filename is to simply replace the
|
||||||
|
default log filter with a new filter that specifies a value for the "path"
|
||||||
|
field. In this example, "conn.log" will be changed to "myconn.log":
|
||||||
|
|
||||||
|
.. code:: bro
|
||||||
|
|
||||||
|
event bro_init()
|
||||||
|
{
|
||||||
|
# Replace default filter for the Conn::LOG stream in order to
|
||||||
|
# change the log filename.
|
||||||
|
|
||||||
|
local f = Log::get_filter(Conn::LOG, "default");
|
||||||
|
f$path = "myconn";
|
||||||
|
Log::add_filter(Conn::LOG, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Keep in mind that the "path" field of a log filter never contains the
|
||||||
|
filename extension. The extension will be determined later by the log writer.
|
||||||
|
|
||||||
|
Add a New Log File
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Normally, a log stream writes to only one log file. However, you can
|
||||||
|
add filters so that the stream writes to multiple files. This is useful
|
||||||
|
if you want to restrict the set of fields being logged to the new file.
|
||||||
|
|
||||||
|
In this example, a new filter is added to the Conn::LOG stream that writes
|
||||||
|
two fields to a new log file:
|
||||||
|
|
||||||
|
.. code:: bro
|
||||||
|
|
||||||
|
event bro_init()
|
||||||
|
{
|
||||||
|
# Add a new filter to the Conn::LOG stream that logs only
|
||||||
|
# timestamp and originator address.
|
||||||
|
|
||||||
|
local filter: Log::Filter = [$name="orig-only", $path="origs",
|
||||||
|
$include=set("ts", "id.orig_h")];
|
||||||
|
Log::add_filter(Conn::LOG, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Notice how the "include" filter attribute specifies a set that limits the
|
||||||
|
fields to the ones given. The names correspond to those in the
|
||||||
|
:bro:type:`Conn::Info` record (however, because the "id" field is itself a
|
||||||
|
record, we can specify an individual field of "id" by the dot notation
|
||||||
|
shown in the example).
|
||||||
|
|
||||||
|
Using the code above, in addition to the regular ``conn.log``, you will
|
||||||
|
now also get a new log file ``origs.log`` that looks like the regular
|
||||||
|
``conn.log``, but will have only the fields specified in the "include"
|
||||||
|
filter attribute.
|
||||||
|
|
||||||
|
If you want to skip only some fields but keep the rest, there is a
|
||||||
|
corresponding ``exclude`` filter attribute that you can use instead of
|
||||||
|
``include`` to list only the ones you are not interested in.
|
||||||
|
|
||||||
|
If you want to make this the only log file for the stream, you can
|
||||||
|
remove the default filter:
|
||||||
|
|
||||||
|
.. code:: bro
|
||||||
|
|
||||||
|
event bro_init()
|
||||||
|
{
|
||||||
|
# Remove the filter called "default".
|
||||||
|
Log::remove_filter(Conn::LOG, "default");
|
||||||
|
}
|
||||||
|
|
||||||
|
Determine Log Path Dynamically
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Instead of using the "path" filter attribute, a filter can determine
|
||||||
|
output paths *dynamically* based on the record being logged. That
|
||||||
|
allows, e.g., to record local and remote connections into separate
|
||||||
|
files. To do this, you define a function that returns the desired path,
|
||||||
|
and use the "path_func" filter attribute:
|
||||||
|
|
||||||
|
.. code:: bro
|
||||||
|
|
||||||
|
function myfunc(id: Log::ID, path: string, rec: Conn::Info) : string
|
||||||
|
{
|
||||||
|
# Return "conn-local" if originator is a local IP, otherwise
|
||||||
|
# return "conn-remote".
|
||||||
|
|
||||||
|
local r = Site::is_local_addr(rec$id$orig_h) ? "local" : "remote";
|
||||||
|
return fmt("%s-%s", path, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
event bro_init()
|
||||||
|
{
|
||||||
|
local filter: Log::Filter = [$name="conn-split",
|
||||||
|
$path_func=myfunc, $include=set("ts", "id.orig_h")];
|
||||||
|
Log::add_filter(Conn::LOG, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Running this will now produce two new files, ``conn-local.log`` and
|
||||||
|
``conn-remote.log``, with the corresponding entries. One could extend this
|
||||||
|
further for example to log information by subnets or even by IP
|
||||||
|
address. Be careful, however, as it is easy to create many files very
|
||||||
|
quickly.
|
||||||
|
|
||||||
|
The ``myfunc`` function has one drawback: it can be used
|
||||||
|
only with the :bro:enum:`Conn::LOG` stream as the record type is hardcoded
|
||||||
|
into its argument list. However, Bro allows to do a more generic
|
||||||
|
variant:
|
||||||
|
|
||||||
|
.. code:: bro
|
||||||
|
|
||||||
|
function myfunc(id: Log::ID, path: string,
|
||||||
|
rec: record { id: conn_id; } ) : string
|
||||||
|
{
|
||||||
|
local r = Site::is_local_addr(rec$id$orig_h) ? "local" : "remote";
|
||||||
|
return fmt("%s-%s", path, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
This function can be used with all log streams that have records
|
||||||
|
containing an ``id: conn_id`` field.
|
||||||
|
|
||||||
|
Filter Log Records
|
||||||
|
------------------
|
||||||
|
|
||||||
|
We have seen how to customize the columns being logged, but
|
||||||
|
you can also control which records are written out by providing a
|
||||||
|
predicate that will be called for each log record:
|
||||||
|
|
||||||
|
.. code:: bro
|
||||||
|
|
||||||
|
function http_only(rec: Conn::Info) : bool
|
||||||
|
{
|
||||||
|
# Record only connections with successfully analyzed HTTP traffic
|
||||||
|
return rec$service == "http";
|
||||||
|
}
|
||||||
|
|
||||||
|
event bro_init()
|
||||||
|
{
|
||||||
|
local filter: Log::Filter = [$name="http-only", $path="conn-http",
|
||||||
|
$pred=http_only];
|
||||||
|
Log::add_filter(Conn::LOG, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
This will result in a new log file ``conn-http.log`` that contains only
|
||||||
|
traffic detected and analyzed as HTTP traffic.
|
||||||
|
|
||||||
|
Rotation
|
||||||
|
--------
|
||||||
|
|
||||||
|
The log rotation interval is globally controllable for all
|
||||||
|
filters by redefining the :bro:id:`Log::default_rotation_interval` option
|
||||||
|
(note that when using BroControl, this option is set automatically via
|
||||||
|
the BroControl configuration).
|
||||||
|
|
||||||
Or specifically for certain :bro:type:`Log::Filter` instances by setting
|
Or specifically for certain :bro:type:`Log::Filter` instances by setting
|
||||||
their ``interv`` field. Here's an example of changing just the
|
their ``interv`` field. Here's an example of changing just the
|
||||||
|
@ -301,90 +449,62 @@ their ``interv`` field. Here's an example of changing just the
|
||||||
{
|
{
|
||||||
local f = Log::get_filter(Conn::LOG, "default");
|
local f = Log::get_filter(Conn::LOG, "default");
|
||||||
f$interv = 1 min;
|
f$interv = 1 min;
|
||||||
Log::remove_filter(Conn::LOG, "default");
|
|
||||||
Log::add_filter(Conn::LOG, f);
|
Log::add_filter(Conn::LOG, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASCII Writer Configuration
|
Writers
|
||||||
--------------------------
|
=======
|
||||||
|
|
||||||
The ASCII writer has a number of options for customizing the format of
|
Each filter has a writer. If you do not specify a writer when adding a
|
||||||
its output, see :doc:`/scripts/base/frameworks/logging/writers/ascii.bro`.
|
filter to a stream, then the ASCII writer is the default.
|
||||||
|
|
||||||
Adding Streams
|
There are two ways to specify a non-default writer. To change the default
|
||||||
==============
|
writer for all log filters, just redefine the :bro:id:`Log::default_writer`
|
||||||
|
option. Alternatively, you can specify the writer to use on a per-filter
|
||||||
|
basis by setting a value for the filter's "writer" field. Consult the
|
||||||
|
documentation of the writer to use to see if there are other options that are
|
||||||
|
needed.
|
||||||
|
|
||||||
It's easy to create a new log stream for custom scripts. Here's an
|
ASCII Writer
|
||||||
example for the ``Foo`` module:
|
------------
|
||||||
|
|
||||||
|
The ASCII writer has a number of options for customizing the format of its
|
||||||
|
output, see :doc:`/scripts/base/frameworks/logging/writers/ascii.bro`.
|
||||||
|
If you change the output format options, then be careful to check whether
|
||||||
|
your postprocessing scripts can still recognize your log files.
|
||||||
|
|
||||||
|
Some writer options are global (i.e., they affect all log filters using
|
||||||
|
that log writer). For example, to change the output format of all ASCII
|
||||||
|
logs to JSON format:
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
module Foo;
|
redef LogAscii::use_json = T;
|
||||||
|
|
||||||
export {
|
Some writer options are filter-specific (i.e., they affect only the filters
|
||||||
# Create an ID for our new stream. By convention, this is
|
that explicitly specify the option). For example, to change the output
|
||||||
# called "LOG".
|
format of the ``conn.log`` only:
|
||||||
redef enum Log::ID += { LOG };
|
|
||||||
|
|
||||||
# Define the fields. By convention, the type is called "Info".
|
.. code:: bro
|
||||||
type Info: record {
|
|
||||||
ts: time &log;
|
|
||||||
id: conn_id &log;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Define a hook event. By convention, this is called
|
event bro_init()
|
||||||
# "log_<stream>".
|
|
||||||
global log_foo: event(rec: Info);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# This event should be handled at a higher priority so that when
|
|
||||||
# users modify your stream later and they do it at priority 0,
|
|
||||||
# their code runs after this.
|
|
||||||
event bro_init() &priority=5
|
|
||||||
{
|
{
|
||||||
# Create the stream. This also adds a default filter automatically.
|
local f = Log::get_filter(Conn::LOG, "default");
|
||||||
Log::create_stream(Foo::LOG, [$columns=Info, $ev=log_foo, $path="foo"]);
|
# Use tab-separated-value mode
|
||||||
|
f$config = table(["tsv"] = "T");
|
||||||
|
Log::add_filter(Conn::LOG, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
You can also add the state to the :bro:type:`connection` record to make
|
|
||||||
it easily accessible across event handlers:
|
|
||||||
|
|
||||||
.. code:: bro
|
|
||||||
|
|
||||||
redef record connection += {
|
|
||||||
foo: Info &optional;
|
|
||||||
}
|
|
||||||
|
|
||||||
Now you can use the :bro:id:`Log::write` method to output log records and
|
|
||||||
save the logged ``Foo::Info`` record into the connection record:
|
|
||||||
|
|
||||||
.. code:: bro
|
|
||||||
|
|
||||||
event connection_established(c: connection)
|
|
||||||
{
|
|
||||||
local rec: Foo::Info = [$ts=network_time(), $id=c$id];
|
|
||||||
c$foo = rec;
|
|
||||||
Log::write(Foo::LOG, rec);
|
|
||||||
}
|
|
||||||
|
|
||||||
See the existing scripts for how to work with such a new connection
|
|
||||||
field. A simple example is :doc:`/scripts/base/protocols/syslog/main.bro`.
|
|
||||||
|
|
||||||
When you are developing scripts that add data to the :bro:type:`connection`
|
|
||||||
record, care must be given to when and how long data is stored.
|
|
||||||
Normally data saved to the connection record will remain there for the
|
|
||||||
duration of the connection and from a practical perspective it's not
|
|
||||||
uncommon to need to delete that data before the end of the connection.
|
|
||||||
|
|
||||||
Other Writers
|
Other Writers
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Bro supports the following built-in output formats other than ASCII:
|
Bro supports the following additional built-in output formats:
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
logging-input-sqlite
|
logging-input-sqlite
|
||||||
|
|
||||||
Further formats are available as external plugins.
|
Additional writers are available as external plugins.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue