4 #include "util/sentry/sentry.h" 6 #include <boost/beast/http/write.hpp> 9 #include "base/logging.h" 10 #include <glog/raw_logging.h> 12 #include "strings/strcat.h" 13 #include "util/asio/glog_asio_sink.h" 14 #include "util/http/http_client.h" 16 DEFINE_string(sentry_dsn,
"",
"Sentry DSN in the format <pivatekey>@hostname/<project_id>");
19 using namespace ::boost;
20 using namespace ::std;
31 class SentrySink :
public GlogAsioSink {
33 explicit SentrySink(Dsn dsn, IoContext* io_context);
36 void HandleItem(
const Item& item)
final;
38 bool ShouldIgnore(google::LogSeverity severity,
const char* full_filename,
int line)
final {
39 return severity < google::GLOG_ERROR;
43 GlogAsioSink::Cancel();
45 VLOG(1) <<
"Sentry::Cancel End";
49 string GenSentryBody(
const Item& item);
65 SentrySink::SentrySink(Dsn dsn, IoContext* io_context) : client_(io_context), dsn_(std::move(dsn)) {
66 size_t pos = dsn_.hostname.find(
':');
67 if (pos != string::npos) {
68 port_ = dsn_.hostname.substr(pos + 1);
69 dsn_.hostname.resize(pos);
73 std::string optional_secret_field = dsn_.secret_key.empty() ?
"" :
74 absl::StrCat(
", sentry_secret=", dsn_.secret_key);
75 client_.AddHeader(
"X-Sentry-Auth",
76 absl::StrCat(
"Sentry sentry_version=7",
77 ", sentry_key=", dsn_.public_key,
78 optional_secret_field));
79 client_.AddHeader(
"Content-Type",
"application/json");
80 client_.AddHeader(
"Host", dsn_.hostname);
81 client_.AddHeader(
"User-Agent",
"gaia-cpp/0.1");
82 dsn_.url = absl::StrCat(
"/api", dsn_.url,
"/store/");
85 void SentrySink::HandleItem(
const Item& item) {
86 RAW_VLOG(2,
"SentrySink::HandleItem");
88 auto ec = client_.Connect(dsn_.hostname, port_);
90 auto msg = ec.message();
91 RAW_VLOG(1,
"Could not connect %s", msg.c_str());
96 http::Client::Response resp;
97 string body = GenSentryBody(item);
98 ec = client_.Send(http::Client::Verb::post, dsn_.url, body, &resp);
101 RAW_VLOG(1,
"Could not send ");
106 string SentrySink::GenSentryBody(
const Item& item) {
107 string res = absl::StrCat(R
"({"culprit":")", item.base_filename, ":", item.line,
108 R
"(", "server_name":"TBD")"); 110 absl::StrAppend(&res, 114 R"(", "level":"error", "platform": "c++", "sdk": {"name": "sentry-cpp", 115 "version": "1.0.0"}, "timestamp":")"); 116 absl::StrAppend(&res, 1900 + item.tm_time.tm_year, "-", item.tm_time.tm_mon + 1,
"-",
117 item.tm_time.tm_mday,
"T", item.tm_time.tm_hour,
":", item.tm_time.tm_min,
":",
118 item.tm_time.tm_sec);
119 absl::StrAppend(&res, R
"("})"); 124 bool ParseDsn(
const string& dsn, Dsn* res) {
126 size_t kpos = dsn.find(
'@');
127 if (kpos == string::npos)
129 size_t protocol_sep_pos = dsn.find(
"://");
130 if (protocol_sep_pos == string::npos)
131 protocol_sep_pos = 0;
133 protocol_sep_pos += 3;
134 std::string key = dsn.substr(protocol_sep_pos, kpos - protocol_sep_pos);
135 size_t colon_pos = key.find(
':');
136 if (colon_pos == string::npos) {
137 res->public_key = key;
138 res->secret_key =
"";
140 res->public_key = key.substr(0, colon_pos);
141 res->secret_key = key.substr(colon_pos + 1);
144 size_t pos = dsn.find(
'/', kpos);
145 if (pos == string::npos)
147 res->hostname = dsn.substr(kpos, pos - kpos);
148 res->url = dsn.substr(pos);
150 VLOG(1) <<
"Dsn is: " << res->public_key
151 <<
"|" << res->secret_key
152 <<
"|" << res->hostname
159 void EnableSentry(IoContext* context) {
160 static std::atomic<bool> ran_once(
false);
161 CHECK(!ran_once.exchange(
true)) <<
"EnableSentry called twice";
163 std::string sentry_dsn = FLAGS_sentry_dsn;
164 if (sentry_dsn.empty()) {
165 if (
const char *env = getenv(
"SENTRY_LOG_URI")) {
167 LOG(INFO) <<
"SENTRY_LOG_URI found: " << sentry_dsn;
170 LOG(INFO) <<
"--sentry_dsn flag found: " << sentry_dsn;
172 if (sentry_dsn.empty()) {
173 LOG(INFO) <<
"No --sentry_dsn or SENTRY_LOG_URI, sentry is disabled";
177 CHECK(ParseDsn(sentry_dsn, &dsn)) <<
"Could not parse " << sentry_dsn;
179 auto ptr = std::make_unique<SentrySink>(std::move(dsn), context);
180 context->AttachCancellable(ptr.get());