-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathserver.cpp
More file actions
181 lines (159 loc) · 8.04 KB
/
Copy pathserver.cpp
File metadata and controls
181 lines (159 loc) · 8.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/**
* @file server.cpp
* @brief Backend app for the distributed tracing demo (see README.md).
*
* Shows the server-side tracing surface of the C++ agent in one request flow:
*
* GET /api/members
* → new span with the trace context extracted from the request headers,
* making it a child of the proxy's span in the same trace
* (helper::TraceHttpServerRequest also records the remote address,
* the endpoint, and the configured request headers — here User-Agent)
* → (simulated) MySQL query traced as a child span event
* · SERVICE_TYPE_MYSQL_QUERY + destination/endpoint of the database
* make the database its own node in the server map
* · SetSqlQuery records the statement itself
* → follow-up work handed to a worker thread traced with an async span
* · created on the request thread, used only by the worker thread
* → response recorded via helper::TraceHttpServerResponse
* (status code, URL stats, configured response headers), span ended
*
* The span travels to the traced helpers through a thread_local slot instead
* of parameters, and span events use the RAII helper::ScopedSpanEvent.
*/
#include <chrono>
#include <csignal>
#include <cstdlib>
#include <iostream>
#include <string>
#include <thread>
#include "pinpoint/tracer.h"
#include "httplib.h"
#include "http_trace_context.h"
// Span of the request being handled on this thread. The handler stores it
// here so the traced helpers below pick it up without parameter plumbing;
// per-thread storage also keeps each span on its single owning thread, as
// the Span thread-safety contract requires.
static thread_local pinpoint::SpanPtr t_span;
/* ---- Root span: extract inbound trace context --------------------------- */
// Opens the root span for an inbound request. NewSpan() reads the Pinpoint-*
// propagation headers through the reader, so this span continues the trace
// started by the caller (the proxy). TraceHttpServerRequest then records the
// remote address, the endpoint, and the configured request headers.
static pinpoint::SpanPtr make_span(const httplib::Request& req) {
HttpHeaderReader header_reader(req.headers);
auto span = pinpoint::GlobalAgent()->NewSpan("C++ DB Server", req.path, req.method, header_reader);
auto end_point = req.get_header_value("Host");
if (end_point.empty()) {
end_point = req.local_addr + ":" + std::to_string(req.local_port);
}
pinpoint::helper::TraceHttpServerRequest(span, req.remote_addr, end_point, header_reader);
return span;
}
/* ---- DB query traced as a MySQL span event ------------------------------ */
// Simulates executing the members query and returns the result rows as JSON.
//
// This is a stand-in so the demo needs no real database. In a real
// application, connect to the database and run the query right here —
// the surrounding span event in query_members() traces that work as-is;
// nothing about the tracing changes.
static std::string execute_query_simulated() {
std::this_thread::sleep_for(std::chrono::milliseconds(20));
return "{\"members\":[{\"id\":1,\"name\":\"pinpoint\"},"
"{\"id\":2,\"name\":\"naver\"},{\"id\":3,\"name\":\"cpp-agent\"}]}";
}
// Answers the request with the member rows, tracing the (simulated) query
// as a child span event on the thread-local span.
static void query_members(httplib::Response& res) {
static const char* kQuery = "SELECT id, name FROM members";
// RAII span event: EndEvent() runs when the scope closes. The MySQL
// service type plus destination/endpoint make the database show up as
// its own node in the server map; SetSqlQuery records the statement.
// On a query failure, record it with event->SetError(...).
pinpoint::helper::ScopedSpanEvent event(t_span, "members.select",
pinpoint::SERVICE_TYPE_MYSQL_QUERY);
event->SetDestination("demo");
event->SetEndPoint("mysql:3306");
event->SetSqlQuery(kQuery, {});
res.status = 200;
res.set_content(execute_query_simulated(), "application/json");
}
/* ---- Background work traced with an async span -------------------------- */
// Hands follow-up work to a worker thread traced with an async span.
static void run_async_audit() {
// NewAsyncSpan() hangs the async child off the open span event, so this
// scoped event must still be alive when it is called.
pinpoint::helper::ScopedSpanEvent schedule_event(t_span, "audit.schedule");
// The async span is created on this (owning) thread and then used
// exclusively by the worker — one span instance must only ever be used
// by a single thread. It is handed over by capture: thread-local state
// does not cross threads.
auto async_span = t_span->NewAsyncSpan("audit.async");
std::thread async_worker([async_span] {
{
// The worker's own work, as an event on the async span; the
// scope ends the event before the async span is ended below.
pinpoint::helper::ScopedSpanEvent work(async_span, "audit.write");
std::this_thread::sleep_for(std::chrono::milliseconds(30));
}
async_span->EndSpan();
});
// Joined so the tracing thread never races agent shutdown.
async_worker.join();
}
/* ---- Entry point --------------------------------------------------------- */
// Signal handling is the host's job, not the agent's: the agent installs no
// handler (Shutdown() joins threads and tears down gRPC, none of which is
// async-signal-safe). Stop the listener from the handler so that main()
// unwinds normally and the ScopedAgent below runs Shutdown(); exiting from
// the handler (or being killed) would drop every span still queued.
httplib::Server* g_server = nullptr;
void stop_server(int) {
if (g_server) {
g_server->stop();
}
}
int main() {
setenv("PINPOINT_CPP_APPLICATION_NAME", "cpp-db-server", 0);
setenv("PINPOINT_CPP_HTTP_COLLECT_URL_STAT", "true", 0);
// Record only the User-Agent request header on the span (see doc/config.md).
setenv("PINPOINT_CPP_HTTP_SERVER_RECORD_REQUEST_HEADER", "User-Agent", 0);
// Starts the agent now and calls Shutdown() when main() returns, on every
// return path. Shutdown() is the only thing that delivers the last queued
// spans and tells the collector the agent stopped.
pinpoint::helper::ScopedAgent agent_guard;
if (!agent_guard.started()) {
std::cerr << "failed to start the pinpoint agent: check the agent log" << std::endl;
}
httplib::Server server;
g_server = &server;
std::signal(SIGINT, stop_server);
std::signal(SIGTERM, stop_server);
server.Get("/api/members", [](const httplib::Request& req, httplib::Response& res) {
// 1. Root span from the inbound trace context, published to the
// traced helpers through the thread-local slot.
auto span = make_span(req);
t_span = span;
// 2. Traced work, wrapped in one handler-level span event whose
// scope covers the whole handler body; the events opened inside
// become its children in the call tree. Both helpers read the
// span from t_span.
{
pinpoint::helper::ScopedSpanEvent handler_event(span, "server.members");
query_members(res);
run_async_audit();
}
// 3. Record the response (status code, URL stats, configured
// response headers) and finish the span; the recorded data is
// delivered to the collector asynchronously.
HttpHeaderReader response_reader(res.headers);
pinpoint::helper::TraceHttpServerResponse(span, req.path, req.method, res.status, response_reader);
span->EndSpan();
// httplib pools threads: clear the slot so the span is released now
// and never seen by the next request on this thread.
t_span.reset();
});
std::cout << "db server listening on :8081" << std::endl;
server.listen("0.0.0.0", 8081); // returns after stop_server()
// agent_guard goes out of scope here: Shutdown() flushes the queued spans.
}