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
55TmEcode LogStatsLogThreadInit(ThreadVars *, const void *, void **);
57static void LogStatsLogDeInitCtx(OutputCtx *);
58
59typedef struct LogStatsFileCtx_ {
61 uint32_t flags; /** Store mode */
63
68
69static 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
165TmEcode LogStatsLogThreadInit(ThreadVars *t, const void *initdata, void **data)
166{
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{
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 * */
210static OutputInitResult LogStatsLogInitCtx(SCConfNode *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 = SCConfNodeLookupChildValue(conf, "totals");
234 const char *threads = SCConfNodeLookupChildValue(conf, "threads");
235 const char *nulls = SCConfNodeLookupChildValue(conf, "null-values");
236 SCLogDebug("totals %s threads %s", totals, threads);
237
238 if ((totals != NULL && SCConfValIsFalse(totals)) &&
239 (threads != NULL && SCConfValIsFalse(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 && SCConfValIsFalse(totals)) {
247 statslog_ctx->flags &= ~LOG_STATS_TOTALS;
248 }
249 if (threads != NULL && SCConfValIsTrue(threads)) {
250 statslog_ctx->flags |= LOG_STATS_THREADS;
251 }
252 if (nulls != NULL && SCConfValIsTrue(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
277static 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}
uint8_t len
int SCConfValIsTrue(const char *val)
Check if a value is true.
Definition conf.c:551
const char * SCConfNodeLookupChildValue(const SCConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition conf.c:824
int SCConfValIsFalse(const char *val)
Check if a value is false.
Definition conf.c:576
ThreadVars * tv
#define LOG_STATS_THREADS
Definition log-stats.c:52
#define MODULE_NAME
Definition log-stats.c:48
TmEcode LogStatsLogThreadInit(ThreadVars *, const void *, void **)
Definition log-stats.c:165
#define LOG_STATS_NULLS
Definition log-stats.c:53
#define OUTPUT_BUFFER_SIZE
Definition log-stats.c:49
#define LOG_STATS_TOTALS
Definition log-stats.c:51
#define DEFAULT_LOG_FILENAME
Definition log-stats.c:47
struct LogStatsLogThread_ LogStatsLogThread
TmEcode LogStatsLogThreadDeinit(ThreadVars *, void *)
Definition log-stats.c:191
struct LogStatsFileCtx_ LogStatsFileCtx
void LogStatsLogRegister(void)
Definition log-stats.c:285
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:570
int(* Write)(const char *buffer, int buffer_len, struct LogFileCtx_ *fp)
uint32_t flags
Definition log-stats.c:61
LogFileCtx * file_ctx
Definition log-stats.c:60
MemBuffer * buffer
Definition log-stats.c:66
LogStatsFileCtx * statslog_ctx
Definition log-stats.c:65
void * data
Definition tm-modules.h:91
void(* DeInit)(struct OutputCtx_ *)
Definition tm-modules.h:94
OutputCtx * ctx
Definition output.h:47
const char * name
const char * tm_name
int64_t value
time_t start_time
StatsRecord * stats
StatsRecord * tstats
uint32_t nstats
uint32_t ntstats
Per thread variable structure.
Definition threadvars.h:58
@ LOGGER_STATS
@ TM_ECODE_FAILED
@ TM_ECODE_OK
void MemBufferWriteString(MemBuffer *dst, const char *fmt,...)
int MemBufferExpand(MemBuffer **buffer, uint32_t expand_by)
expand membuffer by size of 'expand_by'
Definition util-buffer.c:60
MemBuffer * MemBufferCreateNew(uint32_t size)
Definition util-buffer.c:32
void MemBufferFree(MemBuffer *buffer)
Definition util-buffer.c:86
#define MEMBUFFER_SIZE(mem_buffer)
Get the MemBuffers current size.
Definition util-buffer.h:61
#define MEMBUFFER_BUFFER(mem_buffer)
Get the MemBuffers underlying buffer.
Definition util-buffer.h:51
#define MEMBUFFER_OFFSET(mem_buffer)
Get the MemBuffers current offset.
Definition util-buffer.h:56
#define SCEnter(...)
Definition util-debug.h:277
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnInt(x)
Definition util-debug.h:281
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
int LogFileFreeCtx(LogFileCtx *lf_ctx)
LogFileFreeCtx() Destroy a LogFileCtx (Close the file and free memory)
LogFileCtx * LogFileNewCtx(void)
LogFileNewCtx() Get a new LogFileCtx.
int SCConfLogOpenGeneric(SCConfNode *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
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define unlikely(expr)
uint64_t offset
struct tm * SCLocalTime(time_t timep, struct tm *result)
Definition util-time.c:267