Coverage Report

Created: 2026-06-20 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fluent-bit/plugins/out_logdna/logdna.c
Line
Count
Source
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*  Fluent Bit
4
 *  ==========
5
 *  Copyright (C) 2015-2026 The Fluent Bit Authors
6
 *
7
 *  Licensed under the Apache License, Version 2.0 (the "License");
8
 *  you may not use this file except in compliance with the License.
9
 *  You may obtain a copy of the License at
10
 *
11
 *      http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 *  Unless required by applicable law or agreed to in writing, software
14
 *  distributed under the License is distributed on an "AS IS" BASIS,
15
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 *  See the License for the specific language governing permissions and
17
 *  limitations under the License.
18
 */
19
20
#include <fluent-bit/flb_output_plugin.h>
21
#include <fluent-bit/flb_mp.h>
22
#include <fluent-bit/flb_pack.h>
23
#include <fluent-bit/flb_env.h>
24
#include <fluent-bit/flb_http_client.h>
25
#include <fluent-bit/flb_log_event_decoder.h>
26
27
#include "logdna.h"
28
29
0
#define LOGDNA_META_KEY "meta"
30
0
#define LOGDNA_LEVEL_KEY "level"
31
0
#define LOGDNA_SEVERITY_KEY "severity"
32
0
#define LOGDNA_FILE_KEY "file"
33
0
#define LOGDNA_APP_KEY "app"
34
35
static inline int primary_key_check(msgpack_object k, char *name, int len)
36
0
{
37
0
    if (k.type != MSGPACK_OBJECT_STR) {
38
0
        return FLB_FALSE;
39
0
    }
40
41
0
    if (k.via.str.size != len) {
42
0
        return FLB_FALSE;
43
0
    }
44
45
0
    if (memcmp(k.via.str.ptr, name, len) == 0) {
46
0
        return FLB_TRUE;
47
0
    }
48
49
0
    return FLB_FALSE;
50
0
}
51
52
/*
53
 * This function looks for the following primary keys and promotes them to
54
 * the top-level line object:
55
 *
56
 * - level or severity
57
 * - file
58
 * - app
59
 * - meta
60
 *
61
 * When line_pck is not NULL, non-primary keys are packed into it for use
62
 * as the "line" body (excluding the promoted keys).
63
 */
64
static int record_append_primary_keys(struct flb_logdna *ctx,
65
                                      msgpack_object *map,
66
                                      msgpack_packer *mp_sbuf,
67
                                      msgpack_packer *line_pck)
68
0
{
69
0
    int i;
70
0
    int c = 0;
71
0
    int is_primary;
72
0
    int line_count = 0;
73
0
    msgpack_object *level = NULL;
74
0
    msgpack_object *file = NULL;
75
0
    msgpack_object *app = NULL;
76
0
    msgpack_object *meta = NULL;
77
0
    msgpack_object k;
78
0
    msgpack_object v;
79
80
0
    if (line_pck) {
81
0
        for (i = 0; i < map->via.map.size; i++) {
82
0
            k = map->via.map.ptr[i].key;
83
0
            if (primary_key_check(k, LOGDNA_META_KEY, sizeof(LOGDNA_META_KEY) - 1) == FLB_TRUE ||
84
0
                primary_key_check(k, LOGDNA_LEVEL_KEY, sizeof(LOGDNA_LEVEL_KEY) - 1) == FLB_TRUE ||
85
0
                primary_key_check(k, LOGDNA_SEVERITY_KEY, sizeof(LOGDNA_SEVERITY_KEY) - 1) == FLB_TRUE ||
86
0
                primary_key_check(k, LOGDNA_FILE_KEY, sizeof(LOGDNA_FILE_KEY) - 1) == FLB_TRUE ||
87
0
                primary_key_check(k, LOGDNA_APP_KEY, sizeof(LOGDNA_APP_KEY) - 1) == FLB_TRUE) {
88
0
                continue;
89
0
            }
90
0
            line_count++;
91
0
        }
92
0
        msgpack_pack_map(line_pck, line_count);
93
0
    }
94
95
0
    for (i = 0; i < map->via.map.size; i++) {
96
0
        k = map->via.map.ptr[i].key;
97
0
        v = map->via.map.ptr[i].val;
98
0
        is_primary = FLB_FALSE;
99
100
        /* Level - optional (both "level" and "severity" are primary) */
101
0
        if (primary_key_check(k, LOGDNA_LEVEL_KEY, sizeof(LOGDNA_LEVEL_KEY) - 1) == FLB_TRUE ||
102
0
            primary_key_check(k, LOGDNA_SEVERITY_KEY, sizeof(LOGDNA_SEVERITY_KEY) - 1) == FLB_TRUE) {
103
0
            is_primary = FLB_TRUE;
104
0
            if (!level) {
105
0
                level = &k;
106
0
                msgpack_pack_str(mp_sbuf, sizeof(LOGDNA_LEVEL_KEY) - 1);
107
0
                msgpack_pack_str_body(mp_sbuf, LOGDNA_LEVEL_KEY, sizeof(LOGDNA_LEVEL_KEY) - 1);
108
0
                msgpack_pack_object(mp_sbuf, v);
109
0
                c++;
110
0
            }
111
0
        }
112
113
        /* Meta - optional */
114
0
        if (primary_key_check(k, LOGDNA_META_KEY, sizeof(LOGDNA_META_KEY) - 1) == FLB_TRUE) {
115
0
            is_primary = FLB_TRUE;
116
0
            if (!meta) {
117
0
                meta = &k;
118
0
                msgpack_pack_str(mp_sbuf, sizeof(LOGDNA_META_KEY) - 1);
119
0
                msgpack_pack_str_body(mp_sbuf, LOGDNA_META_KEY, sizeof(LOGDNA_META_KEY) - 1);
120
0
                msgpack_pack_object(mp_sbuf, v);
121
0
                c++;
122
0
            }
123
0
        }
124
125
        /* File */
126
0
        if (primary_key_check(k, LOGDNA_FILE_KEY, sizeof(LOGDNA_FILE_KEY) - 1) == FLB_TRUE) {
127
0
            is_primary = FLB_TRUE;
128
0
            if (!file) {
129
0
                file = &k;
130
0
                msgpack_pack_str(mp_sbuf, sizeof(LOGDNA_FILE_KEY) - 1);
131
0
                msgpack_pack_str_body(mp_sbuf, LOGDNA_FILE_KEY, sizeof(LOGDNA_FILE_KEY) - 1);
132
0
                msgpack_pack_object(mp_sbuf, v);
133
0
                c++;
134
0
            }
135
0
        }
136
137
        /* App */
138
0
        if (primary_key_check(k, LOGDNA_APP_KEY, sizeof(LOGDNA_APP_KEY) - 1) == FLB_TRUE) {
139
0
            is_primary = FLB_TRUE;
140
0
            if (!app) {
141
0
                app = &k;
142
0
                msgpack_pack_str(mp_sbuf, sizeof(LOGDNA_APP_KEY) - 1);
143
0
                msgpack_pack_str_body(mp_sbuf, LOGDNA_APP_KEY, sizeof(LOGDNA_APP_KEY) - 1);
144
0
                msgpack_pack_object(mp_sbuf, v);
145
0
                c++;
146
0
            }
147
0
        }
148
149
0
        if (line_pck && is_primary == FLB_FALSE) {
150
0
            msgpack_pack_object(line_pck, k);
151
0
            msgpack_pack_object(line_pck, v);
152
0
        }
153
0
    }
154
155
    /* Set the global file name if the record did not provided one */
156
0
    if (!file && ctx->file) {
157
0
        msgpack_pack_str(mp_sbuf, sizeof(LOGDNA_FILE_KEY) - 1);
158
0
        msgpack_pack_str_body(mp_sbuf, LOGDNA_FILE_KEY, sizeof(LOGDNA_FILE_KEY) - 1);
159
0
        msgpack_pack_str(mp_sbuf, flb_sds_len(ctx->file));
160
0
        msgpack_pack_str_body(mp_sbuf, ctx->file, flb_sds_len(ctx->file));
161
0
        c++;
162
0
    }
163
164
    /* If no application name is set, set the default */
165
0
    if (!app) {
166
0
        msgpack_pack_str(mp_sbuf, sizeof(LOGDNA_APP_KEY) - 1);
167
0
        msgpack_pack_str_body(mp_sbuf, LOGDNA_APP_KEY, sizeof(LOGDNA_APP_KEY) - 1);
168
0
        msgpack_pack_str(mp_sbuf, flb_sds_len(ctx->app));
169
0
        msgpack_pack_str_body(mp_sbuf, ctx->app, flb_sds_len(ctx->app));
170
0
        c++;
171
0
    }
172
173
0
    return c;
174
0
}
175
176
static flb_sds_t logdna_compose_payload(struct flb_logdna *ctx,
177
                                        const void *data, size_t bytes,
178
                                        const char *tag, int tag_len,
179
                                        struct flb_config *config)
180
0
{
181
0
    int ret;
182
0
    int len;
183
0
    int total_lines;
184
0
    int array_size = 0;
185
0
    off_t map_off;
186
0
    size_t off;
187
0
    char *line_json;
188
0
    flb_sds_t json;
189
0
    msgpack_packer mp_pck;
190
0
    msgpack_sbuffer mp_sbuf;
191
0
    msgpack_packer mp_line_pck;
192
0
    msgpack_sbuffer mp_line_sbuf;
193
0
    msgpack_unpacked mp_line_result;
194
0
    struct flb_log_event_decoder log_decoder;
195
0
    struct flb_log_event log_event;
196
197
0
    ret = flb_log_event_decoder_init(&log_decoder, (char *) data, bytes);
198
199
0
    if (ret != FLB_EVENT_DECODER_SUCCESS) {
200
0
        flb_plg_error(ctx->ins,
201
0
                      "Log event decoder initialization error : %d", ret);
202
203
0
        return NULL;
204
0
    }
205
206
    /* Count number of records */
207
0
    total_lines = flb_mp_count_log_records(data, bytes);
208
209
    /* Initialize msgpack buffers */
210
0
    msgpack_sbuffer_init(&mp_sbuf);
211
0
    msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
212
213
0
    msgpack_pack_map(&mp_pck, 1);
214
215
0
    msgpack_pack_str(&mp_pck, 5);
216
0
    msgpack_pack_str_body(&mp_pck, "lines", 5);
217
218
0
    msgpack_pack_array(&mp_pck, total_lines);
219
220
0
    while ((ret = flb_log_event_decoder_next(
221
0
                    &log_decoder,
222
0
                    &log_event)) == FLB_EVENT_DECODER_SUCCESS) {
223
0
        map_off = mp_sbuf.size;
224
225
0
        array_size = 2;
226
0
        msgpack_pack_map(&mp_pck, array_size);
227
228
        /*
229
         * Append primary keys found, the return value is the number of appended
230
         * keys to the record, we use that to adjust the map header size.
231
         *
232
         * When exclude_promoted_keys is enabled, non-primary keys are packed
233
         * into mp_line_sbuf for use as the "line" body.
234
         */
235
0
        if (ctx->exclude_promoted_keys) {
236
0
            msgpack_sbuffer_init(&mp_line_sbuf);
237
0
            msgpack_packer_init(&mp_line_pck, &mp_line_sbuf,
238
0
                                msgpack_sbuffer_write);
239
240
0
            ret = record_append_primary_keys(ctx, log_event.body,
241
0
                                             &mp_pck, &mp_line_pck);
242
0
        }
243
0
        else {
244
0
            ret = record_append_primary_keys(ctx, log_event.body,
245
0
                                             &mp_pck, NULL);
246
0
        }
247
0
        array_size += ret;
248
249
        /* Timestamp */
250
0
        msgpack_pack_str(&mp_pck, 9);
251
0
        msgpack_pack_str_body(&mp_pck, "timestamp", 9);
252
0
        msgpack_pack_int(&mp_pck, (int) flb_time_to_double(&log_event.timestamp));
253
254
        /* Line */
255
0
        msgpack_pack_str(&mp_pck, 4);
256
0
        msgpack_pack_str_body(&mp_pck, "line", 4);
257
258
0
        if (ctx->exclude_promoted_keys) {
259
0
            msgpack_unpacked_init(&mp_line_result);
260
0
            off = 0;
261
0
            msgpack_unpack_next(&mp_line_result,
262
0
                                mp_line_sbuf.data, mp_line_sbuf.size, &off);
263
264
0
            line_json = flb_msgpack_to_json_str(1024, &mp_line_result.data,
265
0
                                                config->json_escape_unicode);
266
267
0
            msgpack_unpacked_destroy(&mp_line_result);
268
0
            msgpack_sbuffer_destroy(&mp_line_sbuf);
269
0
        }
270
0
        else {
271
0
            line_json = flb_msgpack_to_json_str(1024, log_event.body,
272
0
                                                config->json_escape_unicode);
273
0
        }
274
275
0
        len = strlen(line_json);
276
0
        msgpack_pack_str(&mp_pck, len);
277
0
        msgpack_pack_str_body(&mp_pck, line_json, len);
278
0
        flb_free(line_json);
279
280
        /* Adjust map header size */
281
0
        flb_mp_set_map_header_size(mp_sbuf.data + map_off, array_size);
282
0
    }
283
284
0
    flb_log_event_decoder_destroy(&log_decoder);
285
286
0
    json = flb_msgpack_raw_to_json_sds(mp_sbuf.data, mp_sbuf.size,
287
0
                                       config->json_escape_unicode);
288
0
    msgpack_sbuffer_destroy(&mp_sbuf);
289
290
0
    return json;
291
0
}
292
293
static void logdna_config_destroy(struct flb_logdna *ctx)
294
0
{
295
0
    if (ctx->u) {
296
0
        flb_upstream_destroy(ctx->u);
297
0
    }
298
299
0
    if (ctx->tags_formatted) {
300
0
        flb_sds_destroy(ctx->tags_formatted);
301
0
    }
302
303
0
    flb_free(ctx);
304
0
}
305
306
static struct flb_logdna *logdna_config_create(struct flb_output_instance *ins,
307
                                               struct flb_config *config)
308
0
{
309
0
    int ret;
310
0
    int len = 0;
311
0
    char *hostname;
312
0
    flb_sds_t tmp;
313
0
    flb_sds_t encoded;
314
0
    struct mk_list *head;
315
0
    struct flb_slist_entry *tag_entry;
316
0
    struct flb_logdna *ctx;
317
0
    struct flb_upstream *upstream;
318
319
    /* Create context */
320
0
    ctx = flb_calloc(1, sizeof(struct flb_logdna));
321
0
    if (!ctx) {
322
0
        flb_errno();
323
0
        return NULL;
324
0
    }
325
0
    ctx->ins = ins;
326
327
    /* Load config map */
328
0
    ret = flb_output_config_map_set(ins, (void *) ctx);
329
0
    if (ret == -1) {
330
0
        logdna_config_destroy(ctx);
331
0
        return NULL;
332
0
    }
333
334
    /* validate API key */
335
0
    if (!ctx->api_key) {
336
0
        flb_plg_error(ins, "no `api_key` was set, this is a mandatory property");
337
0
        logdna_config_destroy(ctx);
338
0
        return NULL;
339
0
    }
340
341
    /*
342
     * Tags: this value is a linked list of values created by the config map
343
     * reader.
344
     */
345
0
    if (ctx->tags) {
346
        /* For every tag, make sure no empty spaces exists */
347
0
        mk_list_foreach(head, ctx->tags) {
348
0
            tag_entry = mk_list_entry(head, struct flb_slist_entry, _head);
349
0
            len += flb_sds_len(tag_entry->str) + 1;
350
0
        }
351
352
        /* Compose a full tag for URI request */
353
0
        ctx->tags_formatted = flb_sds_create_size(len);
354
0
        if (!ctx->tags_formatted) {
355
0
            logdna_config_destroy(ctx);
356
0
            return NULL;
357
0
        }
358
359
0
        mk_list_foreach(head, ctx->tags) {
360
0
            tag_entry = mk_list_entry(head, struct flb_slist_entry, _head);
361
362
0
            encoded = flb_uri_encode(tag_entry->str,
363
0
                                     flb_sds_len(tag_entry->str));
364
0
            tmp = flb_sds_cat(ctx->tags_formatted,
365
0
                              encoded, flb_sds_len(encoded));
366
0
            ctx->tags_formatted = tmp;
367
0
            flb_sds_destroy(encoded);
368
369
0
            if (tag_entry->_head.next != ctx->tags) {
370
0
                tmp = flb_sds_cat(ctx->tags_formatted, ",", 1);
371
0
                ctx->tags_formatted = tmp;
372
0
            }
373
0
        }
374
0
    }
375
376
    /*
377
     * Hostname: if the hostname was not set manually, try to get it from the
378
     * environment variable.
379
     *
380
     * Note that hostname is populated by a config map, and config maps are
381
     * immutable so we use an internal variable to do a final composition
382
     * if required.
383
     */
384
0
    if (!ctx->hostname) {
385
0
        tmp = NULL;
386
0
        hostname = (char *) flb_env_get(config->env, "HOSTNAME");
387
0
        if (hostname) {
388
0
            ctx->_hostname = flb_sds_create(hostname);
389
0
        }
390
0
        else {
391
0
            ctx->_hostname = flb_sds_create("unknown");
392
0
        }
393
0
    }
394
0
    else {
395
0
        ctx->_hostname = flb_sds_create(ctx->hostname);
396
0
    }
397
398
    /* Bail if unsuccessful hostname creation */
399
0
    if (!ctx->_hostname) {
400
0
        flb_free(ctx);
401
0
        return NULL;
402
0
    }
403
404
    /* Create Upstream connection context */
405
0
    upstream = flb_upstream_create(config,
406
0
                                   ctx->logdna_host,
407
0
                                   ctx->logdna_port,
408
0
                                   FLB_IO_TLS, ins->tls);
409
0
    if (!upstream) {
410
0
        flb_free(ctx);
411
0
        return NULL;
412
0
    }
413
0
    ctx->u = upstream;
414
0
    flb_output_upstream_set(ctx->u, ins);
415
416
    /* Set networking defaults */
417
0
    flb_output_net_default(FLB_LOGDNA_HOST, atoi(FLB_LOGDNA_PORT), ins);
418
0
    return ctx;
419
0
}
420
421
static int cb_logdna_init(struct flb_output_instance *ins,
422
                          struct flb_config *config, void *data)
423
0
{
424
0
    struct flb_logdna *ctx;
425
426
0
    ctx = logdna_config_create(ins, config);
427
0
    if (!ctx) {
428
0
        flb_plg_error(ins, "cannot initialize configuration");
429
0
        return -1;
430
0
    }
431
432
0
    flb_output_set_context(ins, ctx);
433
434
    /*
435
     * This plugin instance uses the HTTP client interface, let's register
436
     * it debugging callbacks.
437
     */
438
0
    flb_output_set_http_debug_callbacks(ins);
439
440
0
    flb_plg_info(ins, "configured, hostname=%s", ctx->hostname);
441
0
    return 0;
442
0
}
443
444
static void cb_logdna_flush(struct flb_event_chunk *event_chunk,
445
                            struct flb_output_flush *out_flush,
446
                            struct flb_input_instance *i_ins,
447
                            void *out_context,
448
                            struct flb_config *config)
449
0
{
450
0
    int ret;
451
0
    int out_ret = FLB_OK;
452
0
    size_t b_sent;
453
0
    flb_sds_t uri;
454
0
    flb_sds_t tmp;
455
0
    flb_sds_t payload;
456
0
    struct flb_logdna *ctx = out_context;
457
0
    struct flb_connection *u_conn;
458
0
    struct flb_http_client *c;
459
460
    /* Format the data to the expected LogDNA Payload */
461
0
    payload = logdna_compose_payload(ctx,
462
0
                                     event_chunk->data,
463
0
                                     event_chunk->size,
464
0
                                     event_chunk->tag,
465
0
                                     flb_sds_len(event_chunk->tag),
466
0
                                     config);
467
0
    if (!payload) {
468
0
        flb_plg_error(ctx->ins, "cannot compose request payload");
469
0
        FLB_OUTPUT_RETURN(FLB_RETRY);
470
0
    }
471
472
    /* Lookup an available connection context */
473
0
    u_conn = flb_upstream_conn_get(ctx->u);
474
0
    if (!u_conn) {
475
0
        flb_plg_error(ctx->ins, "no upstream connections available");
476
0
        flb_sds_destroy(payload);
477
0
        FLB_OUTPUT_RETURN(FLB_RETRY);
478
0
    }
479
480
    /* Compose the HTTP URI */
481
0
    uri = flb_sds_create_size(256);
482
0
    if (!uri) {
483
0
        flb_plg_error(ctx->ins, "cannot allocate buffer for URI");
484
0
        flb_sds_destroy(payload);
485
0
        flb_free(ctx);
486
0
        FLB_OUTPUT_RETURN(FLB_RETRY);
487
0
    }
488
0
    tmp = flb_sds_printf(&uri,
489
0
                         "%s?hostname=%s&mac=%s&ip=%s&now=%lu&tags=%s",
490
0
                         ctx->logdna_endpoint,
491
0
                         ctx->_hostname,
492
0
                         ctx->mac_addr,
493
0
                         ctx->ip_addr,
494
0
                         time(NULL),
495
0
                         ctx->tags_formatted);
496
0
    if (!tmp) {
497
0
        flb_plg_error(ctx->ins, "error formatting URI");
498
0
        flb_sds_destroy(payload);
499
0
        flb_free(ctx);
500
0
        FLB_OUTPUT_RETURN(FLB_RETRY);
501
0
    }
502
503
    /* Create HTTP client context */
504
0
    c = flb_http_client(u_conn, FLB_HTTP_POST, uri,
505
0
                        payload, flb_sds_len(payload),
506
0
                        ctx->logdna_host, ctx->logdna_port,
507
0
                        NULL, 0);
508
0
    if (!c) {
509
0
        flb_plg_error(ctx->ins, "cannot create HTTP client context");
510
0
        flb_sds_destroy(uri);
511
0
        flb_sds_destroy(payload);
512
0
        flb_upstream_conn_release(u_conn);
513
0
        FLB_OUTPUT_RETURN(FLB_RETRY);
514
0
    }
515
516
    /* Set callback context to the HTTP client context */
517
0
    flb_http_set_callback_context(c, ctx->ins->callback);
518
519
    /* User Agent */
520
0
    flb_http_add_header(c, "User-Agent", 10, "Fluent-Bit", 10);
521
522
    /* Add Content-Type header */
523
0
    flb_http_add_header(c,
524
0
                        FLB_LOGDNA_CT, sizeof(FLB_LOGDNA_CT) - 1,
525
0
                        FLB_LOGDNA_CT_JSON, sizeof(FLB_LOGDNA_CT_JSON) - 1);
526
527
    /* Add auth */
528
0
    flb_http_basic_auth(c, ctx->api_key, "");
529
530
0
    flb_http_strip_port_from_host(c);
531
532
    /* Send HTTP request */
533
0
    ret = flb_http_do(c, &b_sent);
534
535
    /* Destroy buffers */
536
0
    flb_sds_destroy(uri);
537
0
    flb_sds_destroy(payload);
538
539
    /* Validate HTTP client return status */
540
0
    if (ret == 0) {
541
        /*
542
         * Only allow the following HTTP status:
543
         *
544
         * - 200: OK
545
         * - 201: Created
546
         * - 202: Accepted
547
         * - 203: no authorative resp
548
         * - 204: No Content
549
         * - 205: Reset content
550
         *
551
         */
552
0
        if (c->resp.status < 200 || c->resp.status > 205) {
553
0
            if (c->resp.payload) {
554
0
                flb_plg_error(ctx->ins, "%s:%i, HTTP status=%i\n%s",
555
0
                              ctx->logdna_host, ctx->logdna_port, c->resp.status,
556
0
                              c->resp.payload);
557
0
            }
558
0
            else {
559
0
                flb_plg_error(ctx->ins, "%s:%i, HTTP status=%i",
560
0
                              ctx->logdna_host, ctx->logdna_port, c->resp.status);
561
0
            }
562
0
            out_ret = FLB_RETRY;
563
0
        }
564
0
        else {
565
0
            if (c->resp.payload) {
566
0
                flb_plg_info(ctx->ins, "%s:%i, HTTP status=%i\n%s",
567
0
                             ctx->logdna_host, ctx->logdna_port,
568
0
                             c->resp.status, c->resp.payload);
569
0
            }
570
0
            else {
571
0
                flb_plg_info(ctx->ins, "%s:%i, HTTP status=%i",
572
0
                             ctx->logdna_host, ctx->logdna_port,
573
0
                             c->resp.status);
574
0
            }
575
0
        }
576
0
    }
577
0
    else {
578
0
        flb_plg_error(ctx->ins, "could not flush records to %s:%s (http_do=%i)",
579
0
                      FLB_LOGDNA_HOST, FLB_LOGDNA_PORT, ret);
580
0
        out_ret = FLB_RETRY;
581
0
    }
582
583
0
    flb_http_client_destroy(c);
584
0
    flb_upstream_conn_release(u_conn);
585
0
    FLB_OUTPUT_RETURN(out_ret);
586
0
}
587
588
static int cb_logdna_exit(void *data, struct flb_config *config)
589
0
{
590
0
    struct flb_logdna *ctx = data;
591
592
0
    if (!ctx) {
593
0
        return 0;
594
0
    }
595
596
0
    if (ctx->_hostname) {
597
0
        flb_sds_destroy(ctx->_hostname);
598
0
    }
599
0
    logdna_config_destroy(ctx);
600
0
    return 0;
601
0
}
602
603
/* Configuration properties map */
604
static struct flb_config_map config_map[] = {
605
    {
606
     FLB_CONFIG_MAP_STR, "logdna_host", FLB_LOGDNA_HOST,
607
     0, FLB_TRUE, offsetof(struct flb_logdna, logdna_host),
608
     "LogDNA Host address"
609
    },
610
611
    {
612
     FLB_CONFIG_MAP_INT, "logdna_port", FLB_LOGDNA_PORT,
613
     0, FLB_TRUE, offsetof(struct flb_logdna, logdna_port),
614
     "LogDNA TCP port"
615
    },
616
617
    {
618
     FLB_CONFIG_MAP_STR, "logdna_endpoint", FLB_LOGDNA_ENDPOINT,
619
     0, FLB_TRUE, offsetof(struct flb_logdna, logdna_endpoint),
620
     "LogDNA endpoint to send logs"
621
    },
622
623
    {
624
     FLB_CONFIG_MAP_STR, "api_key", NULL,
625
     0, FLB_TRUE, offsetof(struct flb_logdna, api_key),
626
     "Logdna API key"
627
    },
628
629
    {
630
     FLB_CONFIG_MAP_STR, "hostname", NULL,
631
     0, FLB_TRUE, offsetof(struct flb_logdna, hostname),
632
     "Local Server or device host name"
633
    },
634
635
    {
636
     FLB_CONFIG_MAP_STR, "mac", "",
637
     0, FLB_TRUE, offsetof(struct flb_logdna, mac_addr),
638
     "MAC address (optional)"
639
    },
640
641
    {
642
     FLB_CONFIG_MAP_STR, "ip", "",
643
     0, FLB_TRUE, offsetof(struct flb_logdna, ip_addr),
644
     "IP address (optional)"
645
    },
646
647
    {
648
     FLB_CONFIG_MAP_CLIST, "tags", "",
649
     0, FLB_TRUE, offsetof(struct flb_logdna, tags),
650
     "Tags (optional)"
651
    },
652
653
    {
654
     FLB_CONFIG_MAP_STR, "file", NULL,
655
     0, FLB_TRUE, offsetof(struct flb_logdna, file),
656
     "Name of the monitored file (optional)"
657
    },
658
659
    {
660
     FLB_CONFIG_MAP_STR, "app", "Fluent Bit",
661
     0, FLB_TRUE, offsetof(struct flb_logdna, app),
662
     "Name of the application generating the data (optional)"
663
    },
664
665
    {
666
     FLB_CONFIG_MAP_BOOL, "exclude_promoted_keys", "false",
667
     0, FLB_TRUE, offsetof(struct flb_logdna, exclude_promoted_keys),
668
     "Exclude promoted keys (meta, level, severity (promoted as level), app, file) from the line body"
669
    },
670
671
    /* EOF */
672
    {0}
673
674
};
675
676
static int cb_logdna_format_test(struct flb_config *config,
677
                                 struct flb_input_instance *ins,
678
                                 void *plugin_context,
679
                                 void *flush_ctx,
680
                                 int event_type,
681
                                 const char *tag, int tag_len,
682
                                 const void *data, size_t bytes,
683
                                 void **out_data, size_t *out_size)
684
0
{
685
0
    flb_sds_t json;
686
0
    struct flb_logdna *ctx = plugin_context;
687
688
0
    json = logdna_compose_payload(ctx, data, bytes, tag, tag_len, config);
689
0
    if (!json) {
690
0
        return -1;
691
0
    }
692
693
0
    *out_data = json;
694
0
    *out_size = flb_sds_len(json);
695
0
    return 0;
696
0
}
697
698
/* Plugin reference */
699
struct flb_output_plugin out_logdna_plugin = {
700
    .name        = "logdna",
701
    .description = "LogDNA",
702
    .cb_init     = cb_logdna_init,
703
    .cb_flush    = cb_logdna_flush,
704
    .cb_exit     = cb_logdna_exit,
705
    .config_map  = config_map,
706
    .test_formatter.callback = cb_logdna_format_test,
707
    .flags       = FLB_OUTPUT_NET | FLB_IO_TLS,
708
};