167 lines
5.0 KiB
Plaintext
167 lines
5.0 KiB
Plaintext
#include <atomic>
|
||
#include <chrono>
|
||
#include <csignal>
|
||
#include <fstream>
|
||
#include <iostream>
|
||
#include <sys/stat.h>
|
||
#include <thread>
|
||
|
||
#include <ctime> // 添加:用于localtime_r和时间函数
|
||
#include <iomanip> // 添加:用于std::setw和std::setfill
|
||
#include <sstream> // 添加:用于std::ostringstream
|
||
|
||
std::atomic<bool> stop_logging(false);
|
||
|
||
void signalHandler(int signum) { stop_logging = true; }
|
||
|
||
class HighPrecisionTimeLogger {
|
||
private:
|
||
std::ofstream log_file;
|
||
int log_interval_ms;
|
||
std::string filename;
|
||
uint64_t last_timestamp_us;
|
||
uint64_t second_last_timestamp_us;
|
||
bool selective_logging;
|
||
bool in_jump_mode;
|
||
int jump_remaining;
|
||
|
||
// 获取当前时间戳(微秒)
|
||
uint64_t getCurrentTimestampUs() {
|
||
auto now = std::chrono::system_clock::now();
|
||
auto duration = now.time_since_epoch();
|
||
return std::chrono::duration_cast<std::chrono::microseconds>(duration)
|
||
.count();
|
||
}
|
||
|
||
// 格式化时间戳为可读字符串
|
||
std::string formatTimestamp(uint64_t timestamp_us) {
|
||
time_t seconds = timestamp_us / 1000000;
|
||
suseconds_t microseconds = timestamp_us % 1000000;
|
||
struct tm tm_info;
|
||
localtime_r(&seconds, &tm_info);
|
||
|
||
char buffer[64];
|
||
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm_info);
|
||
|
||
std::ostringstream oss;
|
||
oss << buffer << "." << std::setw(6) << std::setfill('0') << microseconds;
|
||
return oss.str();
|
||
}
|
||
|
||
// 检查是否需要日志轮转
|
||
void checkLogRotation() {
|
||
struct stat stat_buf;
|
||
if (stat(filename.c_str(), &stat_buf) == 0) {
|
||
// 如果文件超过50MB,进行轮转
|
||
if (static_cast<size_t>(stat_buf.st_size) >= 50 * 1024 * 1024) {
|
||
log_file.close();
|
||
std::string old_filename = filename + ".old";
|
||
rename(filename.c_str(), old_filename.c_str());
|
||
log_file.open(filename, std::ios::out);
|
||
}
|
||
}
|
||
}
|
||
|
||
public:
|
||
HighPrecisionTimeLogger(const std::string &fname = "timestamps.log",
|
||
int interval_ms = 50, bool selective = true)
|
||
: filename(fname), log_interval_ms(interval_ms),
|
||
selective_logging(selective), last_timestamp_us(0),
|
||
second_last_timestamp_us(0), in_jump_mode(false), jump_remaining(0) {
|
||
log_file.open(filename, std::ios::out);
|
||
if (!log_file.is_open()) {
|
||
throw std::runtime_error("无法打开日志文件");
|
||
}
|
||
// 写入表头
|
||
log_file << "timestamp,formatted_time,time_jump_detected,interval_us,note"
|
||
<< std::endl;
|
||
}
|
||
|
||
void startLogging() {
|
||
signal(SIGINT, signalHandler);
|
||
signal(SIGTERM, signalHandler);
|
||
|
||
std::cout << "开始记录时间戳,间隔: " << log_interval_ms << "ms"
|
||
<< std::endl;
|
||
std::cout << "按Ctrl+C停止记录" << std::endl;
|
||
|
||
auto next_log_time = std::chrono::steady_clock::now();
|
||
uint64_t expected_interval_us = log_interval_ms * 1000;
|
||
|
||
while (!stop_logging) {
|
||
uint64_t current_timestamp_us = getCurrentTimestampUs();
|
||
std::string formatted_time = formatTimestamp(current_timestamp_us);
|
||
|
||
bool time_jump_detected = false;
|
||
std::string note = "";
|
||
|
||
// 时间跳变检测逻辑
|
||
if (last_timestamp_us != 0 && second_last_timestamp_us != 0) {
|
||
// 检测时间回退
|
||
if (current_timestamp_us < last_timestamp_us) {
|
||
time_jump_detected = true;
|
||
note = "TIME_REGESSION";
|
||
}
|
||
// 检测异常大的间隔(超过预期1.5倍)
|
||
else {
|
||
uint64_t actual_interval_us =
|
||
current_timestamp_us - second_last_timestamp_us;
|
||
uint64_t threshold_us = expected_interval_us * 1.5;
|
||
|
||
if (actual_interval_us > threshold_us) {
|
||
time_jump_detected = true;
|
||
note = "LONG_INTERVAL";
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新历史时间戳
|
||
if (last_timestamp_us != 0) {
|
||
second_last_timestamp_us = last_timestamp_us;
|
||
}
|
||
last_timestamp_us = current_timestamp_us;
|
||
|
||
// 记录到文件
|
||
uint64_t interval =
|
||
(second_last_timestamp_us == 0)
|
||
? 0
|
||
: (current_timestamp_us - second_last_timestamp_us);
|
||
|
||
log_file << current_timestamp_us << "," << formatted_time << ","
|
||
<< (time_jump_detected ? "YES" : "NO") << "," << interval << ","
|
||
<< note << std::endl;
|
||
|
||
// 检查日志轮转
|
||
checkLogRotation();
|
||
|
||
// 计算下一个日志时间点
|
||
next_log_time += std::chrono::milliseconds(log_interval_ms);
|
||
std::this_thread::sleep_until(next_log_time);
|
||
}
|
||
|
||
log_file.close();
|
||
std::cout << "记录已停止,数据保存在: " << filename << std::endl;
|
||
}
|
||
};
|
||
|
||
int main(int argc, char *argv[]) {
|
||
std::string filename = "time_log.csv";
|
||
int interval_ms = 50;
|
||
|
||
if (argc >= 2) {
|
||
interval_ms = std::stoi(argv[1]);
|
||
}
|
||
if (argc >= 3) {
|
||
filename = argv[2];
|
||
}
|
||
|
||
try {
|
||
HighPrecisionTimeLogger logger(filename, interval_ms);
|
||
logger.startLogging();
|
||
} catch (const std::exception &e) {
|
||
std::cerr << "错误: " << e.what() << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
} |