Rotation support.

This follows rather closely how rotation currently works in
rotate-logs.bro. logging.bro now defines:

        # Default rotation interval; zero disables rotation.
        const default_rotation_interval = 0secs &redef;

        # Default naming suffix format.
        const default_rotation_date_format = "%y-%m-%d_%H.%M.%S" &redef;

        # Default postprocessor for writers outputting into files.
        const default_rotation_postprocessor = "" &redef;

        # Default function to construct the name of the rotated file.
        # The default implementation includes
        # default_rotation_date_format into the file name.
        global default_rotation_path_func: function(info: RotationInfo) : string &redef;

Writer support for rotation is optional, usually it will only make
sense for file-based writers.

TODO: Currently, there's no way to customize rotation on a per file
basis, there are only the global defaults as described above.
Individual customization is coming next.
This commit is contained in:
Robin Sommer 2011-03-06 19:28:48 -08:00
parent 90af0d06c3
commit d6cef16f77
16 changed files with 387 additions and 68 deletions

View file

@ -273,12 +273,12 @@ type entropy_test_result: record {
serial_correlation: double;
};
@load logging # sic! Not logging.bif.
# Prototypes of Bro built-in functions.
@load strings.bif.bro
@load bro.bif.bro
@load logging # sic! Not logging.bif.
global bro_alarm_file: file &redef;
global alarm_hook: function(msg: string): bool &redef;
global log_file_name: function(tag: string): string &redef;

View file

@ -3,8 +3,12 @@ module Log;
# Log::ID and Log::Writer are defined in bro.init due to circular dependencies.
export {
# The default writer to use.
const default_writer = Log::WRITER_ASCII &redef;
# Information passed to a rotation callback function.
type RotationInfo: record {
path: string; # Original path value.
open: time; # Time when opened.
close: time; # Time when closed.
};
# If true, local logging is by default enabled for all filters.
const enable_local_logging = T &redef;
@ -12,6 +16,23 @@ export {
# If true, remote logging is by default enabled for all filters.
const enable_remote_logging = T &redef;
# The default writer to use.
const default_writer = Log::WRITER_ASCII &redef;
# Default rotation interval; zero disables rotation.
const default_rotation_interval = 0secs &redef;
# Default naming suffix format.
const default_rotation_date_format = "%y-%m-%d_%H.%M.%S" &redef;
# Default postprocessor for writers outputting into files.
const default_rotation_postprocessor = "" &redef;
# Default function to construct the name of the rotated file.
# The default implementation includes
# default_rotation_date_format into the file name.
global default_rotation_path_func: function(info: RotationInfo) : string &redef;
# A stream defining the logging.
type Stream: record {
# A record type defining the log's columns.
@ -51,10 +72,10 @@ export {
exclude: set[string] &optional;
# If true, record all log records locally.
log_local: bool &default=Log::enable_local_logging;
log_local: bool &default=enable_local_logging;
# If true, pass all log records on to remote peers if they request it.
log_remote: bool &default=Log::enable_remote_logging;
log_remote: bool &default=enable_remote_logging;
# The writer to use.
writer: Writer &default=Log::default_writer;
@ -74,6 +95,11 @@ export {
module Log;
function default_rotation_path_func(info: RotationInfo) : string
{
return fmt("%s-%s", info$path, strftime(default_rotation_date_format, info$open));
}
function create_stream(id: Log::ID, stream: Log::Stream) : bool
{
if ( ! Log::__create_stream(id, stream) )

View file

@ -3,6 +3,7 @@
#include "Event.h"
#include "EventHandler.h"
#include "NetVar.h"
#include "Net.h"
#include "LogWriterAscii.h"
@ -22,6 +23,7 @@ LogWriterDefinition log_writers[] = {
struct LogMgr::Filter {
string name;
EnumVal* id;
Func* pred;
Func* path_func;
string path;
@ -37,6 +39,12 @@ struct LogMgr::Filter {
~Filter();
};
struct LogMgr::WriterInfo {
double open_time;
Timer* rotation_timer;
LogWriter *writer;
};
struct LogMgr::Stream {
EnumVal* id;
string name;
@ -45,7 +53,9 @@ struct LogMgr::Stream {
list<Filter*> filters;
typedef pair<int, string> WriterPathPair;
typedef map<WriterPathPair, LogWriter *> WriterMap;
typedef map<WriterPathPair, WriterInfo*> WriterMap;
WriterMap writers; // Writers indexed by id/path pair.
~Stream();
@ -213,7 +223,15 @@ LogMgr::Stream::~Stream()
Unref(columns);
for ( WriterMap::iterator i = writers.begin(); i != writers.end(); i++ )
{
WriterInfo* winfo = i->second;
if ( winfo->rotation_timer )
timer_mgr->Cancel(winfo->rotation_timer);
delete winfo->writer;
delete i->second;
}
for ( list<Filter*>::iterator f = filters.begin(); f != filters.end(); ++f )
delete *f;
@ -248,7 +266,7 @@ void LogMgr::RemoveDisabledWriters(Stream* stream)
for ( Stream::WriterMap::iterator j = stream->writers.begin(); j != stream->writers.end(); j++ )
{
if ( j->second->Disabled() )
if ( j->second->writer->Disabled() )
{
delete j->second;
disabled.push_back(j->first);
@ -413,16 +431,26 @@ bool LogMgr::AddFilter(EnumVal* id, RecordVal* fval)
// Create a new Filter instance.
Val* pred = fval->Lookup(rtype->FieldOffset("pred"));
Val* path_func = fval->Lookup(rtype->FieldOffset("path_func"));
Val* name = fval->LookupWithDefault(rtype->FieldOffset("name"));
Val* pred = fval->LookupWithDefault(rtype->FieldOffset("pred"));
Val* path_func = fval->LookupWithDefault(rtype->FieldOffset("path_func"));
Val* log_local = fval->LookupWithDefault(rtype->FieldOffset("log_local"));
Val* log_remote = fval->LookupWithDefault(rtype->FieldOffset("log_remote"));
Filter* filter = new Filter;
filter->name = fval->Lookup(rtype->FieldOffset("name"))->AsString()->CheckString();
filter->name = name->AsString()->CheckString();
filter->id = id->Ref()->AsEnumVal();
filter->pred = pred ? pred->AsFunc() : 0;
filter->path_func = path_func ? path_func->AsFunc() : 0;
filter->writer = writer->Ref()->AsEnumVal();
filter->local = fval->LookupWithDefault(rtype->FieldOffset("log_local"))->AsBool();
filter->remote = fval->LookupWithDefault(rtype->FieldOffset("log_remote"))->AsBool();
filter->local = log_local->AsBool();
filter->remote = log_remote->AsBool();
Unref(name);
Unref(pred);
Unref(path_func);
Unref(log_local);
Unref(log_remote);
// TODO: Check that the predciate is of the right type.
@ -580,7 +608,7 @@ bool LogMgr::Write(EnumVal* id, RecordVal* columns)
if ( w != stream->writers.end() )
// We have a writer already.
writer = w->second;
writer = w->second->writer;
else
{
// No, need to create one.
@ -669,10 +697,13 @@ LogVal** LogMgr::RecordToFilterVals(Filter* filter, RecordVal* columns)
case TYPE_COUNT:
case TYPE_COUNTER:
case TYPE_PORT:
vals[i]->val.uint_val = val->InternalUnsigned();
break;
case TYPE_PORT:
vals[i]->val.uint_val = val->AsPortVal()->Port();
break;
case TYPE_SUBNET:
vals[i]->val.subnet_val = *val->AsSubNet();
break;
@ -719,7 +750,7 @@ LogWriter* LogMgr::CreateWriter(EnumVal* id, EnumVal* writer, string path, int n
if ( w != stream->writers.end() )
// If we already have a writer for this. That's fine, we just return
// it.
return w->second;
return w->second->writer;
// Need to instantiate a new writer.
@ -766,7 +797,13 @@ LogWriter* LogMgr::CreateWriter(EnumVal* id, EnumVal* writer, string path, int n
return 0;
}
stream->writers.insert(Stream::WriterMap::value_type(Stream::WriterPathPair(writer->AsEnum(), path), writer_obj));
WriterInfo* winfo = new WriterInfo;
winfo->writer = writer_obj;
winfo->open_time = network_time;
winfo->rotation_timer = 0;
InstallRotationTimer(winfo);
stream->writers.insert(Stream::WriterMap::value_type(Stream::WriterPathPair(writer->AsEnum(), path), winfo));
return writer_obj;
}
@ -799,7 +836,7 @@ bool LogMgr::Write(EnumVal* id, EnumVal* writer, string path, int num_fields, Lo
return false;
}
bool success = w->second->Write(num_fields, vals);
bool success = w->second->writer->Write(num_fields, vals);
DBG_LOG(DBG_LOGGING, "Wrote pre-filtered record to path '%s' on stream '%s' [%s]", path.c_str(), stream->name.c_str(), (success ? "ok" : "error"));
@ -817,7 +854,7 @@ void LogMgr::SendAllWritersTo(RemoteSerializer::PeerID peer)
for ( Stream::WriterMap::iterator i = stream->writers.begin(); i != stream->writers.end(); i++ )
{
LogWriter* writer = i->second;
LogWriter* writer = i->second->writer;
EnumVal writer_val(i->first.first, BifType::Enum::Log::Writer);
remote_serializer->SendLogCreateWriter(peer, (*s)->id, &writer_val, i->first.second, writer->NumFields(), writer->Fields());
}
@ -831,7 +868,7 @@ bool LogMgr::SetBuf(EnumVal* id, bool enabled)
return false;
for ( Stream::WriterMap::iterator i = stream->writers.begin(); i != stream->writers.end(); i++ )
i->second->SetBuf(enabled);
i->second->writer->SetBuf(enabled);
RemoveDisabledWriters(stream);
@ -845,7 +882,7 @@ bool LogMgr::Flush(EnumVal* id)
return false;
for ( Stream::WriterMap::iterator i = stream->writers.begin(); i != stream->writers.end(); i++ )
i->second->Flush();
i->second->writer->Flush();
RemoveDisabledWriters(stream);
@ -857,3 +894,102 @@ void LogMgr::Error(LogWriter* writer, const char* msg)
run_time(fmt("error with writer for %s: %s", writer->Path().c_str(), msg));
}
// Timer which on dispatching rotates the filter.
class RotationTimer : public Timer {
public:
RotationTimer(double t, LogMgr::WriterInfo* arg_winfo, bool arg_rotate)
: Timer(t, TIMER_ROTATE) { winfo = arg_winfo; rotate = arg_rotate; }
~RotationTimer();
void Dispatch(double t, int is_expire);
protected:
LogMgr::WriterInfo* winfo;
bool rotate;
};
RotationTimer::~RotationTimer()
{
if ( winfo->rotation_timer == this )
winfo->rotation_timer = 0;
}
void RotationTimer::Dispatch(double t, int is_expire)
{
winfo->rotation_timer = 0;
if ( rotate )
log_mgr->Rotate(winfo);
if ( ! is_expire )
{
winfo->open_time = network_time;
log_mgr->InstallRotationTimer(winfo);
}
}
void LogMgr::InstallRotationTimer(WriterInfo* winfo)
{
if ( terminating )
return;
if ( winfo->rotation_timer )
{
timer_mgr->Cancel(winfo->rotation_timer);
winfo->rotation_timer = 0;
}
double rotation_interval = BifConst::Log::default_rotation_interval;
if ( rotation_interval )
{
// When this is called for the first time, network_time can still be
// zero. If so, we set a timer which fires immediately but doesn't
// rotate when it expires.
if ( ! network_time )
winfo->rotation_timer = new RotationTimer(1, winfo, false);
else
{
if ( ! winfo->open_time )
winfo->open_time = network_time;
const char* base_time = log_rotate_base_time ?
log_rotate_base_time->AsString()->CheckString() : 0;
double delta_t =
calc_next_rotate(rotation_interval, base_time);
winfo->rotation_timer = new RotationTimer(network_time + delta_t, winfo, true);
}
timer_mgr->Add(winfo->rotation_timer);
DBG_LOG(DBG_LOGGING, "Scheduled rotation timer for %s to %.6f", winfo->writer->Path().c_str(), winfo->rotation_timer->Time());
}
}
void LogMgr::Rotate(WriterInfo* winfo)
{
DBG_LOG(DBG_LOGGING, "Rotating %s at %.6f", winfo->writer->Path().c_str(), network_time);
// Create the RotationInfo record.
RecordVal* info = new RecordVal(BifType::Record::Log::RotationInfo);
info->Assign(0, new StringVal(winfo->writer->Path().c_str()));
info->Assign(1, new Val(winfo->open_time, TYPE_TIME));
info->Assign(2, new Val(network_time, TYPE_TIME));
// Call the function building us the new path.
Func* rotation_path_func = internal_func("Log::default_rotation_path_func");
string rotation_postprocessor = BifConst::Log::default_rotation_postprocessor->AsString()->CheckString();
val_list vl(1);
vl.append(info);
Val* result = rotation_path_func->Call(&vl);
string new_path = result->AsString()->CheckString();
Unref(result);
winfo->writer->Rotate(new_path, rotation_postprocessor, winfo->open_time, network_time, terminating);
}

View file

@ -57,6 +57,7 @@ private:
class LogWriter;
class RemoteSerializer;
class RotationTimer;
class LogMgr {
public:
@ -75,6 +76,7 @@ public:
protected:
friend class LogWriter;
friend class RemoteSerializer;
friend class RotationTimer;
// These function are also used by the RemoteSerializer.
LogWriter* CreateWriter(EnumVal* id, EnumVal* writer, string path, int num_fields, LogField** fields); // takes ownership of fields.
@ -89,11 +91,14 @@ protected:
private:
struct Filter;
struct Stream;
struct WriterInfo;
bool TraverseRecord(Filter* filter, RecordType* rt, TableVal* include, TableVal* exclude, string path, list<int> indices);
LogVal** RecordToFilterVals(Filter* filter, RecordVal* columns);
Stream* FindStream(EnumVal* id);
void RemoveDisabledWriters(Stream* stream);
void InstallRotationTimer(WriterInfo* winfo);
void Rotate(WriterInfo* info);
vector<Stream *> streams; // Indexed by stream enum.
};

View file

@ -18,7 +18,7 @@ LogWriter::~LogWriter()
delete [] fields;
}
bool LogWriter::Init(string arg_path, int arg_num_fields, LogField** arg_fields)
bool LogWriter::Init(string arg_path, int arg_num_fields, const LogField* const * arg_fields)
{
path = arg_path;
num_fields = arg_num_fields;
@ -75,6 +75,17 @@ bool LogWriter::SetBuf(bool enabled)
return true;
}
bool LogWriter::Rotate(string rotated_path, string postprocessor, double open, double close, bool terminating)
{
if ( ! DoRotate(rotated_path, postprocessor, open, close, terminating) )
{
disabled = true;
return false;
}
return true;
}
bool LogWriter::Flush()
{
if ( ! DoFlush() )
@ -126,4 +137,45 @@ void LogWriter::DeleteVals(LogVal** vals)
delete vals[i];
}
bool LogWriter::RunPostProcessor(string fname, string postprocessor, string old_name, double open, double close, bool terminating)
{
// This function operates in way backwards-compatible with the old Bro
// log rotation scheme.
if ( ! postprocessor.size() )
return true;
const char* const fmt = "%y-%m-%d_%H.%M.%S";
struct tm tm1;
struct tm tm2;
time_t tt1 = (time_t)open;
time_t tt2 = (time_t)close;
localtime_r(&tt1, &tm1);
localtime_r(&tt2, &tm2);
char buf1[128];
char buf2[128];
strftime(buf1, sizeof(buf1), fmt, &tm1);
strftime(buf2, sizeof(buf2), fmt, &tm2);
string cmd = postprocessor;
cmd += " " + fname;
cmd += " " + old_name;
cmd += " " + string(buf1);
cmd += " " + string(buf2);
cmd += " " + string(terminating ? "1" : "0");
cmd += " &";
system(cmd.c_str());
return true;
}

View file

@ -23,7 +23,7 @@ public:
//
// The new instance takes ownership of "fields", and will delete them
// when done.
bool Init(string path, int num_fields, LogField** fields);
bool Init(string path, int num_fields, const LogField* const * fields);
// Writes one log entry. The method takes ownership of "vals" and will
// return immediately after queueing the write request, potentially
@ -35,14 +35,19 @@ public:
// Sets the buffering status for the writer, if the writer supports it.
bool SetBuf(bool enabled);
// Flushes any currently buffered output, if the writer support it.
// Flushes any currently buffered output, if the writer supports it.
bool Flush();
// Triggers rotation, if the writer supports it.
bool Rotate(string rotated_path, string postprocessor, double open, double close, bool terminating);
// Finished writing to this logger. Will not be called if an error has
// been indicated earlier. After calling this, no more writing must be
// performed.
void Finish();
// Returns the path as passed to Init().
const string Path() const { return path; }
int NumFields() const { return num_fields; }
const LogField* const * Fields() const { return fields; }
@ -57,10 +62,10 @@ protected:
// always return true.
// Called once for initialization of the Writer.
virtual bool DoInit(string path, int num_fields, LogField** fields) = 0;
virtual bool DoInit(string path, int num_fields, const LogField* const * fields) = 0;
// Called once per entry to record.
virtual bool DoWrite(int num_fields, LogField** fields, LogVal** vals) = 0;
virtual bool DoWrite(int num_fields, const LogField* const * fields, LogVal** vals) = 0;
// Called when the buffering status for this writer is changed. If
// buffering is disabled, the writer should attempt to write out
@ -80,25 +85,24 @@ protected:
virtual bool DoFlush() = 0;
// Called when a log output is to be rotated. Most directly, this only
// applies to writers outputting files, thoug a writer may also trigger
// other regular actions if that fits a similar model
// applies to writers outputting files, though a writer may also trigger
// other regular actions if semantics are similar.
//
// The string "rotate_path" is interpreted in writer-specific way, yet
// should generally should have similar semantics that the "path" passed
// should generally should have similar semantics as the "path" passed
// into DoInit(), except that now it reflects the name to where the
// rotated output is to be moved. After rotation, output should continue
// normally with the standard "path". As an example, for file-based
// output, the rotate_path may be the original filename with an embedded
// timestamp.
//
// The writer must return false if an error occured that prevent the
// writer for continuing operation; it will then be disabled. The error
// reason should be reported via Error(). If a recoverable error occurs,
// still call Error(), but return true.
// timestamp. "postprocessor" is the name of a command to execute on the
// rotated file. If empty, no such processing should take place; if given
// but the writer doesn't support postprocessing, it can be ignored.
// "open" and "close" are the network time's at opening and closeing the
// current file, respetively.
//
// A writer may ignore rotation requests if it doesn't fit with its
// semantics.
virtual bool DoRotate(string rotated_path) = 0;
virtual bool DoRotate(string rotated_path, string postprocessor, double open, double close, bool terminating) = 0;
// Called once on termination. Not called when any of the other methods
// has previously signaled an error, i.e., executing this method signals
@ -116,8 +120,8 @@ protected:
// Reports an error.
void Error(const char *msg);
// Returns the path as passed to Init().
const string Path() const { return path; }
// Runs a post-processor on the given file.
bool RunPostProcessor(string fname, string postprocessor, string old_name, double open, double close, bool terminating);
private:
friend class LogMgr;
@ -131,7 +135,7 @@ private:
string path;
int num_fields;
LogField** fields;
const LogField* const * fields;
bool buffering;
bool disabled;

View file

@ -6,26 +6,22 @@
LogWriterAscii::LogWriterAscii()
{
fname = 0;
file = 0;
}
LogWriterAscii::~LogWriterAscii()
{
if ( fname )
free(fname);
if ( file )
fclose(file);
}
bool LogWriterAscii::DoInit(string path, int num_fields, LogField** fields)
bool LogWriterAscii::DoInit(string path, int num_fields, const LogField* const * fields)
{
fname = strdup(Fmt("%s.log", path.c_str()));
fname = path + ".log";
if ( ! (file = fopen(fname, "w")) )
if ( ! (file = fopen(fname.c_str(), "w")) )
{
Error(Fmt("cannot open %s: %s", fname, strerror(errno)));
Error(Fmt("cannot open %s: %s", fname.c_str(), strerror(errno)));
return false;
}
@ -34,7 +30,7 @@ bool LogWriterAscii::DoInit(string path, int num_fields, LogField** fields)
for ( int i = 0; i < num_fields; i++ )
{
LogField* field = fields[i];
const LogField* field = fields[i];
if ( fputs(field->name.c_str(), file) == EOF )
goto write_error;
@ -48,7 +44,7 @@ bool LogWriterAscii::DoInit(string path, int num_fields, LogField** fields)
return true;
write_error:
Error(Fmt("error writing to %s: %s", fname, strerror(errno)));
Error(Fmt("error writing to %s: %s", fname.c_str(), strerror(errno)));
return false;
}
@ -62,7 +58,7 @@ void LogWriterAscii::DoFinish()
{
}
bool LogWriterAscii::DoWrite(int num_fields, LogField** fields, LogVal** vals)
bool LogWriterAscii::DoWrite(int num_fields, const LogField* const * fields, LogVal** vals)
{
ODesc desc(DESC_READABLE);
@ -72,7 +68,7 @@ bool LogWriterAscii::DoWrite(int num_fields, LogField** fields, LogVal** vals)
desc.Add("\t");
LogVal* val = vals[i];
LogField* field = fields[i];
const LogField* field = fields[i];
if ( ! val->present )
{
@ -127,7 +123,7 @@ bool LogWriterAscii::DoWrite(int num_fields, LogField** fields, LogVal** vals)
if ( fwrite(desc.Bytes(), desc.Len(), 1, file) != 1 )
{
Error(Fmt("error writing to %s: %s", fname, strerror(errno)));
Error(Fmt("error writing to %s: %s", fname.c_str(), strerror(errno)));
return false;
}
@ -137,9 +133,17 @@ bool LogWriterAscii::DoWrite(int num_fields, LogField** fields, LogVal** vals)
return true;
}
bool LogWriterAscii::DoRotate(string rotated_path)
bool LogWriterAscii::DoRotate(string rotated_path, string postprocessor, double open, double close, bool terminating)
{
return true;
fclose(file);
string nname = rotated_path + ".log";
rename(fname.c_str(), nname.c_str());
if ( postprocessor.size() && ! RunPostProcessor(nname, postprocessor, fname.c_str(), open, close, terminating) )
return false;
return DoInit(Path(), NumFields(), Fields());
}
bool LogWriterAscii::DoSetBuf(bool enabled)

View file

@ -15,16 +15,16 @@ public:
static LogWriter* Instantiate() { return new LogWriterAscii; }
protected:
virtual bool DoInit(string path, int num_fields, LogField** fields);
virtual bool DoWrite(int num_fields, LogField** fields, LogVal** vals);
virtual bool DoInit(string path, int num_fields, const LogField* const * fields);
virtual bool DoWrite(int num_fields, const LogField* const * fields, LogVal** vals);
virtual bool DoSetBuf(bool enabled);
virtual bool DoRotate(string rotated_path);
virtual bool DoRotate(string rotated_path, string postprocessr, double open, double close, bool terminating);
virtual bool DoFlush();
virtual void DoFinish();
private:
FILE* file;
char* fname;
string fname;
};
#endif

View file

@ -113,9 +113,10 @@ TraversalCode Scope::Traverse(TraversalCallback* cb) const
}
ID* lookup_ID(const char* name, const char* curr_module, bool no_global)
ID* lookup_ID(const char* name, const char* curr_module, bool no_global, bool same_module_only)
{
string fullname = make_full_var_name(curr_module, name);
string ID_module = extract_module_name(fullname.c_str());
bool need_export = ID_module != GLOBAL_MODULE_NAME &&
ID_module != curr_module;
@ -134,7 +135,7 @@ ID* lookup_ID(const char* name, const char* curr_module, bool no_global)
}
}
if ( ! no_global )
if ( ! no_global && (strcmp(GLOBAL_MODULE_NAME, curr_module) == 0 || ! same_module_only) )
{
string globalname = make_full_var_name(GLOBAL_MODULE_NAME, name);
ID* id = global_scope()->Lookup(globalname.c_str());

View file

@ -65,7 +65,7 @@ extern bool in_debug;
// If no_global is true, don't search in the default "global" namespace.
extern ID* lookup_ID(const char* name, const char* module,
bool no_global = false);
bool no_global = false, bool same_module_only=false);
extern ID* install_ID(const char* name, const char* module_name,
bool is_global, bool is_export);

View file

@ -8,6 +8,10 @@ module Log;
type Filter: record;
type Stream: record;
type RotationInfo: record;
const Log::default_rotation_interval: interval;
const Log::default_rotation_postprocessor: string;
function Log::__create_stream%(id: Log::ID, stream: Log::Stream%) : bool
%{

View file

@ -42,7 +42,7 @@
%left '$' '[' ']' '(' ')' TOK_HAS_FIELD TOK_HAS_ATTR
%type <str> TOK_ID TOK_PATTERN_TEXT single_pattern
%type <id> local_id global_id event_id global_or_event_id resolve_id begin_func
%type <id> local_id global_id def_global_id event_id global_or_event_id resolve_id begin_func
%type <id_l> local_id_list
%type <ic> init_class
%type <expr> opt_init
@ -102,6 +102,7 @@ Expr* bro_this = 0;
int in_init = 0;
bool in_debug = false;
bool resolving_global_ID = false;
bool defining_global_ID = false;
ID* func_id = 0;
EnumType *cur_enum_type = 0;
@ -842,10 +843,10 @@ decl:
| TOK_EXPORT '{' { is_export = true; } decl_list '}'
{ is_export = false; }
| TOK_GLOBAL global_id opt_type init_class opt_init opt_attr ';'
| TOK_GLOBAL def_global_id opt_type init_class opt_init opt_attr ';'
{ add_global($2, $3, $4, $5, $6, VAR_REGULAR); }
| TOK_CONST global_id opt_type init_class opt_init opt_attr ';'
| TOK_CONST def_global_id opt_type init_class opt_init opt_attr ';'
{ add_global($2, $3, $4, $5, $6, VAR_CONST); }
| TOK_REDEF global_id opt_type init_class opt_init opt_attr ';'
@ -855,7 +856,7 @@ decl:
'{' { parser_redef_enum($3); } enum_body '}' ';'
{ /* no action */ }
| TOK_TYPE global_id ':' refined_type opt_attr ';'
| TOK_TYPE def_global_id ':' refined_type opt_attr ';'
{
add_type($2, $4, $5, 0);
}
@ -883,7 +884,7 @@ conditional:
;
func_hdr:
TOK_FUNCTION global_id func_params
TOK_FUNCTION def_global_id func_params
{
begin_func($2, current_module.c_str(),
FUNC_FLAVOR_FUNCTION, 0, $3);
@ -1265,6 +1266,11 @@ global_id:
{ $$ = $2; }
;
def_global_id:
{ defining_global_ID = 1; } global_id { defining_global_ID = 0; }
{ $$ = $2; }
;
event_id:
{ resolving_global_ID = 0; } global_or_event_id
{ $$ = $2; }
@ -1275,7 +1281,7 @@ global_or_event_id:
{
set_location(@1);
$$ = lookup_ID($1, current_module.c_str(), false);
$$ = lookup_ID($1, current_module.c_str(), false, defining_global_ID);
if ( $$ )
{
if ( ! $$->IsGlobal() )

View file

@ -0,0 +1,50 @@
test-11-03-06_19.00.05.log test.log 11-03-06_19.00.05 11-03-06_20.00.05 0
test-11-03-06_20.00.05.log test.log 11-03-06_20.00.05 11-03-06_21.00.05 0
test-11-03-06_21.00.05.log test.log 11-03-06_21.00.05 11-03-06_22.00.05 0
test-11-03-06_22.00.05.log test.log 11-03-06_22.00.05 11-03-06_23.00.05 0
test-11-03-06_23.00.05.log test.log 11-03-06_23.00.05 11-03-07_00.00.05 0
test-11-03-07_00.00.05.log test.log 11-03-07_00.00.05 11-03-07_01.00.05 0
test-11-03-07_01.00.05.log test.log 11-03-07_01.00.05 11-03-07_02.00.05 0
test-11-03-07_02.00.05.log test.log 11-03-07_02.00.05 11-03-07_03.00.05 0
test-11-03-07_03.00.05.log test.log 11-03-07_03.00.05 11-03-07_04.00.05 0
test-11-03-07_04.00.05.log test.log 11-03-07_04.00.05 11-03-07_04.59.55 1
> test-11-03-06_19.00.05.log
# t id.orig_h id.orig_p id.resp_h id.resp_p
1299466805.0 10.0.0.1 20 10.0.0.2 1024
1299470395.0 10.0.0.2 20 10.0.0.3 0
> test-11-03-06_20.00.05.log
# t id.orig_h id.orig_p id.resp_h id.resp_p
1299470405.0 10.0.0.1 20 10.0.0.2 1025
1299473995.0 10.0.0.2 20 10.0.0.3 1
> test-11-03-06_21.00.05.log
# t id.orig_h id.orig_p id.resp_h id.resp_p
1299474005.0 10.0.0.1 20 10.0.0.2 1026
1299477595.0 10.0.0.2 20 10.0.0.3 2
> test-11-03-06_22.00.05.log
# t id.orig_h id.orig_p id.resp_h id.resp_p
1299477605.0 10.0.0.1 20 10.0.0.2 1027
1299481195.0 10.0.0.2 20 10.0.0.3 3
> test-11-03-06_23.00.05.log
# t id.orig_h id.orig_p id.resp_h id.resp_p
1299481205.0 10.0.0.1 20 10.0.0.2 1028
1299484795.0 10.0.0.2 20 10.0.0.3 4
> test-11-03-07_00.00.05.log
# t id.orig_h id.orig_p id.resp_h id.resp_p
1299484805.0 10.0.0.1 20 10.0.0.2 1029
1299488395.0 10.0.0.2 20 10.0.0.3 5
> test-11-03-07_01.00.05.log
# t id.orig_h id.orig_p id.resp_h id.resp_p
1299488405.0 10.0.0.1 20 10.0.0.2 1030
1299491995.0 10.0.0.2 20 10.0.0.3 6
> test-11-03-07_02.00.05.log
# t id.orig_h id.orig_p id.resp_h id.resp_p
1299492005.0 10.0.0.1 20 10.0.0.2 1031
1299495595.0 10.0.0.2 20 10.0.0.3 7
> test-11-03-07_03.00.05.log
# t id.orig_h id.orig_p id.resp_h id.resp_p
1299495605.0 10.0.0.1 20 10.0.0.2 1032
1299499195.0 10.0.0.2 20 10.0.0.3 8
> test-11-03-07_04.00.05.log
# t id.orig_h id.orig_p id.resp_h id.resp_p
1299499205.0 10.0.0.1 20 10.0.0.2 1033
1299502795.0 10.0.0.2 20 10.0.0.3 9

View file

@ -4,7 +4,7 @@ TestDirs = logging
TmpDir = %(testbase)s/.tmp
BaselineDir = %(testbase)s/Baseline
IgnoreDirs = .svn CVS .tmp
IgnoreFiles = *.tmp *.swp #*
IgnoreFiles = *.tmp *.swp #* *.trace
[environment]
BROPATH=`bash -c %(testbase)s/../../build/bro-path-dev`

View file

@ -0,0 +1,31 @@
#
# @TEST-EXEC: bro -r %DIR/rotation.trace %INPUT >out
# @TEST-EXEC: for i in test-*.log; do printf '> %s\n' $i; cat $i; done >>out
# @TEST-EXEC: btest-diff out
module Test;
export {
# Create a new ID for our log stream
redef enum Log::ID += { Test };
# Define a record with all the columns the log file can have.
# (I'm using a subset of fields from ssh-ext for demonstration.)
type Log: record {
t: time;
id: conn_id; # Will be rolled out into individual columns.
};
}
redef Log::default_rotation_interval = 1hr;
redef Log::default_rotation_postprocessor = "echo";
event bro_init()
{
Log::create_stream(Test, [$columns=Log]);
}
event new_connection(c: connection)
{
Log::write(Test, [$t=network_time(), $id=c$id]);
}

Binary file not shown.