Skip to content

Commit 26a0286

Browse files
PettitWesleyedsiper
authored andcommitted
readme: add 'developer guide'/library reference with code examples
Signed-off-by: Wesley Pettit <[email protected]>
1 parent 83b814d commit 26a0286

File tree

2 files changed

+339
-5
lines changed

2 files changed

+339
-5
lines changed

CONTRIBUTING.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
We build Open Source software and we invite everyone to join us and contribute. So if you are interested into participate, please refer to the guidelines below.
44

5+
## Developer Guide
6+
7+
[Developer Guide with code examples](DEVELOPER_GUIDE.md).
8+
59
## GIT Repositories
610

711
All code changes and submissions happens on [Github](http://github.com), that means that to start contributing you should clone the target repository, perform local changes and then do a Pull Request. For more details about the workflow we suggest you check the following documents:
@@ -75,7 +79,7 @@ the proper way is to perform the variable definitions on top:
7579
int flb_something(int a, int b)
7680
{
7781
int ret;
78-
82+
7983
if (a > 10) {
8084
return 1;
8185
}
@@ -146,24 +150,24 @@ When you commit your local changes in your repository (before to push to Github)
146150

147151
Common components prefix are:
148152

149-
- utils:
153+
- utils:
150154
- pack:
151155
- sds:
152156
- http_client:
153157

154-
As you can see prefixes are basically the file name of the source code file under [src](https://github.com/fluent/fluent-bit/tree/master/src) directory without the file prefix <u>flb_</u>.
158+
As you can see prefixes are basically the file name of the source code file under [src](https://github.com/fluent/fluent-bit/tree/master/src) directory without the file prefix <u>flb_</u>.
155159

156160
When committing changes to code that's related to some plugins, the commit subject must be prefixed with the name of the plugin being changed, e.g:
157161

158162
- in_stdin:
159-
- out_http:
163+
- out_http:
160164
- out_kafka:
161165

162166
please refer to the [plugins](https://github.com/fluent/fluent-bit/tree/master/plugins) directory as a reference
163167

164168
- One single commit **must not** include changes to files that are different from the component specified in the subject, e.g: If you are extending flb_utils.c file, the git patch should not touch any other file than flb_utils.c or flb_utils.h.
165169

166-
- One single commit **must not** include multiple prefixes to specify different areas being touched.
170+
- One single commit **must not** include multiple prefixes to specify different areas being touched.
167171

168172
- The subject of the commit **must not** be longer than 80 characters.
169173

DEVELOPER_GUIDE.md

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
## Beginners Guide to Contributing to Fluent Bit
2+
3+
Assuming you have some basic knowledge of C, this guide should help you understand how to make code
4+
changes to Fluent Bit.
5+
6+
### Table of Contents
7+
- [Libraries](#libraries)
8+
- [Memory Management](#memory-management)
9+
- [Strings](#strings)
10+
- [HTTP Client](#http-client)
11+
- [Linked Lists](#linked-lists)
12+
- [Message Pack](#message-pack)
13+
- [Plugin API](#plugin-api)
14+
- [Input](#input)
15+
- [Filter](#filter)
16+
- [Output](#output)
17+
- [Testing](#testing)
18+
- [Need more help?](#need-more-help)
19+
20+
### Libraries
21+
22+
Most external libraries are embedded in the project in the [/lib](/lib) folder. To keep its footprint low and make cross-platform builds simple, Fluent Bit attempts keep its dependency graph small.
23+
24+
The external library you are mostly likely to interact with is [msgpack](https://github.com/msgpack/msgpack-c).
25+
26+
For crypto, Fluent Bit uses [mbedtls](https://github.com/ARMmbed/mbedtls).
27+
28+
#### Memory Management
29+
30+
When you write Fluent Bit code, you will use Fluent Bit's versions of the standard C functions for working with memory:
31+
- [`flb_alloc()`](include/fluent-bit/flb_mem.h) - equivalent to `malloc`, allocates memory.
32+
- [`flb_calloc()`](include/fluent-bit/flb_mem.h) - equivalent to `calloc`, allocates memory and initializes it to zero.
33+
- [`flb_realloc()`](include/fluent-bit/flb_mem.h) - equivalent to `realloc`.
34+
- [`flb_free()`](include/fluent-bit/flb_mem.h) - equivalent to `free`, releases allocated memory.
35+
36+
Note that many types have a specialized create and destroy function. For example,
37+
[`flb_sds_create()` and `flb_sds_destroy()`](include/fluent-bit/flb_sds.h).
38+
39+
#### Strings
40+
41+
Fluent Bit has a stripped down version of the popular [SDS](https://github.com/antirez/sds) string library. See [flb_sds.h](include/fluent-bit/flb_sds.h) for the API.
42+
43+
In general, you should use SDS strings in any string processing code. SDS strings are fully compatible with any C function that accepts a null-terminated sequence of characters; to understand how they work, see the [explanation on Github](https://github.com/antirez/sds#how-sds-strings-work).
44+
45+
#### HTTP Client
46+
47+
Fluent Bit has its own network connection library. The key types and functions are defined in the following header files:
48+
- [flb_upstream.h](include/fluent-bit/flb_upstream.h)
49+
- [flb_http_client.h](include/fluent-bit/flb_http_client.h)
50+
- [flb_io.h](include/fluent-bit/flb_io.h)
51+
52+
The following code demonstrates making an HTTP request in Fluent Bit:
53+
54+
```c
55+
#include <fluent-bit/flb_upstream.h>
56+
#include <fluent-bit/flb_io.h>
57+
#include <fluent-bit/flb_http_client.h>
58+
#include <fluent-bit/flb_info.h>
59+
#include <fluent-bit/flb_config.h>
60+
61+
#define HOST "127.0.0.1"
62+
#define PORT 80
63+
64+
static flb_sds_t make_request(struct flb_config *config)
65+
{
66+
struct flb_upstream *upstream;
67+
struct flb_http_client *client;
68+
size_t b_sent;
69+
int ret;
70+
struct flb_upstream_conn *u_conn;
71+
flb_sds_t resp;
72+
73+
upstream = flb_upstream_create(config, HOST, PORT, FLB_IO_TCP, NULL);
74+
if (!upstream) {
75+
flb_error("[example] connection initialization error");
76+
return -1;
77+
}
78+
79+
u_conn = flb_upstream_conn_get(upstream);
80+
if (!u_conn) {
81+
flb_error("[example] connection initialization error");
82+
flb_upstream_destroy(upstream);
83+
return -1;
84+
}
85+
86+
/* Compose HTTP Client request */
87+
client = flb_http_client(u_conn,
88+
FLB_HTTP_GET, metadata_path,
89+
NULL, 0,
90+
FLB_FILTER_AWS_IMDS_V2_HOST, 80,
91+
NULL, 0);
92+
93+
if (!client) {
94+
flb_error("[example] count not create http client");
95+
flb_upstream_conn_release(u_conn);
96+
flb_upstream_destroy(upstream);
97+
return -1;
98+
}
99+
100+
if (ret != 0 || client->resp.status != 200) {
101+
if (client->resp.payload_size > 0) {
102+
flb_debug("[example] Request failed and returned: \n%s",
103+
client->resp.payload);
104+
}
105+
flb_http_client_destroy(client);
106+
flb_upstream_conn_release(u_conn);
107+
flb_upstream_destroy(upstream);
108+
return -1;
109+
}
110+
111+
data = flb_sds_create_len(client->resp.payload,
112+
client->resp.payload_size);
113+
114+
flb_http_client_destroy(client);
115+
flb_upstream_conn_release(u_conn);
116+
flb_upstream_destroy(upstream);
117+
118+
return resp;
119+
}
120+
```
121+
122+
An `flb_upstream` structure represents a host/endpoint that you want to call. Normally, you'd store this structure somewhere so that it can be re-used. An `flb_upstream_conn` represents a connection to that host for a single HTTP request. The connection structure should not be used for more than one request.
123+
124+
#### Linked Lists
125+
126+
Fluent Bit contains a library for constructing linked lists- [mk_list](lib/monkey/include/monkey/mk_core/mk_list.h). The type stores data as a circular linked list.
127+
128+
The [`mk_list.h`](lib/monkey/include/monkey/mk_core/mk_list.h) header file contains several macros and functions for use with the lists. The example below shows how to create a list, iterate through it, and delete an element.
129+
130+
```c
131+
#include <monkey/mk_core/mk_list.h>
132+
#include <fluent-bit/flb_info.h>
133+
134+
struct item {
135+
char some_data;
136+
137+
struct mk_list _head;
138+
};
139+
140+
static int example()
141+
{
142+
struct mk_list *tmp;
143+
struct mk_list *head;
144+
struct mk_list items;
145+
int i;
146+
char characters[] = "abcdefghijk";
147+
struct item *an_item;
148+
149+
/* construct a list */
150+
mk_list_init(&items);
151+
152+
for (i = 0; i < strlen(characters); i++) {
153+
an_item = flb_malloc(sizeof(struct item));
154+
if (!an_item) {
155+
flb_errno();
156+
return -1;
157+
}
158+
an_item->some_data = characters[i];
159+
160+
mk_list_add(&an_item->_head, &items);
161+
}
162+
163+
/* iterate through the list */
164+
flb_info("Iterating through list");
165+
mk_list_foreach_safe(head, tmp, &items) {
166+
an_item = mk_list_entry(head, struct item, _head);
167+
flb_info("list item data value: %c", an_item->some_data);
168+
}
169+
170+
/* remove an item */
171+
mk_list_foreach_safe(head, tmp, &items) {
172+
an_item = mk_list_entry(head, struct item, _head);
173+
if (an_item->some_data == 'b') {
174+
mk_list_del(&an_item->_head);
175+
flb_free(an_item);
176+
}
177+
}
178+
}
179+
```
180+
181+
#### Message Pack
182+
183+
Fluent Bit uses [msgpack](https://msgpack.org/index.html) to internally store data. If you write code for Fluent Bit, it is almost certain that you will interact with msgpack.
184+
185+
Fluent Bit embeds the [msgpack-c](https://github.com/msgpack/msgpack-c) library. The example below shows manipulating message pack to add a new key-value pair to a record. In Fluent Bit, the [filter_record_modifier](plugins/filter_record_modifier) plugin adds or deletes keys from records. See its code for more.
186+
187+
```
188+
#define A_NEW_KEY "key"
189+
#define A_NEW_KEY_LEN 3
190+
#define A_NEW_VALUE "value"
191+
#define A_NEW_VALUE_LEN 5
192+
193+
static int cb_filter(const void *data, size_t bytes,
194+
const char *tag, int tag_len,
195+
void **out_buf, size_t *out_size,
196+
struct flb_filter_instance *f_ins,
197+
void *context,
198+
struct flb_config *config)
199+
{
200+
(void) f_ins;
201+
(void) config;
202+
size_t off = 0;
203+
int i = 0;
204+
int ret;
205+
struct flb_time tm;
206+
int total_records;
207+
int new_keys = 1;
208+
msgpack_sbuffer tmp_sbuf;
209+
msgpack_packer tmp_pck;
210+
msgpack_unpacked result;
211+
msgpack_object *obj;
212+
msgpack_object_kv *kv;
213+
214+
/* Create temporary msgpack buffer */
215+
msgpack_sbuffer_init(&tmp_sbuf);
216+
msgpack_packer_init(&tmp_pck, &tmp_sbuf, msgpack_sbuffer_write);
217+
218+
/* Iterate over each item */
219+
msgpack_unpacked_init(&result);
220+
while (msgpack_unpack_next(&result, data, bytes, &off) == MSGPACK_UNPACK_SUCCESS) {
221+
/*
222+
* Each record is a msgpack array [timestamp, map] of the
223+
* timestamp and record map. We 'unpack' each record, and then re-pack
224+
* it with the new fields added.
225+
*/
226+
227+
if (result.data.type != MSGPACK_OBJECT_ARRAY) {
228+
continue;
229+
}
230+
231+
/* unpack the array of [timestamp, map] */
232+
flb_time_pop_from_msgpack(&tm, &result, &obj);
233+
234+
/* obj should now be the record map */
235+
if (obj->type != MSGPACK_OBJECT_MAP) {
236+
continue;
237+
}
238+
239+
/* re-pack the array into a new buffer */
240+
msgpack_pack_array(&tmp_pck, 2);
241+
flb_time_append_to_msgpack(&tm, &tmp_pck, 0);
242+
243+
/* new record map size is old size + the new keys we will add */
244+
total_records = obj->via.map.size + new_keys;
245+
msgpack_pack_map(&tmp_pck, total_records);
246+
247+
/* iterate through the old record map and add it to the new buffer */
248+
kv = obj->via.map.ptr;
249+
for(i=0; i < obj->via.map.size; i++) {
250+
msgpack_pack_object(&tmp_pck, (kv+i)->key);
251+
msgpack_pack_object(&tmp_pck, (kv+i)->val);
252+
}
253+
254+
/* append new keys */
255+
msgpack_pack_str(&tmp_pck, A_NEW_KEY_LEN);
256+
msgpack_pack_str_body(&tmp_pck, A_NEW_KEY, A_NEW_KEY_LEN);
257+
msgpack_pack_str(&tmp_pck, A_NEW_VALUE_LEN);
258+
msgpack_pack_str_body(&tmp_pck, A_NEW_VALUE, A_NEW_VALUE_LEN);
259+
260+
}
261+
msgpack_unpacked_destroy(&result);
262+
263+
/* link new buffers */
264+
*out_buf = tmp_sbuf.data;
265+
*out_size = tmp_sbuf.size;
266+
return FLB_FILTER_MODIFIED;
267+
```
268+
269+
Please also check out the message pack examples on the [msgpack-c GitHub repo](https://github.com/msgpack/msgpack-c).
270+
271+
### Plugin API
272+
273+
Each plugin is a shared object which is [loaded into Fluent Bit](https://github.com/fluent/fluent-bit/blob/1.3/src/flb_plugin.c#L70) using dlopen and dlsym.
274+
275+
#### Input
276+
277+
The input plugin structure is defined in [flb_input.h](https://github.com/fluent/fluent-bit/blob/master/include/fluent-bit/flb_input.h#L62). There are a number of functions which a plugin can implement, most only implement `cb_init`, `cb_collect`, and `cb_exit`.
278+
279+
The [`"dummy"` input plugin](plugins/in_dummy) very simple and is an excellent example to review to understand more.
280+
281+
#### Filter
282+
283+
The structure for filter plugins is defined in [flb_filter.h](https://github.com/fluent/fluent-bit/blob/master/include/fluent-bit/flb_filter.h#L44). Each plugin must implement `cb_init`, `cb_filter`, and `cb_exit`.
284+
285+
The [filter_record_modifier](plugins/filter_record_modifier) is a good example of a filter plugin.
286+
287+
Note that filter plugins can not asynchronously make HTTP requests. If your plugin needs to make a request, add the following code when you initialize your `flb_upstream`:
288+
289+
```c
290+
/* Remove async flag from upstream */
291+
upstream->flags &= ~(FLB_IO_ASYNC);
292+
```
293+
294+
#### Output
295+
296+
Output plugins are defined in [flb_output.h](https://github.com/fluent/fluent-bit/blob/master/include/fluent-bit/flb_output.h#L57). Each plugin must implement `cb_init`, `cb_flush`, and `cb_exit`.
297+
298+
The [stdout plugin](plugins/out_stdout) is very simple; review its code to understand how output plugins work.
299+
300+
### Testing
301+
302+
During development, you can build Fluent Bit as follows:
303+
304+
```
305+
cd build
306+
cmake -DFLB_DEV=On ../
307+
make
308+
```
309+
Note that Fluent Bit uses Cmake 3 and on some systems you may need to invoke it as `cmake3`.
310+
311+
To enable the unit tests run:
312+
```
313+
cmake -DFLB_DEV=On -DFLB_TESTS_RUNTIME=On -DFLB_TESTS_INTERNAL=On ../
314+
make
315+
```
316+
317+
Internal tests are for the internal libraries of Fluent Bit. Runtime tests are for the plugins.
318+
319+
You can run the unit tests with `make test`, however, this is inconvenient in practice. Each test file will create an executable in the `build/bin` directory which you can run directly. For example, if you want to run the SDS tests, you can invoke them as follows:
320+
321+
```
322+
$ ./bin/flb-it-sds
323+
Test sds_usage... [ OK ]
324+
Test sds_printf... [ OK ]
325+
SUCCESS: All unit tests have passed.
326+
```
327+
328+
### Need more help?
329+
330+
The best way to learn how Fluent Bit code works is to read it. If you need help understanding the code, reach out to the community, or open a PR with changes that are a work in progress.

0 commit comments

Comments
 (0)