Skip to content

RUM telemetry #204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ if(NGINX_DATADOG_RUM_ENABLED)
PRIVATE
src/rum/config.cpp
src/rum/injection.cpp
src/rum/telemetry.cpp
)
target_compile_definitions(ngx_http_datadog_objs PUBLIC WITH_RUM)
endif()
Expand Down
2 changes: 2 additions & 0 deletions src/datadog_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ struct datadog_loc_conf_t {
#ifdef WITH_RUM
ngx_flag_t rum_enable = NGX_CONF_UNSET;
Snippet *rum_snippet = nullptr;
std::string rum_application_id_tag;
std::string rum_remote_config_tag;
#endif
};

Expand Down
6 changes: 6 additions & 0 deletions src/datadog_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ void DatadogContext::on_log_request(ngx_http_request_t *request) {
throw std::runtime_error{"on_log_request failed: could not get loc conf"};
}

#ifdef WITH_RUM
if (loc_conf->rum_enable) {
rum_ctx_.on_log_request(request);
}
#endif

if (!loc_conf->enable_tracing) {
return;
}
Expand Down
4 changes: 2 additions & 2 deletions src/ngx_http_datadog_module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ static auto datadog_commands =

#ifdef WITH_RUM
,
rum_directives
datadog::nginx::rum::rum_directives
#endif
);

Expand Down Expand Up @@ -488,7 +488,7 @@ static char *merge_datadog_loc_conf(ngx_conf_t *cf, void *parent,
#endif

#ifdef WITH_RUM
datadog_rum_merge_loc_config(cf, prev, conf);
datadog::nginx::rum::datadog_rum_merge_loc_config(cf, prev, conf);
#endif

return NGX_CONF_OK;
Expand Down
25 changes: 23 additions & 2 deletions src/rum/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ std::optional<int> parse_rum_version(std::string_view config_version) {

} // namespace

extern "C" {
namespace datadog::nginx::rum {

char *on_datadog_rum_config(ngx_conf_t *cf, ngx_command_t *command,
void *conf) {
Expand Down Expand Up @@ -164,6 +164,19 @@ char *on_datadog_rum_config(ngx_conf_t *cf, ngx_command_t *command,
}

loc_conf->rum_snippet = snippet;

loc_conf->rum_remote_config_tag = "remote_config_used:false";

if (auto it = rum_config.find("applicationId");
it != rum_config.end() && !it->second.empty()) {
loc_conf->rum_application_id_tag = "application_id:" + it->second[0];
}

if (auto it = rum_config.find("remoteConfigurationId");
it != rum_config.end() && !it->second.empty()) {
loc_conf->rum_remote_config_tag = "remote_config_used:true";
}

return NGX_CONF_OK;
}

Expand All @@ -175,6 +188,14 @@ char *datadog_rum_merge_loc_config(ngx_conf_t *cf,
child->rum_snippet = parent->rum_snippet;
}

if (child->rum_application_id_tag.empty()) {
child->rum_application_id_tag = parent->rum_application_id_tag;
}

if (child->rum_remote_config_tag.empty()) {
child->rum_remote_config_tag = parent->rum_remote_config_tag;
}

return NGX_OK;
}
}
} // namespace datadog::nginx::rum
8 changes: 5 additions & 3 deletions src/rum/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ extern "C" {
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
}

namespace datadog::nginx::rum {
// Handler for `datadog_rum_json_config` directive.
// Load a JSON RUM configuration file.
char *on_datadog_rum_json_config(ngx_conf_t *cf, ngx_command_t *command,
Expand All @@ -23,7 +25,7 @@ char *datadog_rum_merge_loc_config(ngx_conf_t *cf,
datadog::nginx::datadog_loc_conf_t *parent,
datadog::nginx::datadog_loc_conf_t *child);

const datadog::nginx::directive rum_directives[] = {
constexpr datadog::nginx::directive rum_directives[] = {
{
"datadog_rum",
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF |
Expand All @@ -42,5 +44,5 @@ const datadog::nginx::directive rum_directives[] = {
0,
NULL,
},
}
}
};
} // namespace datadog::nginx::rum
61 changes: 57 additions & 4 deletions src/rum/injection.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "injection.h"

#include <datadog/telemetry/telemetry.h>

extern "C" {
#include <ngx_core.h>
}
Expand All @@ -9,15 +11,15 @@ extern "C" {
#include "datadog_conf.h"
#include "ngx_http_datadog_module.h"
#include "string_util.h"
#include "telemetry.h"

namespace datadog {
namespace nginx {
namespace rum {
namespace {

ngx_table_elt_t *search_header(ngx_http_request_t *request,
std::string_view key) {
ngx_list_part_t *part = &request->headers_in.headers.part;
ngx_table_elt_t *search_header(ngx_list_t &headers, std::string_view key) {
ngx_list_part_t *part = &headers.part;
auto *h = static_cast<ngx_table_elt_t *>(part->elts);

for (std::size_t i = 0;; i++) {
Expand Down Expand Up @@ -86,32 +88,59 @@ ngx_int_t InjectionHandler::on_header_filter(
return next_header_filter(r);
}

if (auto injected_header = search_header(r, "x-datadog-rum-injected");
if (auto injected_header =
search_header(r->headers_in.headers, "x-datadog-rum-injected");
injected_header != nullptr) {
if (nginx::to_string_view(injected_header->value) == "1") {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"RUM SDK injection skipped: resource may already have RUM "
"SDK injected.");
datadog::telemetry::counter::increment(
telemetry::injection_skipped,
telemetry::build_tags("reason:already_injected",
cfg->rum_application_id_tag,
cfg->rum_remote_config_tag));

return next_header_filter(r);
}
}

if (r->header_only || r->headers_out.content_length_n == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"RUM SDK injection skipped: empty content");

datadog::telemetry::counter::increment(
telemetry::injection_skipped,
telemetry::build_tags("reason:no_content", cfg->rum_application_id_tag,
cfg->rum_remote_config_tag));

return next_header_filter(r);
}

if (!is_html_content(&r->headers_out.content_type)) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"RUM SDK injection skipped: not an HTML page");

datadog::telemetry::counter::increment(
telemetry::injection_skipped,
telemetry::build_tags("reason:invalid_content_type",
cfg->rum_application_id_tag,
cfg->rum_remote_config_tag));

return next_header_filter(r);
}

if (auto content_encoding = r->headers_out.content_encoding;
content_encoding != nullptr && content_encoding->value.len != 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"RUM SDK injection skipped: compressed html content");

datadog::telemetry::counter::increment(
telemetry::injection_skipped,
telemetry::build_tags("reason:compressed_html",
cfg->rum_application_id_tag,
cfg->rum_remote_config_tag));

return next_header_filter(r);
}

Expand Down Expand Up @@ -176,6 +205,12 @@ ngx_int_t InjectionHandler::on_body_filter(
state_ = state::injected;
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"RUM SDK injected successfully injected");

datadog::telemetry::counter::increment(
telemetry::injection_succeed,
telemetry::build_tags(cfg->rum_application_id_tag,
cfg->rum_remote_config_tag));

return output(r, output_chain, next_body_filter);
}
}
Expand All @@ -192,11 +227,29 @@ ngx_int_t InjectionHandler::on_body_filter(

ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"RUM SDK injection failed: no injection point found");

datadog::telemetry::counter::increment(
telemetry::injection_failed,
telemetry::build_tags("reason:missing_header_tag",
cfg->rum_application_id_tag,
cfg->rum_remote_config_tag));
}

return output(r, output_chain, next_body_filter);
}

ngx_int_t InjectionHandler::on_log_request(ngx_http_request_t *r) {
if (auto csp =
search_header(r->headers_out.headers, "content-security-policy");
csp != nullptr) {
datadog::telemetry::counter::increment(
telemetry::injection_failed,
telemetry::build_tags("status:seen", "kind:header"));
}

return NGX_OK;
}

// NOTE(@dmehala): this function is not necessary for now, however,
// it will when we will reuse buffer.
ngx_int_t InjectionHandler::output(
Expand Down
5 changes: 5 additions & 0 deletions src/rum/injection.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ class InjectionHandler final {
ngx_chain_t *in,
ngx_http_output_body_filter_pt &next_body_filter);

// Handles the logging phase of an HTTP request.
// @param r - HTTP request being processed.
// @return ngx_int_t - Status code indicating success or failure.
ngx_int_t on_log_request(ngx_http_request_t *r);

private:
// Sends the output to the next body filter.
// @param r - HTTP request being processed.
Expand Down
32 changes: 32 additions & 0 deletions src/rum/telemetry.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include "telemetry.h"

#include <datadog/telemetry/telemetry.h>

#include <format>
#include <string_view>

#include "version.h"

using namespace datadog::telemetry;

namespace datadog {
namespace nginx {
namespace rum {
namespace telemetry {

const datadog::telemetry::Counter injection_skipped = {"injection.skipped",
"rum", true};

const datadog::telemetry::Counter injection_succeed = {"injection.succeed",
"rum", true};

const datadog::telemetry::Counter injection_failed = {"injection.failed", "rum",
true};

const datadog::telemetry::Counter content_security_policy = {
"injection.content_security_policy", "rum", true};

} // namespace telemetry
} // namespace rum
} // namespace nginx
} // namespace datadog
35 changes: 35 additions & 0 deletions src/rum/telemetry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <datadog/telemetry/metrics.h>

#include <format>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "version.h"

namespace datadog {
namespace nginx {
namespace rum {
namespace telemetry {

template <typename... T>
auto build_tags(T&&... specific_tags) {
std::vector<std::string> tags{std::forward<T>(specific_tags)...};
tags.emplace_back("integration_name:nginx");
tags.emplace_back("injector_version:0.1.0");
tags.emplace_back(std::format("integration_version:{}",
std::string_view(datadog_semver_nginx_mod)));

return tags;
}

const extern datadog::telemetry::Counter injection_skipped;
const extern datadog::telemetry::Counter injection_succeed;
const extern datadog::telemetry::Counter injection_failed;
const extern datadog::telemetry::Counter content_security_policy;

} // namespace telemetry
} // namespace rum
} // namespace nginx
} // namespace datadog