Skip to content

Commit 57257de

Browse files
ivanmurashkofacebook-github-bot
authored andcommitted
Scuba Logger for Swift Glass Client
Summary: This diff implements a Scuba logger for the Swift Glass client. The logger is designed to capture request information and log it to Scuba for future analysis. The [swift_glass_client](https://fburl.com/scuba/swift_glass_client/90h9dkm1) scuba table has been created ### Purpose The Scuba logger is designed to provide insights into the performance and usage of the Swift Glass client. By capturing request information and logging it to Scuba, developers can analyze trends, identify issues, and optimize the client for better performance. Reviewed By: Wilfred Differential Revision: D78991582 fbshipit-source-id: 5b56f7f7103f5776e5d8a770a0a31c6a38a795ab
1 parent 9b41a4b commit 57257de

5 files changed

Lines changed: 301 additions & 41 deletions

File tree

glean/client/swift/GlassAccess.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ GlassAccess::GlassAccess() : client(nullptr), hgRoot_(getHgRoot()) {}
2626

2727
std::optional<protocol::LocationList> GlassAccess::usrToDefinition(
2828
const std::string& usr) {
29-
facebook::glean::swift::Clock clock;
3029
std::string msg;
3130

3231
// Check if this is a Swift USR (starts with "s:")
@@ -37,9 +36,6 @@ std::optional<protocol::LocationList> GlassAccess::usrToDefinition(
3736
// Handle non-Swift USR using clangUSRToDefinition with hash
3837
return handleUSRHash(usr, msg);
3938
}
40-
41-
auto duration = clock.duration();
42-
LOG(INFO) << "usrToDefinition request took " << duration << " milliseconds";
4339
}
4440

4541
std::optional<protocol::LocationList> GlassAccess::handleUSR(

glean/client/swift/JsonServer.cpp

Lines changed: 145 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@
88

99
#include "glean/client/swift/JsonServer.h"
1010
#include <folly/json.h>
11+
#include "glean/client/swift/Clock.h"
1112

12-
JsonServer::JsonServer() : running_(false), glassAccess_(nullptr) {}
13+
JsonServer::JsonServer() : running_(false), glassAccess_(nullptr) {
14+
// Initialize ScubaLogger and ScubaData
15+
scubaLogger_ = std::make_unique<facebook::glean::swift::ScubaLogger>();
16+
scubaData_ = std::make_unique<facebook::rfe::ScubaData>("swift_glass_client");
17+
}
1318

1419
void JsonServer::setGlassAccess(std::unique_ptr<IGlassAccess> glassAccess) {
1520
glassAccess_ = std::move(glassAccess);
@@ -33,6 +38,8 @@ void JsonServer::stop() {
3338
void JsonServer::processRequest(
3439
const std::string& requestStr,
3540
std::ostream& output) {
41+
facebook::glean::swift::Clock clock;
42+
3643
try {
3744
auto request = folly::parseJson(requestStr);
3845

@@ -45,72 +52,149 @@ void JsonServer::processRequest(
4552
handleUSRToDefinitionRequest(id, value, output);
4653
} else {
4754
// Send error response for unknown method
48-
sendErrorResponse(id, -32601, "Method not found", output);
55+
auto duration = clock.duration();
56+
sendErrorResponse(
57+
id,
58+
-32601,
59+
"Method not found",
60+
output,
61+
method,
62+
value,
63+
determineUSRType(value),
64+
duration);
4965
}
5066
} catch (const std::exception& e) {
51-
// Send parse error response
67+
// Send parse error response - no method/usr available due to parsing error
68+
auto duration = clock.duration();
5269
sendErrorResponse(
5370
folly::dynamic::object,
5471
-32700,
5572
"Parse error: " + std::string(e.what()),
56-
output);
73+
output,
74+
std::nullopt,
75+
std::nullopt,
76+
std::nullopt,
77+
duration);
5778
}
5879
}
5980

6081
void JsonServer::handleUSRToDefinitionRequest(
6182
const folly::dynamic& id,
6283
const std::string& usr,
6384
std::ostream& output) {
85+
// Start timing for scuba logging
86+
facebook::glean::swift::Clock clock;
87+
6488
if (!glassAccess_) {
89+
auto duration = clock.duration();
6590
sendErrorResponse(
66-
id, -32603, "Internal error: GlassAccess not initialized", output);
91+
id,
92+
-32603,
93+
"Internal error: GlassAccess not initialized",
94+
output,
95+
"USRToDefinition",
96+
usr,
97+
determineUSRType(usr),
98+
duration);
6799
return;
68100
}
69101

70-
// Call GlassAccess to get definition locations
71-
auto locations = glassAccess_->usrToDefinition(usr);
102+
// Determine USR type and method
103+
facebook::glean::swift::USRType usrType = determineUSRType(usr);
104+
std::string method = (usrType == facebook::glean::swift::USRType::SWIFT)
105+
? "usrToDefinition"
106+
: "clangUSRToDefinition";
72107

73-
// Create response
74-
folly::dynamic response = folly::dynamic::object;
75-
response["id"] = id;
108+
facebook::glean::swift::Status status =
109+
facebook::glean::swift::Status::FAILED;
110+
std::string error;
76111

77-
if (locations.has_value()) {
78-
// Convert protocol::Location objects to JSON
79-
folly::dynamic result = folly::dynamic::array;
80-
for (const auto& location : locations.value()) {
81-
folly::dynamic locationJson = folly::dynamic::object;
82-
locationJson["uri"] = location.uri;
112+
try {
113+
// Call GlassAccess to get definition locations
114+
auto locations = glassAccess_->usrToDefinition(usr);
115+
116+
// Create response
117+
folly::dynamic response = folly::dynamic::object;
118+
response["id"] = id;
119+
120+
if (locations.has_value() && !locations->empty()) {
121+
// Convert protocol::Location objects to JSON
122+
folly::dynamic result = folly::dynamic::array;
123+
for (const auto& location : locations.value()) {
124+
folly::dynamic locationJson = folly::dynamic::object;
125+
locationJson["uri"] = location.uri;
126+
127+
folly::dynamic range = folly::dynamic::object;
128+
folly::dynamic start = folly::dynamic::object;
129+
start["line"] = location.range.start.line;
130+
start["character"] = location.range.start.character;
131+
132+
folly::dynamic end = folly::dynamic::object;
133+
end["line"] = location.range.end.line;
134+
end["character"] = location.range.end.character;
135+
136+
range["start"] = start;
137+
range["end"] = end;
138+
locationJson["range"] = range;
139+
140+
result.push_back(locationJson);
141+
}
142+
response["result"] = result;
143+
status = facebook::glean::swift::Status::SUCCESS;
144+
} else {
145+
// No definitions found, return empty array
146+
response["result"] = folly::dynamic::array;
147+
status = facebook::glean::swift::Status::NOT_FOUND;
148+
}
83149

84-
folly::dynamic range = folly::dynamic::object;
85-
folly::dynamic start = folly::dynamic::object;
86-
start["line"] = location.range.start.line;
87-
start["character"] = location.range.start.character;
150+
// Calculate duration and log the request
151+
auto duration = clock.duration();
88152

89-
folly::dynamic end = folly::dynamic::object;
90-
end["line"] = location.range.end.line;
91-
end["character"] = location.range.end.character;
153+
// Use folly::toJson(response) to get the result string for logging
154+
std::string resultStr = folly::toJson(response["result"]);
92155

93-
range["start"] = start;
94-
range["end"] = end;
95-
locationJson["range"] = range;
156+
// Log the request to Scuba
157+
scubaLogger_->logRequest(
158+
scubaData_.get(),
159+
method,
160+
usr,
161+
usrType,
162+
resultStr,
163+
status,
164+
duration,
165+
error,
166+
std::nullopt);
96167

97-
result.push_back(locationJson);
98-
}
99-
response["result"] = result;
100-
} else {
101-
// No definitions found, return empty array
102-
response["result"] = folly::dynamic::array;
103-
}
168+
// Send response
169+
output << folly::toJson(response) << "\n";
104170

105-
// Send response
106-
output << folly::toJson(response) << "\n";
171+
} catch (const std::exception& e) {
172+
error = e.what();
173+
174+
// Calculate duration and log the failed request
175+
auto duration = clock.duration();
176+
177+
sendErrorResponse(
178+
id,
179+
-32603,
180+
"Internal error: " + error,
181+
output,
182+
method,
183+
usr,
184+
usrType,
185+
duration);
186+
}
107187
}
108188

109189
void JsonServer::sendErrorResponse(
110190
const folly::dynamic& id,
111191
int code,
112192
const std::string& message,
113-
std::ostream& output) {
193+
std::ostream& output,
194+
const std::optional<std::string>& method,
195+
const std::optional<std::string>& usr,
196+
const std::optional<facebook::glean::swift::USRType>& usrType,
197+
int64_t duration) {
114198
folly::dynamic response = folly::dynamic::object;
115199
response["id"] = id;
116200

@@ -120,4 +204,29 @@ void JsonServer::sendErrorResponse(
120204
response["error"] = error;
121205

122206
output << folly::toJson(response) << "\n";
207+
208+
// Log error to Scuba
209+
if (scubaLogger_ && scubaData_) {
210+
scubaLogger_->logRequest(
211+
scubaData_.get(),
212+
method.value_or("unknown"),
213+
usr.value_or(""),
214+
usrType.value_or(facebook::glean::swift::USRType::UNKNOWN),
215+
"[]",
216+
facebook::glean::swift::Status::FAILED,
217+
duration,
218+
message,
219+
std::nullopt);
220+
}
221+
}
222+
223+
facebook::glean::swift::USRType JsonServer::determineUSRType(
224+
const std::string& usr) {
225+
if (usr.length() >= 2 && usr.substr(0, 2) == "s:") {
226+
return facebook::glean::swift::USRType::SWIFT;
227+
} else if (usr.length() >= 2 && usr.substr(0, 2) == "c:") {
228+
return facebook::glean::swift::USRType::CPP;
229+
} else {
230+
return facebook::glean::swift::USRType::UNKNOWN;
231+
}
123232
}

glean/client/swift/JsonServer.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <iostream>
1515
#include <memory>
1616
#include "glean/client/swift/IGlassAccess.h"
17+
#include "glean/client/swift/ScubaLogger.h"
18+
#include "rfe/scubadata/ScubaData.h"
1719

1820
class JsonServer {
1921
public:
@@ -36,6 +38,9 @@ class JsonServer {
3638
FRIEND_TEST(JsonServerTest, InvalidJSONRequest);
3739
std::atomic<bool> running_;
3840
std::unique_ptr<IGlassAccess> glassAccess_;
41+
std::unique_ptr<facebook::glean::swift::ScubaLogger> scubaLogger_;
42+
std::unique_ptr<facebook::rfe::ScubaData> scubaData_;
43+
3944
void handleUSRToDefinitionRequest(
4045
const folly::dynamic& id,
4146
const std::string& usr,
@@ -44,5 +49,12 @@ class JsonServer {
4449
const folly::dynamic& id,
4550
int code,
4651
const std::string& message,
47-
std::ostream& output);
52+
std::ostream& output,
53+
const std::optional<std::string>& method = std::nullopt,
54+
const std::optional<std::string>& usr = std::nullopt,
55+
const std::optional<facebook::glean::swift::USRType>& usrType =
56+
std::nullopt,
57+
int64_t duration = 0);
58+
59+
facebook::glean::swift::USRType determineUSRType(const std::string& usr);
4860
};

glean/client/swift/ScubaLogger.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#include "glean/client/swift/ScubaLogger.h"
10+
#include <glog/logging.h>
11+
#include <cstdlib>
12+
13+
namespace facebook::glean::swift {
14+
15+
ScubaLogger::ScubaLogger()
16+
: unixname_(std::getenv("USER") ? std::getenv("USER") : "unknown"),
17+
hostmachine_(boost::asio::ip::host_name()) {}
18+
19+
void ScubaLogger::logRequest(
20+
facebook::rfe::ScubaData* scubaLogger,
21+
const std::string& method,
22+
const std::string& usr,
23+
USRType usrType,
24+
const std::string& result,
25+
Status status,
26+
int64_t duration,
27+
const std::string& error,
28+
const std::optional<std::string>& revision) {
29+
if (!scubaLogger) {
30+
return;
31+
}
32+
33+
facebook::rfe::ScubaDataSample sample;
34+
sample.setTimeColumnNow();
35+
36+
// Core request information
37+
sample.addNormalValue("method", method);
38+
sample.addNormalValue("usr", usr);
39+
sample.addNormalValue("usr_type", usrTypeToString(usrType));
40+
sample.addNormalValue("result", result);
41+
sample.addNormalValue("status", statusToString(status));
42+
sample.addNormalValue("error", error);
43+
44+
// Environment information
45+
sample.addNormalValue("unixname", unixname_);
46+
sample.addNormalValue("hostmachine", hostmachine_);
47+
48+
// Timing and status
49+
sample.addIntValue("time_taken_ms", duration);
50+
sample.addIntValue("result_size", static_cast<int64_t>(result.length()));
51+
sample.addIntValue("usr_length", static_cast<int64_t>(usr.length()));
52+
53+
// Optional revision information
54+
if (revision.has_value()) {
55+
sample.addNormalValue("revision", revision.value());
56+
}
57+
58+
// Add sample to scuba
59+
scubaLogger->addSample("swift_glass_client", sample);
60+
61+
// Log to console for debugging
62+
LOG(INFO) << "Request - method: " << method
63+
<< " usr_type: " << usrTypeToString(usrType)
64+
<< " status: " << statusToString(status) << " time: " << duration
65+
<< "ms" << " result_size: " << result.length()
66+
<< (error.empty() ? "" : " error: " + error);
67+
}
68+
69+
std::string ScubaLogger::usrTypeToString(USRType type) const {
70+
switch (type) {
71+
case USRType::SWIFT:
72+
return "swift";
73+
case USRType::CPP:
74+
return "cpp";
75+
case USRType::UNKNOWN:
76+
return "unknown";
77+
default:
78+
return "invalid";
79+
}
80+
}
81+
82+
std::string ScubaLogger::statusToString(Status status) const {
83+
switch (status) {
84+
case Status::SUCCESS:
85+
return "success";
86+
case Status::FAILED:
87+
return "failed";
88+
case Status::TIMEOUT:
89+
return "timeout";
90+
case Status::NOT_FOUND:
91+
return "not_found";
92+
default:
93+
return "invalid";
94+
}
95+
}
96+
97+
} // namespace facebook::glean::swift

0 commit comments

Comments
 (0)