From f7a9d42592bc980dee9aa0ee525d93a599cf286d Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 27 Jun 2017 22:29:00 -0400 Subject: logging: Add WebService as a log cateogry. --- src/common/logging/backend.cpp | 3 ++- src/common/logging/log.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 0e4b85a76..4b83eeb28 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -73,7 +73,8 @@ namespace Log { SUB(Audio, Sink) \ CLS(Input) \ CLS(Network) \ - CLS(Loader) + CLS(Loader) \ + CLS(WebService) // GetClassName is a macro defined by Windows.h, grrr... const char* GetLogClassName(Class log_class) { diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 8f13b80b3..fe4dfed69 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -91,6 +91,7 @@ enum class Class : ClassType { Loader, ///< ROM loader Input, ///< Input emulation Network, ///< Network emulation + WebService, ///< Interface to Citra Web Services Count ///< Total number of logging classes }; -- cgit v1.2.3 From 8abf808854e92af5f656744d582fbc7830eb68e2 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 27 Jun 2017 22:46:52 -0400 Subject: settings: Add telemetry endpoint URL. --- src/citra/config.cpp | 4 ++++ src/citra/default_ini.h | 4 ++++ src/citra_qt/configuration/config.cpp | 12 ++++++++++++ src/core/settings.h | 3 +++ 4 files changed, 23 insertions(+) (limited to 'src') diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 957d8dc86..69247b166 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -151,6 +151,10 @@ void Config::ReadValues() { Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); Settings::values.gdbstub_port = static_cast(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); + + // Web Service + Settings::values.telemetry_endpoint_url = sdl2_config->Get( + "WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry"); } void Config::Reload() { diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index d8a8fe44f..a12498e0f 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -168,5 +168,9 @@ log_filter = *:Info # Port for listening to GDB connections. use_gdbstub=false gdbstub_port=24689 + +[WebService] +# Endpoint URL for submitting telemetry data +telemetry_endpoint_url = )"; } diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 64ffc9152..40142b6d9 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -133,6 +133,13 @@ void Config::ReadValues() { Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); qt_config->endGroup(); + qt_config->beginGroup("WebService"); + Settings::values.telemetry_endpoint_url = + qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry") + .toString() + .toStdString(); + qt_config->endGroup(); + qt_config->beginGroup("UI"); qt_config->beginGroup("UILayout"); @@ -268,6 +275,11 @@ void Config::SaveValues() { qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); qt_config->endGroup(); + qt_config->beginGroup("WebService"); + qt_config->setValue("telemetry_endpoint_url", + QString::fromStdString(Settings::values.telemetry_endpoint_url)); + qt_config->endGroup(); + qt_config->beginGroup("UI"); qt_config->beginGroup("UILayout"); diff --git a/src/core/settings.h b/src/core/settings.h index 03c64c94c..ee16bb90a 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -126,6 +126,9 @@ struct Values { // Debugging bool use_gdbstub; u16 gdbstub_port; + + // WebService + std::string telemetry_endpoint_url; } extern values; // a special value for Values::region_value indicating that citra will automatically select a region -- cgit v1.2.3 From 52fbe1e10cffcfd4c4ee1c50b7c8e98f3eb4cb50 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 27 Jun 2017 23:01:49 -0400 Subject: web_service: Add skeleton project. --- src/CMakeLists.txt | 1 + src/core/CMakeLists.txt | 2 +- src/web_service/CMakeLists.txt | 14 ++++++++++++++ src/web_service/telemetry_json.cpp | 9 +++++++++ src/web_service/telemetry_json.h | 9 +++++++++ src/web_service/web_backend.cpp | 9 +++++++++ src/web_service/web_backend.h | 9 +++++++++ 7 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/web_service/CMakeLists.txt create mode 100644 src/web_service/telemetry_json.cpp create mode 100644 src/web_service/telemetry_json.h create mode 100644 src/web_service/web_backend.cpp create mode 100644 src/web_service/web_backend.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 655bd83aa..f855a5195 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,3 +14,4 @@ endif() if (ENABLE_QT) add_subdirectory(citra_qt) endif() +add_subdirectory(web_service) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ea09819e5..72233877b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -386,5 +386,5 @@ set(HEADERS create_directory_groups(${SRCS} ${HEADERS}) add_library(core STATIC ${SRCS} ${HEADERS}) -target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) +target_link_libraries(core PUBLIC common PRIVATE audio_core video_core web_service) target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt) diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt new file mode 100644 index 000000000..334d82a8a --- /dev/null +++ b/src/web_service/CMakeLists.txt @@ -0,0 +1,14 @@ +set(SRCS + telemetry_json.cpp + web_backend.cpp + ) + +set(HEADERS + telemetry_json.h + web_backend.h + ) + +create_directory_groups(${SRCS} ${HEADERS}) + +add_library(web_service STATIC ${SRCS} ${HEADERS}) +target_link_libraries(web_service PUBLIC common cpr json-headers) diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp new file mode 100644 index 000000000..68e092699 --- /dev/null +++ b/src/web_service/telemetry_json.cpp @@ -0,0 +1,9 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "web_service/telemetry_json.h" + +namespace WebService { + +} // namespace WebService diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h new file mode 100644 index 000000000..1f2e08f54 --- /dev/null +++ b/src/web_service/telemetry_json.h @@ -0,0 +1,9 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace WebService { + +} // namespace WebService diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp new file mode 100644 index 000000000..c7bd8a38a --- /dev/null +++ b/src/web_service/web_backend.cpp @@ -0,0 +1,9 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "web_service/web_backend.h" + +namespace WebService { + +} // namespace WebService diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h new file mode 100644 index 000000000..1f2e08f54 --- /dev/null +++ b/src/web_service/web_backend.h @@ -0,0 +1,9 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace WebService { + +} // namespace WebService -- cgit v1.2.3 From a634efa40e988d86ae21d86ab1a93e062614fd0b Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 27 Jun 2017 23:18:52 -0400 Subject: web_backend: Add initial interface to POST data to Citra Web Services. --- src/web_service/web_backend.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ src/web_service/web_backend.h | 22 ++++++++++++++++++++++ 2 files changed, 63 insertions(+) (limited to 'src') diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index c7bd8a38a..6d5470157 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp @@ -2,8 +2,49 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include +#include "common/logging/log.h" #include "web_service/web_backend.h" namespace WebService { +static constexpr char ENV_VAR_USERNAME[]{"CITRA_WEB_SERVICES_USERNAME"}; +static constexpr char ENV_VAR_TOKEN[]{"CITRA_WEB_SERVICES_TOKEN"}; + +static std::string GetEnvironmentVariable(const char* name) { + const char* value{getenv(name)}; + if (value) { + return value; + } + return {}; +} + +const std::string& GetUsername() { + static const std::string username{GetEnvironmentVariable(ENV_VAR_USERNAME)}; + return username; +} + +const std::string& GetToken() { + static const std::string token{GetEnvironmentVariable(ENV_VAR_TOKEN)}; + return token; +} + +void PostJson(const std::string& url, const std::string& data) { + if (url.empty()) { + LOG_ERROR(WebService, "URL is invalid"); + return; + } + + if (GetUsername().empty() || GetToken().empty()) { + LOG_ERROR(WebService, "Environment variables %s and %s must be set to POST JSON", + ENV_VAR_USERNAME, ENV_VAR_TOKEN); + return; + } + + cpr::PostAsync(cpr::Url{url}, cpr::Body{data}, cpr::Header{{"Content-Type", "application/json"}, + {"x-username", GetUsername()}, + {"x-token", GetToken()}}); +} + } // namespace WebService diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h index 1f2e08f54..2753d3b68 100644 --- a/src/web_service/web_backend.h +++ b/src/web_service/web_backend.h @@ -4,6 +4,28 @@ #pragma once +#include +#include "common/common_types.h" + namespace WebService { +/** + * Gets the current username for accessing services.citra-emu.org. + * @returns Username as a string, empty if not set. + */ +const std::string& GetUsername(); + +/** + * Gets the current token for accessing services.citra-emu.org. + * @returns Token as a string, empty if not set. + */ +const std::string& GetToken(); + +/** + * Posts JSON to services.citra-emu.org. + * @param url URL of the services.citra-emu.org endpoint to post data to. + * @param data String of JSON data to use for the body of the POST request. + */ +void PostJson(const std::string& url, const std::string& data); + } // namespace WebService -- cgit v1.2.3 From fd3b4451eccbf0dfdc2597f1a68e98763c17f207 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 9 Jul 2017 13:48:47 -0400 Subject: web_service: Implement JSON serialization of telemetry data. --- src/web_service/telemetry_json.cpp | 80 ++++++++++++++++++++++++++++++++++++++ src/web_service/telemetry_json.h | 45 +++++++++++++++++++++ 2 files changed, 125 insertions(+) (limited to 'src') diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp index 68e092699..c61522ff1 100644 --- a/src/web_service/telemetry_json.cpp +++ b/src/web_service/telemetry_json.cpp @@ -2,8 +2,88 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/assert.h" +#include "core/settings.h" #include "web_service/telemetry_json.h" +#include "web_service/web_backend.h" namespace WebService { +TelemetryJson::TelemetryJson() {} + +template +void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) { + sections[static_cast(type)][name] = value; +} + +void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) { + TopSection()[name] = sections[static_cast(type)]; +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), std::string(field.GetValue())); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + Serialize(field.GetType(), field.GetName(), field.GetValue().count()); +} + +void TelemetryJson::Complete() { + SerializeSection(Telemetry::FieldType::App, "App"); + SerializeSection(Telemetry::FieldType::Session, "Session"); + SerializeSection(Telemetry::FieldType::Performance, "Performance"); + SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); + SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); + SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); + PostJson(Settings::values.telemetry_endpoint_url, TopSection().dump()); +} + } // namespace WebService diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h index 1f2e08f54..cc511cdd1 100644 --- a/src/web_service/telemetry_json.h +++ b/src/web_service/telemetry_json.h @@ -4,6 +4,51 @@ #pragma once +#include +#include +#include +#include "common/telemetry.h" + namespace WebService { +/** + * Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the + * Citra web service + */ +class TelemetryJson : public Telemetry::VisitorInterface { +public: + TelemetryJson(); + ~TelemetryJson() = default; + + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + void Visit(const Telemetry::Field& field) override; + + void Complete() override; + +private: + nlohmann::json& TopSection() { + return sections[static_cast(Telemetry::FieldType::None)]; + } + + template + void Serialize(Telemetry::FieldType type, const std::string& name, T value); + + void SerializeSection(Telemetry::FieldType type, const std::string& name); + + nlohmann::json output; + std::array sections; +}; + } // namespace WebService -- cgit v1.2.3 From 8af3ebb149b057b40bc4efd5bae2b9cb70b2066c Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 9 Jul 2017 13:49:51 -0400 Subject: telemetry_session: Use TelemetryJson to submit real telemetry. --- src/core/telemetry_session.cpp | 4 ++-- src/web_service/telemetry_json.cpp | 2 -- src/web_service/telemetry_json.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index ddc8b262e..1ba0a698d 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -6,12 +6,12 @@ #include "common/scm_rev.h" #include "core/telemetry_session.h" +#include "web_services/telemetry_json.h" namespace Core { TelemetrySession::TelemetrySession() { - // TODO(bunnei): Replace with a backend that logs to our web service - backend = std::make_unique(); + backend = std::make_unique(); // Log one-time session start information const auto duration{std::chrono::steady_clock::now().time_since_epoch()}; diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp index c61522ff1..a2d007e77 100644 --- a/src/web_service/telemetry_json.cpp +++ b/src/web_service/telemetry_json.cpp @@ -9,8 +9,6 @@ namespace WebService { -TelemetryJson::TelemetryJson() {} - template void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) { sections[static_cast(type)][name] = value; diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h index cc511cdd1..39038b4f9 100644 --- a/src/web_service/telemetry_json.h +++ b/src/web_service/telemetry_json.h @@ -17,7 +17,7 @@ namespace WebService { */ class TelemetryJson : public Telemetry::VisitorInterface { public: - TelemetryJson(); + TelemetryJson() = default; ~TelemetryJson() = default; void Visit(const Telemetry::Field& field) override; -- cgit v1.2.3 From 33b012e86b846bbba1a42193cbaf34fa16b8fb93 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 9 Jul 2017 17:52:18 -0400 Subject: web_service: Add CMake flag to enable. --- src/CMakeLists.txt | 4 +++- src/core/CMakeLists.txt | 5 ++++- src/core/telemetry_session.cpp | 10 ++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f855a5195..e11940f59 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,4 +14,6 @@ endif() if (ENABLE_QT) add_subdirectory(citra_qt) endif() -add_subdirectory(web_service) +if (ENABLE_WEB_SERVICE) + add_subdirectory(web_service) +endif() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 72233877b..b80efe192 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -386,5 +386,8 @@ set(HEADERS create_directory_groups(${SRCS} ${HEADERS}) add_library(core STATIC ${SRCS} ${HEADERS}) -target_link_libraries(core PUBLIC common PRIVATE audio_core video_core web_service) +target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt) +if (ENABLE_WEB_SERVICE) + target_link_libraries(core PUBLIC json-headers web_service) +endif() diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 1ba0a698d..70eff4340 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -6,13 +6,19 @@ #include "common/scm_rev.h" #include "core/telemetry_session.h" -#include "web_services/telemetry_json.h" + +#ifdef ENABLE_WEB_SERVICE +#include "web_service/telemetry_json.h" +#endif namespace Core { TelemetrySession::TelemetrySession() { +#ifdef ENABLE_WEB_SERVICE backend = std::make_unique(); - +#else + backend = std::make_unique(); +#endif // Log one-time session start information const auto duration{std::chrono::steady_clock::now().time_since_epoch()}; const auto start_time{std::chrono::duration_cast(duration).count()}; -- cgit v1.2.3 From 28c35756790bfad06e339b82c551ab521268561b Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 9 Jul 2017 18:37:14 -0400 Subject: web_backend: Specify api-version on JSON post. --- src/web_service/web_backend.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index 6d5470157..13e4555ac 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp @@ -9,6 +9,7 @@ namespace WebService { +static constexpr char API_VERSION[]{"1"}; static constexpr char ENV_VAR_USERNAME[]{"CITRA_WEB_SERVICES_USERNAME"}; static constexpr char ENV_VAR_TOKEN[]{"CITRA_WEB_SERVICES_TOKEN"}; @@ -44,7 +45,8 @@ void PostJson(const std::string& url, const std::string& data) { cpr::PostAsync(cpr::Url{url}, cpr::Body{data}, cpr::Header{{"Content-Type", "application/json"}, {"x-username", GetUsername()}, - {"x-token", GetToken()}}); + {"x-token", GetToken()}, + {"api-version", API_VERSION}}); } } // namespace WebService -- cgit v1.2.3