mirror of https://github.com/OISF/suricata
Introduce stats log API, convert existing output
Convert regular 'stats.log' output to this new API. In addition to the current stats value, also give the last value. This makes it easy to display the difference.pull/1234/head
parent
ee8da21e36
commit
e98346b555
@ -0,0 +1,231 @@
|
||||
/* Copyright (C) 2014 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Victor Julien <victor@inliniac.net>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "debug.h"
|
||||
#include "detect.h"
|
||||
#include "pkt-var.h"
|
||||
#include "conf.h"
|
||||
|
||||
#include "threads.h"
|
||||
#include "threadvars.h"
|
||||
#include "tm-threads.h"
|
||||
|
||||
#include "util-print.h"
|
||||
#include "util-unittest.h"
|
||||
|
||||
#include "util-debug.h"
|
||||
|
||||
#include "output.h"
|
||||
#include "log-stats.h"
|
||||
#include "util-privs.h"
|
||||
#include "util-buffer.h"
|
||||
|
||||
#include "util-logopenfile.h"
|
||||
#include "util-time.h"
|
||||
|
||||
#define DEFAULT_LOG_FILENAME "stats.log"
|
||||
#define MODULE_NAME "LogStatsLog"
|
||||
#define OUTPUT_BUFFER_SIZE 65535
|
||||
|
||||
TmEcode LogStatsLogThreadInit(ThreadVars *, void *, void **);
|
||||
TmEcode LogStatsLogThreadDeinit(ThreadVars *, void *);
|
||||
void LogStatsLogExitPrintStats(ThreadVars *, void *);
|
||||
static void LogStatsLogDeInitCtx(OutputCtx *);
|
||||
|
||||
typedef struct LogStatsFileCtx_ {
|
||||
LogFileCtx *file_ctx;
|
||||
uint32_t flags; /** Store mode */
|
||||
} LogStatsFileCtx;
|
||||
|
||||
typedef struct LogStatsLogThread_ {
|
||||
LogStatsFileCtx *statslog_ctx;
|
||||
MemBuffer *buffer;
|
||||
} LogStatsLogThread;
|
||||
|
||||
int LogStatsLogger(ThreadVars *tv, void *thread_data, const StatsTable *st)
|
||||
{
|
||||
SCEnter();
|
||||
LogStatsLogThread *aft = (LogStatsLogThread *)thread_data;
|
||||
|
||||
struct timeval tval;
|
||||
struct tm *tms;
|
||||
|
||||
gettimeofday(&tval, NULL);
|
||||
struct tm local_tm;
|
||||
tms = SCLocalTime(tval.tv_sec, &local_tm);
|
||||
|
||||
/* Calculate the Engine uptime */
|
||||
int up_time = (int)difftime(tval.tv_sec, st->start_time);
|
||||
int sec = up_time % 60; // Seconds in a minute
|
||||
int in_min = up_time / 60;
|
||||
int min = in_min % 60; // Minutes in a hour
|
||||
int in_hours = in_min / 60;
|
||||
int hours = in_hours % 24; // Hours in a day
|
||||
int days = in_hours / 24;
|
||||
|
||||
MemBufferWriteString(aft->buffer, "----------------------------------------------"
|
||||
"---------------------\n");
|
||||
MemBufferWriteString(aft->buffer, "Date: %" PRId32 "/%" PRId32 "/%04d -- "
|
||||
"%02d:%02d:%02d (uptime: %"PRId32"d, %02dh %02dm %02ds)\n",
|
||||
tms->tm_mon + 1, tms->tm_mday, tms->tm_year + 1900, tms->tm_hour,
|
||||
tms->tm_min, tms->tm_sec, days, hours, min, sec);
|
||||
MemBufferWriteString(aft->buffer, "----------------------------------------------"
|
||||
"---------------------\n");
|
||||
MemBufferWriteString(aft->buffer, "%-25s | %-25s | %-s\n", "Counter", "TM Name",
|
||||
"Value");
|
||||
MemBufferWriteString(aft->buffer, "----------------------------------------------"
|
||||
"---------------------\n");
|
||||
|
||||
uint32_t u = 0;
|
||||
for (u = 0; u < st->nstats; u++) {
|
||||
if (st->stats[u].name == NULL)
|
||||
break;
|
||||
MemBufferWriteString(aft->buffer, "%-25s | %-25s | %-" PRIu64 "\n",
|
||||
st->stats[u].name, st->stats[u].tm_name, st->stats[u].value);
|
||||
}
|
||||
|
||||
SCMutexLock(&aft->statslog_ctx->file_ctx->fp_mutex);
|
||||
aft->statslog_ctx->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer),
|
||||
MEMBUFFER_OFFSET(aft->buffer), aft->statslog_ctx->file_ctx);
|
||||
SCMutexUnlock(&aft->statslog_ctx->file_ctx->fp_mutex);
|
||||
|
||||
MemBufferReset(aft->buffer);
|
||||
|
||||
SCReturnInt(0);
|
||||
}
|
||||
|
||||
TmEcode LogStatsLogThreadInit(ThreadVars *t, void *initdata, void **data)
|
||||
{
|
||||
LogStatsLogThread *aft = SCMalloc(sizeof(LogStatsLogThread));
|
||||
if (unlikely(aft == NULL))
|
||||
return TM_ECODE_FAILED;
|
||||
memset(aft, 0, sizeof(LogStatsLogThread));
|
||||
|
||||
if(initdata == NULL)
|
||||
{
|
||||
SCLogDebug("Error getting context for HTTPLog. \"initdata\" argument NULL");
|
||||
SCFree(aft);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
|
||||
if (aft->buffer == NULL) {
|
||||
SCFree(aft);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
/* Use the Ouptut Context (file pointer and mutex) */
|
||||
aft->statslog_ctx= ((OutputCtx *)initdata)->data;
|
||||
|
||||
*data = (void *)aft;
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
TmEcode LogStatsLogThreadDeinit(ThreadVars *t, void *data)
|
||||
{
|
||||
LogStatsLogThread *aft = (LogStatsLogThread *)data;
|
||||
if (aft == NULL) {
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
MemBufferFree(aft->buffer);
|
||||
/* clear memory */
|
||||
memset(aft, 0, sizeof(LogStatsLogThread));
|
||||
|
||||
SCFree(aft);
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
void LogStatsLogExitPrintStats(ThreadVars *tv, void *data)
|
||||
{
|
||||
LogStatsLogThread *aft = (LogStatsLogThread *)data;
|
||||
if (aft == NULL) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Create a new http log LogFileCtx.
|
||||
* \param conf Pointer to ConfNode containing this loggers configuration.
|
||||
* \return NULL if failure, LogFileCtx* to the file_ctx if succesful
|
||||
* */
|
||||
OutputCtx *LogStatsLogInitCtx(ConfNode *conf)
|
||||
{
|
||||
LogFileCtx *file_ctx = LogFileNewCtx();
|
||||
if (file_ctx == NULL) {
|
||||
SCLogError(SC_ERR_HTTP_LOG_GENERIC, "couldn't create new file_ctx");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME) < 0) {
|
||||
LogFileFreeCtx(file_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LogStatsFileCtx *statslog_ctx = SCMalloc(sizeof(LogStatsFileCtx));
|
||||
if (unlikely(statslog_ctx == NULL)) {
|
||||
LogFileFreeCtx(file_ctx);
|
||||
return NULL;
|
||||
}
|
||||
memset(statslog_ctx, 0x00, sizeof(LogStatsFileCtx));
|
||||
|
||||
statslog_ctx->file_ctx = file_ctx;
|
||||
|
||||
OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
|
||||
if (unlikely(output_ctx == NULL)) {
|
||||
LogFileFreeCtx(file_ctx);
|
||||
SCFree(statslog_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
output_ctx->data = statslog_ctx;
|
||||
output_ctx->DeInit = LogStatsLogDeInitCtx;
|
||||
|
||||
SCLogDebug("STATS log output initialized");
|
||||
|
||||
OutputRegisterFileRotationFlag(&file_ctx->rotation_flag);
|
||||
return output_ctx;
|
||||
}
|
||||
|
||||
static void LogStatsLogDeInitCtx(OutputCtx *output_ctx)
|
||||
{
|
||||
LogStatsFileCtx *statslog_ctx = (LogStatsFileCtx *)output_ctx->data;
|
||||
OutputUnregisterFileRotationFlag(&statslog_ctx->file_ctx->rotation_flag);
|
||||
LogFileFreeCtx(statslog_ctx->file_ctx);
|
||||
SCFree(statslog_ctx);
|
||||
SCFree(output_ctx);
|
||||
}
|
||||
|
||||
void TmModuleLogStatsLogRegister (void)
|
||||
{
|
||||
tmm_modules[TMM_LOGSTATSLOG].name = MODULE_NAME;
|
||||
tmm_modules[TMM_LOGSTATSLOG].ThreadInit = LogStatsLogThreadInit;
|
||||
tmm_modules[TMM_LOGSTATSLOG].ThreadExitPrintStats = LogStatsLogExitPrintStats;
|
||||
tmm_modules[TMM_LOGSTATSLOG].ThreadDeinit = LogStatsLogThreadDeinit;
|
||||
tmm_modules[TMM_LOGSTATSLOG].RegisterTests = NULL;
|
||||
tmm_modules[TMM_LOGSTATSLOG].cap_flags = 0;
|
||||
tmm_modules[TMM_LOGSTATSLOG].flags = TM_FLAG_LOGAPI_TM;
|
||||
|
||||
OutputRegisterStatsModule(MODULE_NAME, "stats", LogStatsLogInitCtx, LogStatsLogger);
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/* Copyright (C) 2014 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Victor Julien <victor@inliniac.net>
|
||||
*/
|
||||
|
||||
#ifndef __LOG_STATS_H__
|
||||
#define __LOG_STATS_H__
|
||||
|
||||
void TmModuleLogStatsLogRegister (void);
|
||||
|
||||
#endif /* __LOG_HTTPLOG_H__ */
|
||||
@ -0,0 +1,234 @@
|
||||
/* Copyright (C) 2014 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Victor Julien <victor@inliniac.net>
|
||||
*
|
||||
* Stats Logger Output registration functions
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "tm-modules.h"
|
||||
#include "output-stats.h"
|
||||
|
||||
typedef struct OutputLoggerThreadStore_ {
|
||||
void *thread_data;
|
||||
struct OutputLoggerThreadStore_ *next;
|
||||
} OutputLoggerThreadStore;
|
||||
|
||||
/** per thread data for this module, contains a list of per thread
|
||||
* data for the packet loggers. */
|
||||
typedef struct OutputLoggerThreadData_ {
|
||||
OutputLoggerThreadStore *store;
|
||||
} OutputLoggerThreadData;
|
||||
|
||||
/* logger instance, a module + a output ctx,
|
||||
* it's perfectly valid that have multiple instances of the same
|
||||
* log module (e.g. http.log) with different output ctx'. */
|
||||
typedef struct OutputStatsLogger_ {
|
||||
StatsLogger LogFunc;
|
||||
OutputCtx *output_ctx;
|
||||
struct OutputStatsLogger_ *next;
|
||||
const char *name;
|
||||
TmmId module_id;
|
||||
} OutputStatsLogger;
|
||||
|
||||
static OutputStatsLogger *list = NULL;
|
||||
|
||||
int OutputRegisterStatsLogger(const char *name, StatsLogger LogFunc, OutputCtx *output_ctx)
|
||||
{
|
||||
int module_id = TmModuleGetIdByName(name);
|
||||
if (module_id < 0)
|
||||
return -1;
|
||||
|
||||
OutputStatsLogger *op = SCMalloc(sizeof(*op));
|
||||
if (op == NULL)
|
||||
return -1;
|
||||
memset(op, 0x00, sizeof(*op));
|
||||
|
||||
op->LogFunc = LogFunc;
|
||||
op->output_ctx = output_ctx;
|
||||
op->name = name;
|
||||
op->module_id = (TmmId) module_id;
|
||||
|
||||
if (list == NULL)
|
||||
list = op;
|
||||
else {
|
||||
OutputStatsLogger *t = list;
|
||||
while (t->next)
|
||||
t = t->next;
|
||||
t->next = op;
|
||||
}
|
||||
|
||||
SCLogDebug("OutputRegisterStatsLogger happy");
|
||||
return 0;
|
||||
}
|
||||
|
||||
TmEcode OutputStatsLog(ThreadVars *tv, void *thread_data, StatsTable *st)
|
||||
{
|
||||
BUG_ON(thread_data == NULL);
|
||||
BUG_ON(list == NULL);
|
||||
|
||||
OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
|
||||
OutputStatsLogger *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);
|
||||
|
||||
logger = list;
|
||||
store = op_thread_data->store;
|
||||
while (logger && store) {
|
||||
BUG_ON(logger->LogFunc == NULL);
|
||||
|
||||
logger->LogFunc(tv, store->thread_data, st);
|
||||
|
||||
logger = logger->next;
|
||||
store = store->next;
|
||||
|
||||
BUG_ON(logger == NULL && store != NULL);
|
||||
BUG_ON(logger != NULL && store == NULL);
|
||||
}
|
||||
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
/** \brief thread init for the tx logger
|
||||
* This will run the thread init functions for the individual registered
|
||||
* loggers */
|
||||
static TmEcode OutputStatsLogThreadInit(ThreadVars *tv, void *initdata, void **data)
|
||||
{
|
||||
OutputLoggerThreadData *td = SCMalloc(sizeof(*td));
|
||||
if (td == NULL)
|
||||
return TM_ECODE_FAILED;
|
||||
memset(td, 0x00, sizeof(*td));
|
||||
|
||||
*data = (void *)td;
|
||||
|
||||
SCLogDebug("OutputStatsLogThreadInit happy (*data %p)", *data);
|
||||
|
||||
OutputStatsLogger *logger = list;
|
||||
while (logger) {
|
||||
TmModule *tm_module = TmModuleGetByName((char *)logger->name);
|
||||
if (tm_module == NULL) {
|
||||
SCLogError(SC_ERR_INVALID_ARGUMENT,
|
||||
"TmModuleGetByName for %s failed", logger->name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (tm_module->ThreadInit) {
|
||||
void *retptr = NULL;
|
||||
if (tm_module->ThreadInit(tv, (void *)logger->output_ctx, &retptr) == TM_ECODE_OK) {
|
||||
OutputLoggerThreadStore *ts = SCMalloc(sizeof(*ts));
|
||||
/* todo */ BUG_ON(ts == NULL);
|
||||
memset(ts, 0x00, sizeof(*ts));
|
||||
|
||||
/* store thread handle */
|
||||
ts->thread_data = retptr;
|
||||
|
||||
if (td->store == NULL) {
|
||||
td->store = ts;
|
||||
} else {
|
||||
OutputLoggerThreadStore *tmp = td->store;
|
||||
while (tmp->next != NULL)
|
||||
tmp = tmp->next;
|
||||
tmp->next = ts;
|
||||
}
|
||||
|
||||
SCLogDebug("%s is now set up", logger->name);
|
||||
}
|
||||
}
|
||||
|
||||
logger = logger->next;
|
||||
}
|
||||
|
||||
SCLogDebug("OutputStatsLogThreadInit happy (*data %p)", *data);
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
static TmEcode OutputStatsLogThreadDeinit(ThreadVars *tv, void *thread_data)
|
||||
{
|
||||
OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
|
||||
OutputLoggerThreadStore *store = op_thread_data->store;
|
||||
OutputStatsLogger *logger = list;
|
||||
|
||||
while (logger && store) {
|
||||
TmModule *tm_module = TmModuleGetByName((char *)logger->name);
|
||||
if (tm_module == NULL) {
|
||||
SCLogError(SC_ERR_INVALID_ARGUMENT,
|
||||
"TmModuleGetByName for %s failed", logger->name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (tm_module->ThreadDeinit) {
|
||||
tm_module->ThreadDeinit(tv, store->thread_data);
|
||||
}
|
||||
|
||||
OutputLoggerThreadStore *next_store = store->next;
|
||||
SCFree(store);
|
||||
store = next_store;
|
||||
logger = logger->next;
|
||||
}
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
static void OutputStatsLogExitPrintStats(ThreadVars *tv, void *thread_data)
|
||||
{
|
||||
OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
|
||||
OutputLoggerThreadStore *store = op_thread_data->store;
|
||||
OutputStatsLogger *logger = list;
|
||||
|
||||
while (logger && store) {
|
||||
TmModule *tm_module = TmModuleGetByName((char *)logger->name);
|
||||
if (tm_module == NULL) {
|
||||
SCLogError(SC_ERR_INVALID_ARGUMENT,
|
||||
"TmModuleGetByName for %s failed", logger->name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (tm_module->ThreadExitPrintStats) {
|
||||
tm_module->ThreadExitPrintStats(tv, store->thread_data);
|
||||
}
|
||||
|
||||
logger = logger->next;
|
||||
store = store->next;
|
||||
}
|
||||
}
|
||||
|
||||
void TmModuleStatsLoggerRegister (void)
|
||||
{
|
||||
tmm_modules[TMM_STATSLOGGER].name = "__stats_logger__";
|
||||
tmm_modules[TMM_STATSLOGGER].ThreadInit = OutputStatsLogThreadInit;
|
||||
//tmm_modules[TMM_STATSLOGGER].Func = OutputStatsLog;
|
||||
tmm_modules[TMM_STATSLOGGER].ThreadExitPrintStats = OutputStatsLogExitPrintStats;
|
||||
tmm_modules[TMM_STATSLOGGER].ThreadDeinit = OutputStatsLogThreadDeinit;
|
||||
tmm_modules[TMM_STATSLOGGER].cap_flags = 0;
|
||||
}
|
||||
|
||||
void OutputStatsShutdown(void)
|
||||
{
|
||||
OutputStatsLogger *logger = list;
|
||||
while (logger) {
|
||||
OutputStatsLogger *next_logger = logger->next;
|
||||
SCFree(logger);
|
||||
logger = next_logger;
|
||||
}
|
||||
list = NULL;
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/* Copyright (C) 2007-2013 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Victor Julien <victor@inliniac.net>
|
||||
*
|
||||
* Stats Logger Output registration functions
|
||||
*/
|
||||
|
||||
#ifndef __OUTPUT_STATS_H__
|
||||
#define __OUTPUT_STATS_H__
|
||||
|
||||
typedef struct StatsRecord_ {
|
||||
const char *name;
|
||||
const char *tm_name;
|
||||
uint64_t value; /**< total value */
|
||||
uint64_t pvalue; /**< prev value (may be higher for memuse counters) */
|
||||
} StatsRecord;
|
||||
|
||||
typedef struct StatsTable_ {
|
||||
StatsRecord *stats;
|
||||
uint32_t nstats;
|
||||
time_t start_time;
|
||||
struct timeval ts;
|
||||
} StatsTable;
|
||||
|
||||
TmEcode OutputStatsLog(ThreadVars *tv, void *thread_data, StatsTable *st);
|
||||
|
||||
typedef int (*StatsLogger)(ThreadVars *, void *thread_data, const StatsTable *);
|
||||
|
||||
int OutputRegisterStatsLogger(const char *name, StatsLogger LogFunc, OutputCtx *);
|
||||
|
||||
void TmModuleStatsLoggerRegister (void);
|
||||
|
||||
void OutputStatsShutdown(void);
|
||||
|
||||
#endif /* __OUTPUT_STATS_H__ */
|
||||
Loading…
Reference in New Issue