一 說明
suricata的日志先按照packet日志/事務(wù)日志/文件日志/流日志進(jìn)行分類,這個在output.c的OutputRegisterRootLoggers函數(shù)中有注冊,內(nèi)容如下:
void OutputRegisterRootLoggers(void)
{
OutputPacketLoggerRegister();
OutputTxLoggerRegister();
OutputFiledataLoggerRegister();
OutputFileLoggerRegister();
OutputStreamingLoggerRegister();
}
以tx的日志為例,看下注冊的代碼:
void OutputTxLoggerRegister (void)
{
OutputRegisterRootLogger(OutputTxLogThreadInit, OutputTxLogThreadDeinit,
OutputTxLogExitPrintStats, OutputTxLog);
}
將這種大類的日志注冊到rootLogger上面如下:
void OutputRegisterRootLogger(ThreadInitFunc ThreadInit,
ThreadDeinitFunc ThreadDeinit,
ThreadExitPrintStatsFunc ThreadExitPrintStats,
OutputLogFunc LogFunc)
{
RootLogger *logger = SCCalloc(1, sizeof(*logger));
if (logger == NULL) {
return;
}
logger->ThreadInit = ThreadInit;
logger->ThreadDeinit = ThreadDeinit;
logger->ThreadExitPrintStats = ThreadExitPrintStats;
logger->LogFunc = LogFunc;
TAILQ_INSERT_TAIL(&RootLoggers, logger, entries);
}
RootLoggers就是全局的大類日志,循環(huán)調(diào)用每個大類的日志輸出內(nèi)容如下:
TmEcode OutputLoggerLog(ThreadVars *tv, Packet *p, void *thread_data)
{
LoggerThreadStore *thread_store = (LoggerThreadStore *)thread_data;
RootLogger *logger = TAILQ_FIRST(&RootLoggers);
LoggerThreadStoreNode *thread_store_node = TAILQ_FIRST(thread_store);
while (logger && thread_store_node) {
if (logger->LogFunc != NULL) {
logger->LogFunc(tv, p, thread_store_node->thread_data);
}
logger = TAILQ_NEXT(logger, entries);
thread_store_node = TAILQ_NEXT(thread_store_node, entries);
}
return TM_ECODE_OK;
}
那看下PacketLog(這個日志更簡單)這個大類的日志輸出吧:
static TmEcode OutputPacketLog(ThreadVars *tv, Packet *p, void *thread_data)
{
BUG_ON(thread_data == NULL);
if (list == NULL) {
/* No child loggers. */
return TM_ECODE_OK;
}
OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
OutputPacketLogger *logger = list;
OutputLoggerThreadStore *store = op_thread_data->store;
BUG_ON(logger == NULL && store != NULL);
BUG_ON(logger != NULL && store == NULL);
BUG_ON(logger == NULL && store == NULL);
while (logger && store) {
BUG_ON(logger->LogFunc == NULL || logger->ConditionFunc == NULL);
if ((logger->ConditionFunc(tv, (const Packet *)p)) == TRUE) {
PACKET_PROFILING_LOGGER_START(p, logger->logger_id);
logger->LogFunc(tv, store->thread_data, (const Packet *)p);
PACKET_PROFILING_LOGGER_END(p, logger->logger_id);
}
logger = logger->next;
store = store->next;
BUG_ON(logger == NULL && store != NULL);
BUG_ON(logger != NULL && store == NULL);
}
return TM_ECODE_OK;
}
關(guān)鍵是從list這個靜態(tài)鏈表中取日志,然后輸出,那么這個靜態(tài)的鏈表的值在哪里賦值的那?
我們看下非root類的日志注冊:
void OutputRegisterLoggers(void) {
/* custom format log*/
LogCustomFormatRegister();
LuaLogRegister();
/* fast log */
AlertFastLogRegister();
/* debug log */
AlertDebugLogRegister();
/* prelue log */
AlertPreludeRegister();
/* syslog log */
AlertSyslogRegister();
/* unified2 log */
Unified2AlertRegister();
/* drop log */
LogDropLogRegister();
JsonDropLogRegister();
....
}
很多,看下具體一個注冊到底干了什么事情?
void JsonDropLogRegister (void)
{
OutputRegisterPacketModule(LOGGER_JSON_DROP, MODULE_NAME, "drop-json-log",
JsonDropLogInitCtx, JsonDropLogger, JsonDropLogCondition,
JsonDropLogThreadInit, JsonDropLogThreadDeinit, NULL);
OutputRegisterPacketSubModule(LOGGER_JSON_DROP, "eve-log", MODULE_NAME,
"eve-log.drop", JsonDropLogInitCtxSub, JsonDropLogger,
JsonDropLogCondition, JsonDropLogThreadInit, JsonDropLogThreadDeinit,
NULL);
}
在進(jìn)一步看下怎么注冊的:
void OutputRegisterPacketModule(LoggerId id, const char *name,
const char *conf_name, OutputInitFunc InitFunc,
PacketLogger PacketLogFunc, PacketLogCondition PacketConditionFunc,
ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit,
ThreadExitPrintStatsFunc ThreadExitPrintStats)
{
if (unlikely(PacketLogFunc == NULL || PacketConditionFunc == NULL)) {
goto error;
}
OutputModule *module = SCCalloc(1, sizeof(*module));
if (unlikely(module == NULL)) {
goto error;
}
module->logger_id = id;
module->name = name;
module->conf_name = conf_name;
module->InitFunc = InitFunc;
module->PacketLogFunc = PacketLogFunc;
module->PacketConditionFunc = PacketConditionFunc;
module->ThreadInit = ThreadInit;
module->ThreadDeinit = ThreadDeinit;
module->ThreadExitPrintStats = ThreadExitPrintStats;
TAILQ_INSERT_TAIL(&output_modules, module, entries);
SCLogDebug("Packet logger \"%s\" registered.", name);
return;
error:
SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
exit(EXIT_FAILURE);
}
申請一個日志模塊,然后加入到全局變量:output_modules 里面。
這個地方有點蒙了,為什么循環(huán)用的是list,這里面是日志模塊,這兩者是怎么關(guān)聯(lián)的。
通過循環(huán)獲取output_modules內(nèi)容填充到list中去。runmodes.c里面的函數(shù)如下:
static void RunModeInitializeEveOutput(ConfNode *conf, OutputCtx *parent_ctx)
{
ConfNode *types = ConfNodeLookupChild(conf, "types");
SCLogDebug("types %p", types);
if (types == NULL) {
return;
}
ConfNode *type = NULL;
TAILQ_FOREACH(type, &types->head, next) {
SCLogConfig("enabling 'eve-log' module '%s'", type->val);
int sub_count = 0;
char subname[256];
snprintf(subname, sizeof(subname), "eve-log.%s", type->val);
/* Now setup all registers logger of this name. */
OutputModule *sub_module;
TAILQ_FOREACH(sub_module, &output_modules, entries) {
if (strcmp(subname, sub_module->conf_name) == 0) {
sub_count++;
if (sub_module->parent_name == NULL ||
strcmp(sub_module->parent_name, "eve-log") != 0) {
FatalError(SC_ERR_INVALID_ARGUMENT,
"bad parent for %s", subname);
}
if (sub_module->InitSubFunc == NULL) {
FatalError(SC_ERR_INVALID_ARGUMENT,
"bad sub-module for %s", subname);
}
ConfNode *sub_output_config =
ConfNodeLookupChild(type, type->val);
// sub_output_config may be NULL if no config
/* pass on parent output_ctx */
OutputInitResult result =
sub_module->InitSubFunc(sub_output_config, parent_ctx);
if (!result.ok || result.ctx == NULL) {
continue;
}
AddOutputToFreeList(sub_module, result.ctx);
SetupOutput(sub_module->name, sub_module,
result.ctx);
}
}
/* Error is no registered loggers with this name
* were found .*/
if (!sub_count) {
FatalErrorOnInit(SC_ERR_INVALID_ARGUMENT,
"No output module named %s", subname);
continue;
}
}
}
runmodes.c中有個SetupOut函數(shù):
static void SetupOutput(const char *name, OutputModule *module, OutputCtx *output_ctx)
{
/* flow logger doesn't run in the packet path */
if (module->FlowLogFunc) {
OutputRegisterFlowLogger(module->name, module->FlowLogFunc,
output_ctx, module->ThreadInit, module->ThreadDeinit,
module->ThreadExitPrintStats);
return;
}
/* stats logger doesn't run in the packet path */
if (module->StatsLogFunc) {
OutputRegisterStatsLogger(module->name, module->StatsLogFunc,
output_ctx,module->ThreadInit, module->ThreadDeinit,
module->ThreadExitPrintStats);
return;
}
if (module->logger_id == LOGGER_ALERT_DEBUG) {
debuglog_enabled = 1;
}
if (module->PacketLogFunc) {
SCLogDebug("%s is a packet logger", module->name);
OutputRegisterPacketLogger(module->logger_id, module->name,
module->PacketLogFunc, module->PacketConditionFunc, output_ctx,
module->ThreadInit, module->ThreadDeinit,
module->ThreadExitPrintStats);
} else if (module->TxLogFunc) {
SCLogDebug("%s is a tx logger", module->name);
OutputRegisterTxLogger(module->logger_id, module->name, module->alproto,
module->TxLogFunc, output_ctx, module->tc_log_progress,
module->ts_log_progress, module->TxLogCondition,
module->ThreadInit, module->ThreadDeinit,
module->ThreadExitPrintStats);
logger_bits[module->alproto] |= (1<<module->logger_id);
} else if (module->FiledataLogFunc) {
SCLogDebug("%s is a filedata logger", module->name);
OutputRegisterFiledataLogger(module->logger_id, module->name,
module->FiledataLogFunc, output_ctx, module->ThreadInit,
module->ThreadDeinit, module->ThreadExitPrintStats);
filedata_logger_count++;
} else if (module->FileLogFunc) {
SCLogDebug("%s is a file logger", module->name);
OutputRegisterFileLogger(module->logger_id, module->name,
module->FileLogFunc, output_ctx, module->ThreadInit,
module->ThreadDeinit, module->ThreadExitPrintStats);
file_logger_count++;
} else if (module->StreamingLogFunc) {
SCLogDebug("%s is a streaming logger", module->name);
OutputRegisterStreamingLogger(module->logger_id, module->name,
module->StreamingLogFunc, output_ctx, module->stream_type,
module->ThreadInit, module->ThreadDeinit,
module->ThreadExitPrintStats);
} else {
SCLogError(SC_ERR_INVALID_ARGUMENT, "Unknown logger type: name=%s",
module->name);
}
}
二 整理下邏輯
2.1 初始化思路:
1)首先通過rootlogger的注冊,注冊幾個基本的大日志,注冊到RootLoggers里面。
注冊的循環(huán)函數(shù)是對一個大類里面的list做循環(huán)調(diào)用打印日志。
2)通過非rootLogger的日志注冊,注冊具體的日志到output_modules模塊中。
3)在初始化的時候會將output_modules 變成串到相關(guān)大類的list里面去。
2.3 調(diào)用思路
1)通過flow-worker.c中的OutputLoggerLog 函數(shù)調(diào)用。
2)上面函數(shù)遍歷:RootLoggers 調(diào)用相關(guān)大類的日志打印函數(shù)。
3)日志打印函數(shù)遍歷內(nèi)部的list循環(huán)打印。
4)list的循環(huán)其實是注冊具體日志的具體日志模塊轉(zhuǎn)變得到的大類日志。
5)實際輸出日志內(nèi)容。
