suricata
log-stats.c
Go to the documentation of this file.
1 /* Copyright (C) 2014-2024 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Victor Julien <victor@inliniac.net>
22  *
23  */
24 
25 #include "suricata-common.h"
26 #include "detect.h"
27 #include "pkt-var.h"
28 #include "conf.h"
29 
30 #include "threads.h"
31 #include "threadvars.h"
32 #include "tm-threads.h"
33 
34 #include "util-print.h"
35 #include "util-unittest.h"
36 
37 #include "util-debug.h"
38 
39 #include "output.h"
40 #include "log-stats.h"
41 #include "util-privs.h"
42 #include "util-buffer.h"
43 
44 #include "util-logopenfile.h"
45 #include "util-time.h"
46 
47 #define DEFAULT_LOG_FILENAME "stats.log"
48 #define MODULE_NAME "LogStatsLog"
49 #define OUTPUT_BUFFER_SIZE 16384
50 
51 #define LOG_STATS_TOTALS (1<<0)
52 #define LOG_STATS_THREADS (1<<1)
53 #define LOG_STATS_NULLS (1<<2)
54 
55 TmEcode LogStatsLogThreadInit(ThreadVars *, const void *, void **);
57 static void LogStatsLogDeInitCtx(OutputCtx *);
58 
59 typedef struct LogStatsFileCtx_ {
61  uint32_t flags; /** Store mode */
63 
64 typedef struct LogStatsLogThread_ {
68 
69 static int LogStatsLogger(ThreadVars *tv, void *thread_data, const StatsTable *st)
70 {
71  SCEnter();
72  LogStatsLogThread *aft = (LogStatsLogThread *)thread_data;
73 
74  struct timeval tval;
75  struct tm *tms;
76 
77  gettimeofday(&tval, NULL);
78  struct tm local_tm;
79  tms = SCLocalTime(tval.tv_sec, &local_tm);
80 
81  /* Calculate the Engine uptime */
82  double up_time_d = difftime(tval.tv_sec, st->start_time);
83  int up_time = (int)up_time_d; // ignoring risk of overflow here
84  int sec = up_time % 60; // Seconds in a minute
85  int in_min = up_time / 60;
86  int min = in_min % 60; // Minutes in a hour
87  int in_hours = in_min / 60;
88  int hours = in_hours % 24; // Hours in a day
89  int days = in_hours / 24;
90 
91  MemBufferWriteString(aft->buffer, "----------------------------------------------"
92  "-----------------------------------------------------\n");
93  MemBufferWriteString(aft->buffer, "Date: %" PRId32 "/%" PRId32 "/%04d -- "
94  "%02d:%02d:%02d (uptime: %"PRId32"d, %02dh %02dm %02ds)\n",
95  tms->tm_mon + 1, tms->tm_mday, tms->tm_year + 1900, tms->tm_hour,
96  tms->tm_min, tms->tm_sec, days, hours, min, sec);
97  MemBufferWriteString(aft->buffer, "----------------------------------------------"
98  "-----------------------------------------------------\n");
99  MemBufferWriteString(aft->buffer, "%-60s | %-25s | %-s\n", "Counter", "TM Name", "Value");
100  MemBufferWriteString(aft->buffer, "----------------------------------------------"
101  "-----------------------------------------------------\n");
102 
103  /* global stats */
104  uint32_t u = 0;
105  if (aft->statslog_ctx->flags & LOG_STATS_TOTALS) {
106  for (u = 0; u < st->nstats; u++) {
107  if (st->stats[u].name == NULL)
108  continue;
109 
110  if (!(aft->statslog_ctx->flags & LOG_STATS_NULLS) && st->stats[u].value == 0)
111  continue;
112 
113  char line[256];
114  size_t len = snprintf(line, sizeof(line), "%-60s | %-25s | %-" PRIu64 "\n",
115  st->stats[u].name, st->stats[u].tm_name, st->stats[u].value);
116 
117  /* since we can have many threads, the buffer might not be big enough.
118  * Expand if necessary. */
119  if (MEMBUFFER_OFFSET(aft->buffer) + len >= MEMBUFFER_SIZE(aft->buffer)) {
121  }
122 
123  MemBufferWriteString(aft->buffer, "%s", line);
124  }
125  }
126 
127  /* per thread stats */
128  if (st->tstats != NULL && aft->statslog_ctx->flags & LOG_STATS_THREADS) {
129  /* for each thread (store) */
130  uint32_t x;
131  for (x = 0; x < st->ntstats; x++) {
132  uint32_t offset = x * st->nstats;
133 
134  /* for each counter */
135  for (u = offset; u < (offset + st->nstats); u++) {
136  if (st->tstats[u].name == NULL)
137  continue;
138 
139  if (!(aft->statslog_ctx->flags & LOG_STATS_NULLS) && st->tstats[u].value == 0)
140  continue;
141 
142  char line[256];
143  size_t len = snprintf(line, sizeof(line), "%-45s | %-25s | %-" PRIi64 "\n",
144  st->tstats[u].name, st->tstats[u].tm_name, st->tstats[u].value);
145 
146  /* since we can have many threads, the buffer might not be big enough.
147  * Expand if necessary. */
148  if (MEMBUFFER_OFFSET(aft->buffer) + len >= MEMBUFFER_SIZE(aft->buffer)) {
150  }
151 
152  MemBufferWriteString(aft->buffer, "%s", line);
153  }
154  }
155  }
156 
157  aft->statslog_ctx->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer),
159 
160  MemBufferReset(aft->buffer);
161 
162  SCReturnInt(0);
163 }
164 
165 TmEcode LogStatsLogThreadInit(ThreadVars *t, const void *initdata, void **data)
166 {
167  LogStatsLogThread *aft = SCCalloc(1, sizeof(LogStatsLogThread));
168  if (unlikely(aft == NULL))
169  return TM_ECODE_FAILED;
170 
171  if(initdata == NULL)
172  {
173  SCLogDebug("Error getting context for LogStats. \"initdata\" argument NULL");
174  SCFree(aft);
175  return TM_ECODE_FAILED;
176  }
177 
179  if (aft->buffer == NULL) {
180  SCFree(aft);
181  return TM_ECODE_FAILED;
182  }
183 
184  /* Use the Output Context (file pointer and mutex) */
185  aft->statslog_ctx= ((OutputCtx *)initdata)->data;
186 
187  *data = (void *)aft;
188  return TM_ECODE_OK;
189 }
190 
192 {
193  LogStatsLogThread *aft = (LogStatsLogThread *)data;
194  if (aft == NULL) {
195  return TM_ECODE_OK;
196  }
197 
198  MemBufferFree(aft->buffer);
199  /* clear memory */
200  memset(aft, 0, sizeof(LogStatsLogThread));
201 
202  SCFree(aft);
203  return TM_ECODE_OK;
204 }
205 
206 /** \brief Create a new http log LogFileCtx.
207  * \param conf Pointer to ConfNode containing this loggers configuration.
208  * \return NULL if failure, LogFileCtx* to the file_ctx if successful
209  * */
210 static OutputInitResult LogStatsLogInitCtx(ConfNode *conf)
211 {
212  OutputInitResult result = { NULL, false };
213  LogFileCtx *file_ctx = LogFileNewCtx();
214  if (file_ctx == NULL) {
215  SCLogError("couldn't create new file_ctx");
216  return result;
217  }
218 
219  if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
220  LogFileFreeCtx(file_ctx);
221  return result;
222  }
223 
224  LogStatsFileCtx *statslog_ctx = SCCalloc(1, sizeof(LogStatsFileCtx));
225  if (unlikely(statslog_ctx == NULL)) {
226  LogFileFreeCtx(file_ctx);
227  return result;
228  }
229 
230  statslog_ctx->flags = LOG_STATS_TOTALS;
231 
232  if (conf != NULL) {
233  const char *totals = ConfNodeLookupChildValue(conf, "totals");
234  const char *threads = ConfNodeLookupChildValue(conf, "threads");
235  const char *nulls = ConfNodeLookupChildValue(conf, "null-values");
236  SCLogDebug("totals %s threads %s", totals, threads);
237 
238  if ((totals != NULL && ConfValIsFalse(totals)) &&
239  (threads != NULL && ConfValIsFalse(threads))) {
240  LogFileFreeCtx(file_ctx);
241  SCFree(statslog_ctx);
242  SCLogError("Cannot disable both totals and threads in stats logging");
243  return result;
244  }
245 
246  if (totals != NULL && ConfValIsFalse(totals)) {
247  statslog_ctx->flags &= ~LOG_STATS_TOTALS;
248  }
249  if (threads != NULL && ConfValIsTrue(threads)) {
250  statslog_ctx->flags |= LOG_STATS_THREADS;
251  }
252  if (nulls != NULL && ConfValIsTrue(nulls)) {
253  statslog_ctx->flags |= LOG_STATS_NULLS;
254  }
255  SCLogDebug("statslog_ctx->flags %08x", statslog_ctx->flags);
256  }
257 
258  statslog_ctx->file_ctx = file_ctx;
259 
260  OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
261  if (unlikely(output_ctx == NULL)) {
262  LogFileFreeCtx(file_ctx);
263  SCFree(statslog_ctx);
264  return result;
265  }
266 
267  output_ctx->data = statslog_ctx;
268  output_ctx->DeInit = LogStatsLogDeInitCtx;
269 
270  SCLogDebug("STATS log output initialized");
271 
272  result.ctx = output_ctx;
273  result.ok = true;
274  return result;
275 }
276 
277 static void LogStatsLogDeInitCtx(OutputCtx *output_ctx)
278 {
279  LogStatsFileCtx *statslog_ctx = (LogStatsFileCtx *)output_ctx->data;
280  LogFileFreeCtx(statslog_ctx->file_ctx);
281  SCFree(statslog_ctx);
282  SCFree(output_ctx);
283 }
284 
286 {
287  OutputRegisterStatsModule(LOGGER_STATS, MODULE_NAME, "stats", LogStatsLogInitCtx,
289 }
log-stats.h
tm-threads.h
LogStatsFileCtx_::file_ctx
LogFileCtx * file_ctx
Definition: log-stats.c:60
StatsTable_::ntstats
uint32_t ntstats
Definition: output-stats.h:43
OUTPUT_BUFFER_SIZE
#define OUTPUT_BUFFER_SIZE
Definition: log-stats.c:49
len
uint8_t len
Definition: app-layer-dnp3.h:2
LogStatsLogThreadDeinit
TmEcode LogStatsLogThreadDeinit(ThreadVars *, void *)
Definition: log-stats.c:191
offset
uint64_t offset
Definition: util-streaming-buffer.h:0
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
LogFileNewCtx
LogFileCtx * LogFileNewCtx(void)
LogFileNewCtx() Get a new LogFileCtx.
Definition: util-logopenfile.c:659
MemBufferExpand
int MemBufferExpand(MemBuffer **buffer, uint32_t expand_by)
expand membuffer by size of 'expand_by'
Definition: util-buffer.c:60
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
threads.h
LOG_STATS_NULLS
#define LOG_STATS_NULLS
Definition: log-stats.c:53
LogFileCtx_
Definition: util-logopenfile.h:72
LogStatsFileCtx_
Definition: log-stats.c:59
util-privs.h
LogFileCtx_::Write
int(* Write)(const char *buffer, int buffer_len, struct LogFileCtx_ *fp)
Definition: util-logopenfile.h:87
LogStatsLogThread_
Definition: log-stats.c:64
LOGGER_STATS
@ LOGGER_STATS
Definition: suricata-common.h:488
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:81
LogStatsLogThread
struct LogStatsLogThread_ LogStatsLogThread
util-unittest.h
LogStatsLogThreadInit
TmEcode LogStatsLogThreadInit(ThreadVars *, const void *, void **)
Definition: log-stats.c:165
ConfValIsTrue
int ConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:536
OutputCtx_::data
void * data
Definition: tm-modules.h:87
TM_ECODE_OK
@ TM_ECODE_OK
Definition: tm-threads-common.h:80
OutputCtx_
Definition: tm-modules.h:84
SCConfLogOpenGeneric
int SCConfLogOpenGeneric(ConfNode *conf, LogFileCtx *log_ctx, const char *default_filename, int rotate)
open a generic output "log file", which may be a regular file or a socket
Definition: util-logopenfile.c:452
LogStatsLogThread_::buffer
MemBuffer * buffer
Definition: log-stats.c:66
util-debug.h
OutputRegisterStatsModule
void OutputRegisterStatsModule(LoggerId id, const char *name, const char *conf_name, OutputInitFunc InitFunc, StatsLogger StatsLogFunc, ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit)
Register a stats data output module.
Definition: output.c:541
OutputInitResult_::ctx
OutputCtx * ctx
Definition: output.h:47
util-print.h
SCEnter
#define SCEnter(...)
Definition: util-debug.h:271
StatsRecord_::name
const char * name
Definition: output-stats.h:32
detect.h
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:58
pkt-var.h
util-time.h
OutputInitResult_::ok
bool ok
Definition: output.h:48
LogStatsFileCtx
struct LogStatsFileCtx_ LogStatsFileCtx
StatsTable_
Definition: output-stats.h:39
StatsRecord_::value
int64_t value
Definition: output-stats.h:35
SCLocalTime
struct tm * SCLocalTime(time_t timep, struct tm *result)
Definition: util-time.c:267
conf.h
TmEcode
TmEcode
Definition: tm-threads-common.h:79
StatsTable_::start_time
time_t start_time
Definition: output-stats.h:44
MemBuffer_
Definition: util-buffer.h:27
StatsTable_::stats
StatsRecord * stats
Definition: output-stats.h:40
LogStatsLogRegister
void LogStatsLogRegister(void)
Definition: log-stats.c:285
OutputInitResult_
Definition: output.h:46
DEFAULT_LOG_FILENAME
#define DEFAULT_LOG_FILENAME
Definition: log-stats.c:47
suricata-common.h
OutputCtx_::DeInit
void(* DeInit)(struct OutputCtx_ *)
Definition: tm-modules.h:90
LogStatsLogThread_::statslog_ctx
LogStatsFileCtx * statslog_ctx
Definition: log-stats.c:65
MODULE_NAME
#define MODULE_NAME
Definition: log-stats.c:48
MemBufferFree
void MemBufferFree(MemBuffer *buffer)
Definition: util-buffer.c:81
StatsRecord_::tm_name
const char * tm_name
Definition: output-stats.h:34
LogFileFreeCtx
int LogFileFreeCtx(LogFileCtx *lf_ctx)
LogFileFreeCtx() Destroy a LogFileCtx (Close the file and free memory)
Definition: util-logopenfile.c:868
StatsTable_::tstats
StatsRecord * tstats
Definition: output-stats.h:41
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:32
threadvars.h
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:261
SCFree
#define SCFree(p)
Definition: util-mem.h:61
ConfNode_
Definition: conf.h:32
LogStatsFileCtx_::flags
uint32_t flags
Definition: log-stats.c:61
util-logopenfile.h
MEMBUFFER_SIZE
#define MEMBUFFER_SIZE(mem_buffer)
Get the MemBuffers current size.
Definition: util-buffer.h:61
util-buffer.h
LOG_STATS_TOTALS
#define LOG_STATS_TOTALS
Definition: log-stats.c:51
ConfValIsFalse
int ConfValIsFalse(const char *val)
Check if a value is false.
Definition: conf.c:561
MemBufferWriteString
void MemBufferWriteString(MemBuffer *dst, const char *fmt,...)
Definition: util-buffer.c:125
LOG_STATS_THREADS
#define LOG_STATS_THREADS
Definition: log-stats.c:52
MEMBUFFER_BUFFER
#define MEMBUFFER_BUFFER(mem_buffer)
Get the MemBuffers underlying buffer.
Definition: util-buffer.h:51
MEMBUFFER_OFFSET
#define MEMBUFFER_OFFSET(mem_buffer)
Get the MemBuffers current offset.
Definition: util-buffer.h:56
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:275
StatsTable_::nstats
uint32_t nstats
Definition: output-stats.h:42
MemBufferCreateNew
MemBuffer * MemBufferCreateNew(uint32_t size)
Definition: util-buffer.c:32
output.h
ConfNodeLookupChildValue
const char * ConfNodeLookupChildValue(const ConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:809