You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++如何按第二列sessionid合并邮件日志并过滤不完整会话

邮件日志会话合并实现

问题背景

现有email.log邮件日志文件,截取的日志样例如下:

2021-04-30T23:55:00.127629  886715E6D6C9D4FB    status=rejected
2021-04-30T23:55:00.791921  F8F63278A6A3AD87    from=<sarah.smith@example.com>
2021-04-30T23:55:01.470432  418512384DDDD2C6    from=<robert.rodriguez@example.com>
2021-04-30T23:55:01.697902  0D8760D4ADAB456D    message-id=<696b66ea-f493-4ba2-9e8a-553bd03b7d37@2PPZOR2ULU>
2021-04-30T23:55:01.736492  0D8760D4ADAB456D    from=<william.smith@example.com>
2021-04-30T23:55:02.043100  0D8760D4ADAB456D    to=<william.davis@example.com>
2021-04-30T23:55:02.842802  EC5AD35BADC381F9    client=10.2.38.215
2021-04-30T23:55:03.132660  2AB0E95297136E70    client=2001:db8::8f75:20e2:c47f
2021-04-30T23:55:03.550296  BAB22895DB867DFF    status=sent
2021-04-30T23:55:04.392986  5BE423F6370D1D1B    client=10.38.217.222
2021-04-30T23:55:04.661467  5D11914582F8C85A    client=2001:db8::7bbb:6743:c8c5
2021-04-30T23:55:05.306358  E2E8D917BB751176    message-id=<da3d4d7c-643c-4a15-989a-3cd6269030f4@30Q7E75V7B>
2021-04-30T23:55:05.872830  F8F63278A6A3AD87    to=<patricia.garcia@example.com>
2021-04-30T23:55:06.272336  F8F63278A6A3AD87    status=sent
2021-04-30T23:55:06.716495  C7CC8201A67C8E52    from=<thomas.wilson@example.com>
2021-04-30T23:55:07.056882  5BE423F6370D1D1B    message-id=<d5db8bc8-871e-48de-9e64-e23fca0c0134@56WZW5K1C2>
2021-04-30T23:55:07.113379  0D8760D4ADAB456D    status=sent
2021-04-30T23:55:07.370491  5D11914582F8C85A    message-id=<b041aedf-07ec-4e67-9a1a-e9f23a6ab434@82AZ76YJPI>
2021-04-30T23:55:07.732459  2AB0E95297136E70    message-id=<c948f7eb-144c-4688-b122-1191eea2cb29@4Q1QAJ0BLI>
2021-04-30T23:55:08.608998  C1D805D68377D513    to=<karen.smith@example.com>
2021-04-30T23:55:08.782778  418512384DDDD2C6    to=<jessica.jones@example.com>
2021-04-30T23:55:09.383173  E2E8D917BB751176    from=<barbara.rodriguez@example.com>
2021-04-30T23:55:09.676896  33DD2B20F2AB9262    status=sent
2021-04-30T23:55:10.157677  452FAD67C2867C47    client=10.38.217.222
2021-04-30T23:55:11.064902  C7CC8201A67C8E52    to=<patricia.jones@example.com>
2021-04-30T23:55:11.709673  E2E8D917BB751176    to=<richard.garcia@example.com>
2021-04-30T23:55:12.667447  EC5AD35BADC381F9    message-id=<b2fd4dac-2513-4895-ac84-3c68ecabc3ec@U83K85L7HK>

当前已实现日志读取到Session结构体向量的逻辑,需要实现以下需求:

  • 按第二列的sessionid字段合并同一会话的所有事件
  • 兼容日志事件并行、时间重叠的场景
  • 缺少任意必填字段的不完整会话直接忽略

已编写的初始代码如下:

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>

using namespace std;


struct Session
{
    string time;
    string sessionid;
    string other;
    friend istream& operator>>(istream& input, Session& session);
    friend ostream& operator<<(ostream& output, Session& session);
};

istream& operator>>(istream& input, Session& session)
{
  input >> session.time;
  input >> session.sessionid;
  input >> session.other;
  return input;
}

ostream& operator<<(ostream& output, Session& session)
{
  output << session.time;
  output << session.sessionid;
  output << session.other;
  return output;
}

int main()
{
  vector<Session> session;
  vector<Session> complete_session;
  Session record;

  ifstream read("email.log");
  while (read >> record)
  {
    session.push_back(record);
  }
  read.close();

  return 0;
} 

实现思路

  • 重构Session结构体:原结构用单一other字段存储所有属性无法做字段校验,拆分出邮件会话的核心字段:客户端IP、message-id、发件人、收件人、投递状态,同时保留会话最早时间、sessionid两个基础字段。
  • 哈希表聚合:用unordered_map<string, Session>做中间聚合容器,键为sessionid,值为对应会话的聚合对象。遍历所有日志行时,解析每行第三列的键值对,填充到对应sessionid的Session对象中,自动覆盖同字段重复值(日志顺序中后出现的同字段值为准,符合日志写入逻辑)。
  • 完整性校验:所有日志行遍历完成后,遍历哈希表中的所有会话,校验5个核心字段是否全部非空,非空则为完整会话,加入结果向量;缺任意字段则直接丢弃。

完整实现代码

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <unordered_map>

using namespace std;

struct Session
{
    string first_time; // 会话最早出现的时间
    string sessionid;
    // 核心必填字段
    string client;
    string message_id;
    string from;
    string to;
    string status;

    // 校验是否为完整会话
    bool is_complete() const {
        return !client.empty() && !message_id.empty() && !from.empty() 
            && !to.empty() && !status.empty();
    }
};

// 解析单行日志为临时结构,只做基础字段拆分
struct LogLine {
    string time;
    string sessionid;
    string key;
    string value;

    friend istream& operator>>(istream& input, LogLine& line) {
        string kv;
        if (!(input >> line.time >> line.sessionid >> kv)) {
            return input;
        }
        // 拆分key=value
        size_t eq_pos = kv.find('=');
        if (eq_pos == string::npos) {
            input.setstate(ios::failbit);
            return input;
        }
        line.key = kv.substr(0, eq_pos);
        line.value = kv.substr(eq_pos + 1);
        return input;
    }
};

int main()
{
    vector<Session> complete_session;
    unordered_map<string, Session> session_map;
    LogLine line;

    ifstream read("email.log");
    while (read >> line)
    {
        Session& s = session_map[line.sessionid];
        // 新会话初始化基础字段
        if (s.sessionid.empty()) {
            s.sessionid = line.sessionid;
            s.first_time = line.time;
        }
        // 根据key填充对应字段
        if (line.key == "client") {
            s.client = line.value;
        } else if (line.key == "message-id") {
            s.message_id = line.value;
        } else if (line.key == "from") {
            s.from = line.value;
        } else if (line.key == "to") {
            s.to = line.value;
        } else if (line.key == "status") {
            s.status = line.value;
        }
    }
    read.close();

    // 筛选完整会话
    for (auto& pair : session_map) {
        if (pair.second.is_complete()) {
            complete_session.push_back(pair.second);
        }
    }

    // 测试输出,可按需删除
    for (auto& s : complete_session) {
        cout << "完整会话: " << s.sessionid << endl;
        cout << "时间: " << s.first_time << endl;
        cout << "客户端: " << s.client << endl;
        cout << "Message-ID: " << s.message_id << endl;
        cout << "发件人: " << s.from << endl;
        cout << "收件人: " << s.to << endl;
        cout << "状态: " << s.status << endl << endl;
    }

    return 0;
}

说明

  • 该实现不依赖日志顺序,无论同sessionid的日志行是否连续、是否和其他会话日志交叉出现,都能正确聚合,适配并行会话、时间重叠的场景。
  • 字段解析逻辑可按需扩展,如果后续新增其他必填字段,只需要在Session结构体中新增对应字段、在解析分支增加赋值逻辑、在is_complete方法中增加非空判断即可。
  • 样例日志运行后,会输出0D8760D4ADAB456D、F8F63278A6A3AD87两个完整会话,其余缺字段的会话会被自动过滤。

内容的提问来源于stack exchange,提问作者Dominic Ask

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 06:57:19