Skip to content
Draft
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ set(FLB_VERSION_PATCH 0)
set(FLB_VERSION_STR "${FLB_VERSION_MAJOR}.${FLB_VERSION_MINOR}.${FLB_VERSION_PATCH}")

set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set (CMAKE_CXX_STANDARD 11)

# Find openssl
find_package(OpenSSL REQUIRED)
include_directories(${OpenSSL_INCLUDE_DIRS})

find_library(SSL_LIB libssl.a REQUIRED)
find_library(CRYPTO_LIB libcrypto.a REQUIRED)

# Find boost
find_package(Boost REQUIRED system)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME ON)
include_directories(${Boost_INCLUDE_DIRS})



# Define macro to identify Windows system (without Cygwin)
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
Expand Down
6 changes: 5 additions & 1 deletion plugins/out_stackdriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ set(src
stackdriver_http_request.c
stackdriver_timestamp.c
stackdriver_helper.c
stackdriver_flush.cpp
)

FLB_PLUGIN(out_stackdriver "${src}" "")

FLB_PLUGIN(out_stackdriver "${src}" "${Boost_LIBRARIES}")
target_link_libraries(flb-plugin-out_stackdriver OpenSSL::Crypto)
target_link_libraries(flb-plugin-out_stackdriver OpenSSL::SSL)
49 changes: 12 additions & 37 deletions plugins/out_stackdriver/stackdriver.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#include "stackdriver_http_request.h"
#include "stackdriver_timestamp.h"
#include "stackdriver_helper.h"
#include "stackdriver_flush.h"

#include <mbedtls/base64.h>
#include <mbedtls/sha256.h>

Expand Down Expand Up @@ -263,7 +265,7 @@ static int get_oauth2_token(struct flb_stackdriver *ctx)
return 0;
}

static char *get_google_token(struct flb_stackdriver *ctx)
char *get_google_token(struct flb_stackdriver *ctx)
{
int ret = 0;

Expand Down Expand Up @@ -855,15 +857,9 @@ static int cb_stackdriver_init(struct flb_output_instance *ins,
io_flags |= FLB_IO_IPV6;
}

/* Create Upstream context for Stackdriver Logging (no oauth2 service) */
ctx->u = flb_upstream_create_url(config, FLB_STD_WRITE_URL,
io_flags, &ins->tls);
ctx->flush_ctx = stackdriver_cpp_init(1);
ctx->metadata_u = flb_upstream_create_url(config, "http://metadata.google.internal",
FLB_IO_TCP, NULL);
if (!ctx->u) {
flb_plg_error(ctx->ins, "upstream creation failed");
return -1;
}
FLB_IO_TCP, NULL);
if (!ctx->metadata_u) {
flb_plg_error(ctx->ins, "metadata upstream creation failed");
return -1;
Expand Down Expand Up @@ -1263,13 +1259,10 @@ static int pack_json_payload(int insert_id_extracted,
return ret;
}

static int stackdriver_format(struct flb_config *config,
struct flb_input_instance *ins,
void *plugin_context,
void *flush_ctx,
int stackdriver_format(struct flb_stackdriver *ctx,
const char *tag, int tag_len,
const void *data, size_t bytes,
void **out_data, size_t *out_size)
const char *data, size_t bytes,
flb_sds_t* out_data, size_t *out_size)
{
int len;
int ret;
Expand All @@ -1289,7 +1282,6 @@ static int stackdriver_format(struct flb_config *config,
msgpack_sbuffer mp_sbuf;
msgpack_packer mp_pck;
flb_sds_t out_buf;
struct flb_stackdriver *ctx = plugin_context;

/* Parameters for severity */
int severity_extracted = FLB_FALSE;
Expand Down Expand Up @@ -1881,31 +1873,14 @@ static void cb_stackdriver_flush(const void *data, size_t bytes,
char *token;
flb_sds_t payload_buf;
size_t payload_size;
void *out_buf;
size_t out_size;
struct flb_stackdriver *ctx = out_context;
struct flb_upstream_conn *u_conn;
struct flb_http_client *c;

/* Get upstream connection */
u_conn = flb_upstream_conn_get(ctx->u);
if (!u_conn) {
FLB_OUTPUT_RETURN(FLB_RETRY);
}

/* Reformat msgpack to stackdriver JSON payload */
ret = stackdriver_format(config, i_ins,
ctx, NULL,
tag, tag_len,
data, bytes,
&out_buf, &out_size);
if (ret != 0) {
flb_upstream_conn_release(u_conn);
FLB_OUTPUT_RETURN(FLB_RETRY);
}
struct flb_thread *cur_thread = (struct flb_thread *) pthread_getspecific(flb_thread_key);
stackdriver_cpp_flush(ctx, cur_thread, data, bytes, tag, tag_len);
FLB_OUTPUT_RETURN(FLB_RETRY);

payload_buf = (flb_sds_t) out_buf;
payload_size = out_size;

/* Get or renew Token */
token = get_google_token(ctx);
Expand Down Expand Up @@ -1985,7 +1960,7 @@ struct flb_output_plugin out_stackdriver_plugin = {
.cb_exit = cb_stackdriver_exit,

/* Test */
.test_formatter.callback = stackdriver_format,
//.test_formatter.callback = stackdriver_format,

/* Plugin flags */
.flags = FLB_OUTPUT_NET | FLB_IO_TLS,
Expand Down
17 changes: 12 additions & 5 deletions plugins/out_stackdriver/stackdriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@

/* Stackdriver Logging 'write' end-point */
#define FLB_STD_WRITE_URI "/v2/entries:write"
#define FLB_STD_WRITE_URL \
"https://logging.googleapis.com" FLB_STD_WRITE_URI
#define FLB_STD_WRITE_DOMAIN "logging.googleapis.com"
#define FLB_STD_WRITE_URL ("https://" FLB_STD_WRITE_DOMAIN FLB_STD_WRITE_URI)

/* Timestamp format */
#define FLB_STD_TIME_FMT "%Y-%m-%dT%H:%M:%S"
Expand Down Expand Up @@ -118,9 +118,6 @@ struct flb_stackdriver {
/* oauth2 context */
struct flb_oauth2 *o;

/* upstream context for stackdriver write end-point */
struct flb_upstream *u;

/* upstream context for metadata end-point */
struct flb_upstream *metadata_u;

Expand All @@ -129,6 +126,9 @@ struct flb_stackdriver {

/* Fluent Bit context */
struct flb_config *config;

/* C++ flush context context */
struct StackdriverFlushContext* flush_ctx;
};

typedef enum {
Expand All @@ -154,4 +154,11 @@ typedef enum {
INSERTID_NOT_PRESENT = 2
} insert_id_status;

char *get_google_token(struct flb_stackdriver *ctx);

int stackdriver_format(struct flb_stackdriver *ctx,
const char *tag, int tag_len,
const char *data, size_t bytes,
flb_sds_t* out_data, size_t *out_size);

#endif
9 changes: 6 additions & 3 deletions plugins/out_stackdriver/stackdriver_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

#include "stackdriver.h"
#include "stackdriver_conf.h"
#include "stackdriver_flush.h"

static inline int key_cmp(const char *str, int len, const char *cmp) {

Expand Down Expand Up @@ -351,6 +352,10 @@ int flb_stackdriver_conf_destroy(struct flb_stackdriver *ctx)
return -1;
}

if (ctx->flush_ctx) {
stackdriver_cpp_destroy(ctx);
}

if (ctx->k8s_resource_type){
flb_sds_destroy(ctx->namespace_name);
flb_sds_destroy(ctx->pod_name);
Expand Down Expand Up @@ -390,9 +395,7 @@ int flb_stackdriver_conf_destroy(struct flb_stackdriver *ctx)
flb_upstream_destroy(ctx->metadata_u);
}

if (ctx->u) {
flb_upstream_destroy(ctx->u);
}

flb_free(ctx);

return 0;
Expand Down
155 changes: 155 additions & 0 deletions plugins/out_stackdriver/stackdriver_flush.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#define BOOST_NETWORK_ENABLE_HTTPS
extern "C" {
#include "stackdriver.h"
#include <fluent-bit/flb_output.h>
#include <fluent-bit/flb_output_plugin.h>
#include <fluent-bit/flb_thread.h>
}
#include "stackdriver_flush.h"

#include <string>
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>

#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/ssl.hpp>

#include <boost/bind.hpp>

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/http/status.hpp>
#include <boost/beast/version.hpp>
#include <boost/lexical_cast.hpp>



namespace beast = boost::beast;
namespace http = beast::http;
using boost::asio::ip::tcp;

extern "C" char *get_google_token(struct flb_stackdriver *ctx);


extern "C" int stackdriver_format(struct flb_stackdriver *ctx,
const char *tag, int tag_len,
const char *data, size_t bytes,
flb_sds_t* out_data, size_t *out_size);



extern "C" StackdriverFlushContext* stackdriver_cpp_init(int num_threads) {

StackdriverFlushContext* ctx = new StackdriverFlushContext();
ctx->workers.reserve(num_threads);
for(int i = 0; i < num_threads; i++)
{
ctx->workers.emplace_back(
[ctx] {
// Stops the asio event loop from running out of work
boost::asio::executor_work_guard<boost::asio::io_context::executor_type>
work_guard = boost::asio::make_work_guard(ctx->ioc);

ctx->ioc.run();
});
}

return ctx;
}

void cpp_internal_flush(struct flb_stackdriver* plg_ctx, struct flb_thread* calling_thread, const char* data, size_t data_len, const char* tag, int tag_len){
StackdriverFlushContext* ctx = plg_ctx->flush_ctx;

/* Get the authorization token */
std::unique_lock<std::mutex> lock(ctx->mutex);
char* c_token = get_google_token(plg_ctx);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to token mutex

if (!c_token) {
// flb_plg_.. ids are macros, not funcitons.
flb_plg_error(plg_ctx->ins, "cannot retrieve oauth2 token");
flb_output_return(FLB_RETRY, calling_thread);
return;
}
std::string token = c_token;
lock.unlock();

flb_sds_t c_payload_buf = NULL;
size_t payload_size = 0;
/* Reformat msgpack to stackdriver JSON payload */
int ret = stackdriver_format(plg_ctx, tag, tag_len,
data, data_len,
&c_payload_buf, &payload_size);
if (ret != 0) {
flb_plg_error(plg_ctx->ins, "cannot format payload JSON");
flb_output_return(FLB_RETRY, calling_thread);
return;
}
std::string payload(c_payload_buf, payload_size);
flb_sds_destroy(c_payload_buf);

try {
// look up endpoint
tcp::resolver resolver(ctx->ioc);
tcp::resolver::query query(FLB_STD_WRITE_DOMAIN, "https");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);

// handshake
boost::asio::ssl::context ssl_ctx(boost::asio::ssl::context::method::sslv23_client);
boost::asio::ssl::stream<tcp::socket> stream(ctx->ioc, ssl_ctx);
boost::asio::connect(stream.lowest_layer(), endpoint_iterator);
stream.handshake(boost::asio::ssl::stream_base::handshake_type::client);

// HTTP request
http::request<http::string_body> req{http::verb::post, FLB_STD_WRITE_URI, 11};
req.set(http::field::host, FLB_STD_WRITE_DOMAIN);
req.set(http::field::user_agent, "Fluent-Bit");
req.set(http::field::content_type, "application/json");
req.set(http::field::authorization, std::string("Bearer ") + token);
req.set(http::field::content_length, boost::lexical_cast<std::string>(payload_size));
req.body() = payload;
req.prepare_payload();

http::write(stream, req);


// Receive the HTTP response
beast::flat_buffer buffer;
http::response<http::dynamic_body> resp;
http::read(stream, buffer, resp);
http::status_class status_class = http::to_status_class(resp.result());
if (status_class == http::status_class::successful){
flb_output_return(FLB_OK, calling_thread);
return;
} else if (status_class == http::status_class::server_error){
flb_output_return(FLB_RETRY, calling_thread);
return;
} else {
flb_output_return(FLB_ERROR, calling_thread);
return;
}

} catch (std::exception& e) {
flb_plg_error(plg_ctx->ins, "https request failed: ", e.what());
flb_output_return(FLB_RETRY, calling_thread);
return;
}
flb_plg_error(plg_ctx->ins, "This line shouldn't be executed");
flb_output_return(FLB_ERROR, calling_thread);

}


extern "C" void stackdriver_cpp_flush(struct flb_stackdriver * plg_ctx, struct flb_thread* calling_thread, const char* data, size_t data_len, const char* tag, int tag_len) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document lifetime of variables and thread control

StackdriverFlushContext* ctx = plg_ctx->flush_ctx;
boost::asio::post(ctx->ioc, boost::bind(cpp_internal_flush, plg_ctx, calling_thread, data, data_len, tag, tag_len));
flb_thread_yield(calling_thread, FLB_FALSE);
}

extern "C" void stackdriver_cpp_destroy(struct flb_stackdriver * plg_ctx) {
// Just leak for now

}
Loading