Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_kafka.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <locale.h>
32#include <libgen.h>
33#include <errno.h>
34
35#include "nilorea/n_log.h"
36#include "nilorea/n_network.h"
37#include "nilorea/n_kafka.h"
38#include "cJSON.h"
39
40#include "rdkafka.h"
41
42#define OK 0
43#define ERROR -1
44#define NB_TEST_EVENTS 10
45
46char *config_file = NULL,
47 *event_string = NULL,
48 *event_file = NULL,
50 *log_prefix = NULL;
51
52int log_level = LOG_ERR, /* default log level */
53 getoptret = 0, /* getopt return value */
56 run = 1;
57
58// help func
59void usage(void) {
60 fprintf(stderr,
61 "Syntax is: ex_kafka -v -c config_file [-s event or -f eventfile] -o event_log_file -V LOGLEVEL\n"
62 " -v version: print version and exit\n"
63 " -c config_file: [required] Kproducer config file\n"
64 " -s : string of the event to send\n"
65 " -f : file containing the event to send\n"
66 " -C : start a consumer (default output received in terminal)"
67 " -P : start a producer and produce event"
68 " -o : optionnal, set a log file instead of default (stderr/stdout)\n"
69 " -p : optionnal, set a log prefix\n"
70 " -V verbosity: specify a log level for console output\n"
71 " Supported: LOG_ EMERG,ALERT,CRIT,ERR,WARNING,NOTICE,INFO,DEBUG\n");
72}
73
74// stop handler
75static void stop(int sig) {
76 (void)sig;
77 run = 0;
78}
79
80int main(int argc, char* argv[]) {
81 /* Signal handler for clean shutdown */
82 signal(SIGINT, stop);
83
84 /* temporary header structure */
85 rd_kafka_headers_t* headers = NULL;
86
87 /* Analysing arguments */
88 while ((getoptret = getopt(argc, argv, "vhCPH:c:s:f:V:o:p:")) != -1) {
89 switch (getoptret) {
90 case 'v':
91 fprintf(stderr, " Version compiled on %s at %s\n", __DATE__, __TIME__);
92 exit(TRUE);
93 case 'h':
94 usage();
95 exit(0);
96 break;
97 case 'C':
98 if (KAFKA_MODE == -1) {
99 KAFKA_MODE = RD_KAFKA_CONSUMER;
100 } else {
101 fprintf(stderr, "-C and -P can not be used at the ame time!");
102 exit(TRUE);
103 }
104 break;
105 case 'P':
106 if (KAFKA_MODE == -1) {
107 KAFKA_MODE = RD_KAFKA_PRODUCER;
108 } else {
109 fprintf(stderr, "-C and -P can not be used at the ame time!");
110 exit(TRUE);
111 }
112 break;
113 case 'H': {
114 char *name = NULL, *val = NULL;
115 ssize_t name_sz = -1;
116
117 name = optarg;
118 val = strchr(name, '=');
119 if (val) {
120 name_sz = val - name;
121 val++; /* past the '=' */
122 }
123
124 if (!headers)
125 headers = rd_kafka_headers_new(16);
126
127 int err = rd_kafka_header_add(headers, name, name_sz, val, -1);
128 if (err) {
129 fprintf(stderr,
130 "%% Failed to add header %s: %s\n",
131 name, rd_kafka_err2str(err));
132 exit(1);
133 }
134 } break;
135 case 'c':
136 config_file = local_strdup(optarg);
137 break;
138 case 's':
139 Malloc(event_string, char, strlen(optarg) + 1);
140 strcpy(event_string, optarg);
141 break;
142 case 'f':
143 event_file = local_strdup(optarg);
144 break;
145 case 'o':
147 break;
148 case 'p':
149 log_prefix = local_strdup(optarg);
150 break;
151 case 'V':
152 if (!strncmp("LOG_NULL", optarg, 8)) {
154 } else if (!strncmp("LOG_NOTICE", optarg, 10)) {
156 } else if (!strncmp("LOG_INFO", optarg, 8)) {
158 } else if (!strncmp("LOG_ERR", optarg, 7)) {
160 } else if (!strncmp("LOG_DEBUG", optarg, 9)) {
162 } else {
163 fprintf(stderr, "%s n'est pas un niveau de log valide.\n", optarg);
164 exit(-1);
165 }
166 break;
167 case '?':
168 if (optopt == 'c' || optopt == 's' || optopt == 'f') {
169 fprintf(stderr, "Option -%c need a parameter\n", optopt);
170 exit(FALSE);
171 }
173 default:
174 usage();
175 exit(-1);
176 break;
177 }
178 }
179
180 if (KAFKA_MODE == -1) {
181 n_log(LOG_ERR, "consumer (-C) or producer (-P) mode is not defined !", log_prefix);
182 exit(1);
183 }
184
185 if (!log_prefix) {
186 log_prefix = strdup("");
187 }
188
190 if (event_log_file) {
191 int log_file_ret = set_log_file(event_log_file);
192 n_log(LOG_DEBUG, "%s log to file: %s , %d , %p", log_prefix, event_log_file, log_file_ret, get_log_file());
193 }
194
195 /* testing parameters */
196 if (!config_file) {
197 n_log(LOG_ERR, "%s parameter config_file needs to be set !", log_prefix);
198 exit(1);
199 }
200
201 if (KAFKA_MODE == RD_KAFKA_PRODUCER) {
202 if (!event_string && !event_file) {
203 n_log(LOG_ERR, "%s one of (event_string|event_file) needs to be set !", log_prefix);
204 exit(1);
205 }
206 }
207
208 if (event_string && event_file) {
209 n_log(LOG_ERR, "%s do not define event_string AND event_file, only one needs to be set !", log_prefix);
210 exit(1);
211 }
212
213 // load kafka config file
214 int exit_code = 0;
216 __n_assert(kafka_handle, n_log(LOG_ERR, "kafka handle is NULL !!"); exit(1));
217
218 n_kafka_start_polling_thread(kafka_handle);
219
220 size_t nb_queued = 0;
221 size_t nb_waiting = 0;
222 size_t nb_error = 0;
223 int poll_status = 1;
224 N_KAFKA_EVENT* event = NULL;
225
226 if (KAFKA_MODE == RD_KAFKA_PRODUCER) {
227 // create a new kafka event from a string or from a file
228 if (event_string) {
229 event = n_kafka_new_event_from_char(event_string, strlen(event_string), kafka_handle->schema_id);
230 }
231 if (event_file) {
232 event = n_kafka_new_event_from_file(event_file, kafka_handle->schema_id);
233 }
234 // set headers if any
235 if (headers && event) {
236 event->rd_kafka_headers = rd_kafka_headers_copy(headers);
237 // clean them if no more needed
238 rd_kafka_headers_destroy(headers);
239 }
240 // produce the event, API is charging itself of destroying it
241 if (n_kafka_produce(kafka_handle, event) == FALSE) {
242 n_log(LOG_ERR, "n_kafka_produce returned an error for event %p", event);
243 } else {
244 n_log(LOG_INFO, "n_kafka_produce returned OK for event %p", event);
245 }
246 }
247
248 // loop on poll
249 do {
250 poll_status = n_kafka_get_status(kafka_handle, &nb_queued, &nb_waiting, &nb_error);
251 n_log(LOG_DEBUG, "polling kafka handle, status: %d, %d in queue, %d waiting for ack, %d on error", poll_status, nb_queued, nb_waiting, nb_error);
252
253 // if we were waiting only for producing elemens we could use a test like this to break out of the loop
254 if (KAFKA_MODE == RD_KAFKA_PRODUCER) {
255 usleep(30000);
256 if (nb_queued == 0 && nb_waiting == 0 && nb_error == 0)
257 break;
258 } else {
259 event = n_kafka_get_event(kafka_handle);
260 if (event) {
261 if (kafka_handle->schema_id != -1)
262 n_log(LOG_INFO, "received event schema id %d string:\n%s", event->schema_id, _str(event->event_string->data + 4));
263 else
264 n_log(LOG_INFO, "received event string:\n%s", event->event_string->data);
265
266 list_foreach(node, event->received_headers) {
267 N_STR* header = (N_STR*)node->ptr;
268 n_log(LOG_INFO, "headers: %s", _nstr(header));
269 }
270 n_kafka_event_destroy(&event);
271 } else {
272 usleep(30000);
273 }
274 }
275 } while (run && poll_status > 0);
276
277 n_log(LOG_INFO, "kafka_handle: %d queued, %d waiting ack, %d on error", nb_queued, nb_waiting, nb_error);
278 if (nb_error > 0 || nb_waiting > 0) {
279 n_log(LOG_ERR, "kafka_handle: %d events are still waiting for ack, and %d are on error !", nb_waiting, nb_error);
280 n_kafka_dump_unprocessed(kafka_handle, "DATAS/kafka/unprocessed");
281 }
282
283 // log unprocessed events
284 list_foreach(node, kafka_handle->received_events) {
285 N_KAFKA_EVENT* unprocessed_event = (N_KAFKA_EVENT*)node->ptr;
286 if (unprocessed_event) {
287 if (kafka_handle->schema_id != -1)
288 n_log(LOG_INFO, "[unprocessed]received event schema id %d string:\n%s", unprocessed_event->schema_id, unprocessed_event->event_string->data + 4);
289 else
290 n_log(LOG_INFO, "[unprocessed]received event string:\n%s", unprocessed_event->event_string->data);
291 }
292 }
293
294 // closing kafka handle
295 n_kafka_delete(kafka_handle);
296
297 exit(exit_code);
298}
static void usage(void)
int main(void)
int getoptret
Definition ex_fluid.c:59
int log_level
Definition ex_fluid.c:60
int PRODUCER_MODE
Definition ex_kafka.c:55
static void stop(int sig)
Definition ex_kafka.c:75
char * log_prefix
Definition ex_kafka.c:50
int KAFKA_MODE
Definition ex_kafka.c:54
char * event_log_file
Definition ex_kafka.c:49
char * event_file
Definition ex_kafka.c:48
char * event_string
Definition ex_kafka.c:47
char * config_file
Definition ex_kafka.c:46
int run
Definition ex_kafka.c:56
#define FALL_THROUGH
set windows if true
Definition n_common.h:72
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:279
#define _str(__PTR)
define true
Definition n_common.h:193
#define _nstr(__PTR)
N_STR or "NULL" string for logging purposes.
Definition n_common.h:199
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper, safe for node removal during iteration.
Definition n_list.h:89
FILE * get_log_file(void)
return the current log_file
Definition n_log.c:198
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:89
#define LOG_DEBUG
debug-level messages
Definition n_log.h:84
#define LOG_ERR
error conditions
Definition n_log.h:76
int set_log_file(char *file)
Set the logging to a file instead of stderr.
Definition n_log.c:168
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition n_log.c:121
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:80
#define LOG_NULL
no log output
Definition n_log.h:46
#define LOG_INFO
informational
Definition n_log.h:82
int schema_id
kafka schema_id
Definition n_kafka.h:81
LIST * received_events
list of received N_KAFKA_EVENT
Definition n_kafka.h:93
N_STR * event_string
string containing the topic id + payload
Definition n_kafka.h:69
int schema_id
kafka schema id in network order
Definition n_kafka.h:115
int n_kafka_dump_unprocessed(N_KAFKA *kafka, char *directory)
dump unprocessed/unset events
Definition n_kafka.c:1441
N_KAFKA_EVENT * n_kafka_get_event(N_KAFKA *kafka)
get a received event from the N_KAFKA kafka handle
Definition n_kafka.c:1515
N_KAFKA * n_kafka_load_config(char *config_file, int mode)
load a kafka configuration from a file
Definition n_kafka.c:463
int n_kafka_get_status(N_KAFKA *kafka, size_t *nb_queued, size_t *nb_waiting, size_t *nb_error)
return the queues status
Definition n_kafka.c:206
int n_kafka_start_polling_thread(N_KAFKA *kafka)
start the polling thread of a kafka handle
Definition n_kafka.c:1310
N_KAFKA_EVENT * n_kafka_new_event_from_char(const char *string, size_t written, int schema_id)
make a new event from a char *string
Definition n_kafka.c:946
int n_kafka_event_destroy(N_KAFKA_EVENT **event)
destroy a kafka event and set it's pointer to NULL
Definition n_kafka.c:1043
int n_kafka_produce(N_KAFKA *kafka, N_KAFKA_EVENT *event)
put an event in the events_to_send list
Definition n_kafka.c:870
N_KAFKA_EVENT * n_kafka_new_event_from_file(char *filename, int schema_id)
make a new event from a N_STR *string
Definition n_kafka.c:999
void n_kafka_delete(N_KAFKA *kafka)
delete a N_KAFKA handle
Definition n_kafka.c:344
structure of a KAFKA consumer or producer handle
Definition n_kafka.h:89
structure of a KAFKA message
Definition n_kafka.h:67
char * data
the string
Definition n_str.h:63
#define local_strdup(__src_)
Do tar(1) matching rules, which ignore a trailing slash?
Definition n_str.h:79
A box including a string and his lenght.
Definition n_str.h:61
Kafka generic produce and consume event header.
Generic log system.
Network Engine.