Files
whetstone_DSL/editor/src/Telemetry.h
2026-02-09 19:56:55 -07:00

84 lines
2.3 KiB
C++

#pragma once
// Step 163: Telemetry + crash reporting (opt-in)
#include <string>
#include <fstream>
#include <sstream>
#include <chrono>
#include <ctime>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
class Telemetry {
public:
void initialize(const std::string& root, bool optIn) {
workspaceRoot_ = root;
optIn_ = optIn;
if (!workspaceRoot_.empty()) {
telemetryPath_ = workspaceRoot_ + "/telemetry.log";
crashPath_ = workspaceRoot_ + "/crash.log";
}
if (optIn_) {
recordEvent("session_start", json::object());
}
}
void setOptIn(bool optIn) { optIn_ = optIn; }
bool isOptedIn() const { return optIn_; }
void recordEvent(const std::string& name, const json& fields) {
if (!optIn_) return;
if (telemetryPath_.empty()) return;
json payload = fields;
payload["event"] = name;
payload["timestamp"] = currentTimestamp();
std::ofstream out(telemetryPath_, std::ios::app);
if (out.is_open()) {
out << payload.dump() << "\n";
}
}
void installCrashHandler() {
if (crashPath_.empty()) return;
crashInstance_ = this;
std::set_terminate(&Telemetry::onTerminate);
}
const std::string& telemetryPath() const { return telemetryPath_; }
const std::string& crashPath() const { return crashPath_; }
private:
static std::string currentTimestamp() {
auto now = std::chrono::system_clock::now();
std::time_t tt = std::chrono::system_clock::to_time_t(now);
std::tm tm{};
#ifdef _WIN32
localtime_s(&tm, &tt);
#else
localtime_r(&tt, &tm);
#endif
char buf[32];
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
return buf;
}
std::string workspaceRoot_;
std::string telemetryPath_;
std::string crashPath_;
bool optIn_ = false;
inline static Telemetry* crashInstance_ = nullptr;
static void onTerminate() {
if (crashInstance_ && !crashInstance_->crashPath_.empty()) {
std::ofstream out(crashInstance_->crashPath_, std::ios::app);
if (out.is_open()) {
out << "[crash] terminate called at "
<< currentTimestamp() << "\n";
}
}
std::abort();
}
};