14. 公共信息获取

大家好,我是程序喵。


上一章节中,我介绍了项目中上报模块的实现,本节我们来点轻松的,介绍下如何获取公共信息。


为什么要获取公共信息?


上报每一条埋点数据时,我们其实最好还是需要拼接一些其他公共信息和系统信息,这样才方便我们排查问题。


具体要获取的信息字段,我其实在前面章节中有过介绍,这里再简单贴一下:

这其中很多有一些字段需要用户配置进来,但也有些字段需要我们内部自己获取。


比如:

  1. system_version
  2. device_name
  3. device_id
  4. buried_version
  5. lifecycle_id
  6. timestamp
  7. process_time


本节我就主要介绍如何获取和生成这些字段值。


system_version

由于我们这个项目是针对Windows平台,所以需要获取Windows的系统版本,可以直接调用系统的API,详见代码:

static std::string GetSystemVersion() {
OSVERSIONINFOEXA os_version_info;
ZeroMemory(&os_version_info, sizeof(OSVERSIONINFOEXA));
os_version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA);
::GetVersionExA(reinterpret_cast<OSVERSIONINFOA*>(&os_version_info));
std::string system_version =
std::to_string(os_version_info.dwMajorVersion) + "." +
std::to_string(os_version_info.dwMinorVersion) + "." +
std::to_string(os_version_info.dwBuildNumber);
return system_version;
}

通过GetVersionExA方法给os_version_info赋值,然后将major、minor、build三个字段拼接即可。


device_name

device_name和system_version类似,都和操作系统强相关,这里也需要使用Windows API获取:

static std::string GetDeviceName() {
char buf[1024] = {0};
DWORD buf_size = sizeof(buf);
::GetComputerNameA(buf, &buf_size);
std::string device_name = buf;
return device_name;
}


device_id

device_id作为设备唯一的ID,它稍微麻烦一些,需要我们先生成一个随机数,作为设备ID,存到设备的某个固定的位置,后面每次都从这个位置读取,这样才可以保证重启程序或者重启设备后,获取到的device_id是一致的。

static std::string GetDeviceId() {
static constexpr auto kDeviceIdKey = "device_id";
static std::string device_id = ReadRegister(kDeviceIdKey);
if (device_id.empty()) {
device_id = CommonService::GetRandomId();
WriteRegister(kDeviceIdKey, device_id);
}
return device_id;
}


存到哪里呢?Windows下我们可以存到注册表里,正常用户一般不会查看和改动注册表,这里还涉及到注册表相关的编码:

static void WriteRegister(const std::string& key, const std::string& value) {
HKEY h_key;
LONG ret = ::RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\\\Buried", 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&h_key, NULL);
if (ret != ERROR_SUCCESS) {
return;
}
ret = ::RegSetValueExA(h_key, key.c_str(), 0, REG_SZ,
reinterpret_cast<const BYTE*>(value.c_str()),
value.size());
if (ret != ERROR_SUCCESS) {
return;
}
::RegCloseKey(h_key);
}

static std::string ReadRegister(const std::string& key) {
HKEY h_key;
LONG ret = ::RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\\\Buried", 0,
KEY_ALL_ACCESS, &h_key);
if (ret != ERROR_SUCCESS) {
return "";
}
char buf[1024] = {0};
DWORD buf_size = sizeof(buf);
ret = ::RegQueryValueExA(h_key, key.c_str(), NULL, NULL,
reinterpret_cast<BYTE*>(buf), &buf_size);
if (ret != ERROR_SUCCESS) {
return "";
}
::RegCloseKey(h_key);
return buf;
}


代码中可以看到,我们将相关数据存到了HKEY_CURRENT_USER\Software\Buried下面。


写完代码运行后,我们也可以打开Windows的注册表,查看是否存储成功。




buried_version

用这个version表示SDK的版本号,这个版本号我们通过CMake生成,可以看到项目src目录下有个buried_config.h.in文件,内容如下:

#pragma once

#define PROJECT_NAME "@PROJECT_NAME@"
#define PROJECT_VER "@PROJECT_VERSION@"


那如何使用这个文件呢?再看src目录下的CMakelists.txt文件,有一行这样的代码:

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/buried_config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/buried_config.h)


根目录的CMakeLists.txt就设置了项目的PROJECT_NAME和VERSION:

cmake_minimum_required(VERSION 3.20)

set(PROJECT_NAME "BuriedPoint")

project(${PROJECT_NAME} VERSION 1.1.1.1)


这样通过cmake编译后,就会自动生成build_config.h的头文件,我们只需include这个头文件,就可以获取buried_version:

buried_version = PROJECT_VER;


lifecycle_id

标识某一进程的id,这个好弄,一个static的随机数即可:

static std::string GetLifeCycleId() {
static std::string life_cycle_id = CommonService::GetRandomId();
return life_cycle_id;
}

std::string CommonService::GetRandomId() {
static constexpr size_t len = 32;
static constexpr auto chars =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
static std::mt19937_64 rng{std::random_device{}()};
static std::uniform_int_distribution<size_t> dist{0, 60};
std::string result;
result.reserve(len);
std::generate_n(std::back_inserter(result), len,
[&]() { return chars[dist(rng)]; });
return result;
}


process_time

获取进程的运行时间直接调用Windows的API方法即可,不过Windows的GetProcessTimes获取出来的时间是FILETIME,还需要我们自己转成SystemTime才可以,具体可以看下面的代码:

std::string CommonService::GetProcessTime() {
DWORD pid = ::GetCurrentProcessId();
HANDLE h_process =
::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (h_process == NULL) {
return "";
}
FILETIME create_time;
FILETIME exit_time;
FILETIME kernel_time;
FILETIME user_time;
BOOL ret = ::GetProcessTimes(h_process, &create_time, &exit_time,
&kernel_time, &user_time);
::CloseHandle(h_process);
if (ret == 0) {
return "";
}

FILETIME create_local_time;
::FileTimeToLocalFileTime(&create_time, &create_local_time);

SYSTEMTIME create_sys_time;
::FileTimeToSystemTime(&create_local_time, &create_sys_time);
char buf[128] = {0};
// year month day hour minute second millisecond
sprintf_s(buf, "%04d-%02d-%02d %02d:%02d:%02d.%03d", create_sys_time.wYear,
create_sys_time.wMonth, create_sys_time.wDay, create_sys_time.wHour,
create_sys_time.wMinute, create_sys_time.wSecond,
create_sys_time.wMilliseconds);
return buf;
}


有了这些公共信息后,上报的埋点才更有意义,更方便我们排查问题。

到这里,整个公共信息模块已经介绍完毕,下期见。

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
程序喵
下载 APP