Skip to content

Commit 0e5bb04

Browse files
committed
feat: support openai-api parse
1 parent 44204e1 commit 0e5bb04

13 files changed

Lines changed: 3623 additions & 206 deletions

File tree

Binary file not shown.
199 KB
Binary file not shown.
Binary file not shown.
48.6 KB
Binary file not shown.

agent/src/config/config.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2028,6 +2028,86 @@ impl Default for InferenceWhitelist {
20282028
}
20292029
}
20302030

2031+
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq)]
2032+
#[serde(default)]
2033+
pub struct OpenAIBizDimExtractor {
2034+
pub headers: Vec<String>,
2035+
pub json_paths: Vec<String>,
2036+
}
2037+
2038+
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq)]
2039+
#[serde(default)]
2040+
pub struct OpenAIBizDimExtractors {
2041+
pub org_path: OpenAIBizDimExtractor,
2042+
pub user_id: OpenAIBizDimExtractor,
2043+
pub app_id: OpenAIBizDimExtractor,
2044+
}
2045+
2046+
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
2047+
#[serde(default)]
2048+
pub struct OpenAIUsageFieldPaths {
2049+
/// JSON paths (dot-notation) to read the input token count, tried in order.
2050+
pub input_tokens: Vec<String>,
2051+
/// JSON paths (dot-notation) to read the output token count, tried in order.
2052+
pub output_tokens: Vec<String>,
2053+
/// JSON paths (dot-notation) to read the total token count, tried in order.
2054+
pub total_tokens: Vec<String>,
2055+
}
2056+
2057+
impl Default for OpenAIUsageFieldPaths {
2058+
fn default() -> Self {
2059+
Self {
2060+
input_tokens: vec!["usage.prompt_tokens".to_string()],
2061+
output_tokens: vec!["usage.completion_tokens".to_string()],
2062+
total_tokens: vec!["usage.total_tokens".to_string()],
2063+
}
2064+
}
2065+
}
2066+
2067+
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
2068+
#[serde(default)]
2069+
pub struct OpenAIApiConfig {
2070+
pub enabled: bool,
2071+
pub path_prefixes: Vec<String>,
2072+
pub path_suffixes: Vec<String>,
2073+
pub request_body_max_bytes: usize,
2074+
pub response_event_max_bytes: usize,
2075+
pub sse_buffer_max_bytes: usize,
2076+
pub usage_field_paths: OpenAIUsageFieldPaths,
2077+
pub biz_dimension_extractors: OpenAIBizDimExtractors,
2078+
}
2079+
2080+
impl Default for OpenAIApiConfig {
2081+
fn default() -> Self {
2082+
Self {
2083+
enabled: false,
2084+
path_prefixes: vec![],
2085+
path_suffixes: vec![
2086+
"/v1/chat/completions".to_string(),
2087+
"/v1/responses".to_string(),
2088+
],
2089+
request_body_max_bytes: 65536,
2090+
response_event_max_bytes: 32768,
2091+
sse_buffer_max_bytes: 131072,
2092+
usage_field_paths: OpenAIUsageFieldPaths::default(),
2093+
biz_dimension_extractors: OpenAIBizDimExtractors {
2094+
org_path: OpenAIBizDimExtractor {
2095+
headers: vec!["x-org-path".to_string()],
2096+
json_paths: vec![],
2097+
},
2098+
user_id: OpenAIBizDimExtractor {
2099+
headers: vec!["x-user-id".to_string()],
2100+
json_paths: vec![],
2101+
},
2102+
app_id: OpenAIBizDimExtractor {
2103+
headers: vec!["appid".to_string()],
2104+
json_paths: vec![],
2105+
},
2106+
},
2107+
}
2108+
}
2109+
}
2110+
20312111
#[derive(Clone, Default, Debug, Deserialize, PartialEq, Eq)]
20322112
#[serde(default)]
20332113
pub struct ProtocolSpecialConfig {
@@ -2037,6 +2117,7 @@ pub struct ProtocolSpecialConfig {
20372117
pub net_sign: NetSignConfig,
20382118
pub mysql: MysqlConfig,
20392119
pub grpc: GrpcConfig,
2120+
pub openai_api: OpenAIApiConfig,
20402121
}
20412122

20422123
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]

agent/src/config/handler.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@ use super::config::{Ebpf, EbpfFileIoEvent, ProcessMatcher, SymbolTable};
5555
use super::{
5656
config::{
5757
ApiResources, Config, DpdkSource, ExtraLogFields, ExtraLogFieldsInfo, HttpEndpoint,
58-
HttpEndpointMatchRule, Iso8583ParseConfig, NetSignParseConfig, OracleConfig, PcapStream,
59-
PortConfig, ProcessorsFlowLogTunning, RequestLogTunning, SessionTimeout, TagFilterOperator,
60-
Timeouts, UserConfig, WebSphereMqParseConfig, GRPC_BUFFER_SIZE_MIN,
58+
HttpEndpointMatchRule, Iso8583ParseConfig, NetSignParseConfig, OpenAIApiConfig,
59+
OracleConfig, PcapStream, PortConfig, ProcessorsFlowLogTunning, RequestLogTunning,
60+
SessionTimeout, TagFilterOperator, Timeouts, UserConfig, WebSphereMqParseConfig,
61+
GRPC_BUFFER_SIZE_MIN,
6162
},
6263
ConfigError, KubernetesPollerType, TrafficOverflowAction,
6364
};
@@ -1205,6 +1206,7 @@ pub struct LogParserConfig {
12051206
pub unconcerned_dns_nxdomain_trie: DomainNameTrie,
12061207
pub mysql_decompress_payload: bool,
12071208
pub mysql_endpoint_disabled: bool,
1209+
pub openai_api: OpenAIApiConfig,
12081210
pub custom_app: CustomAppConfig,
12091211
}
12101212

@@ -1225,6 +1227,7 @@ impl Default for LogParserConfig {
12251227
unconcerned_dns_nxdomain_trie: DomainNameTrie::default(),
12261228
mysql_decompress_payload: true,
12271229
mysql_endpoint_disabled: true,
1230+
openai_api: OpenAIApiConfig::default(),
12281231
custom_app: CustomAppConfig::default(),
12291232
}
12301233
}
@@ -1272,6 +1275,7 @@ impl fmt::Debug for LogParserConfig {
12721275
)
12731276
.field("mysql_decompress_payload", &self.mysql_decompress_payload)
12741277
.field("mysql_endpoint_disabled", &self.mysql_endpoint_disabled)
1278+
.field("openai_api_enabled", &self.openai_api.enabled)
12751279
.field("custom_app", &self.custom_app)
12761280
.finish()
12771281
}
@@ -2382,6 +2386,13 @@ impl TryFrom<(Config, UserConfig)> for ModuleConfig {
23822386
.protocol_special_config
23832387
.mysql
23842388
.endpoint_disabled,
2389+
openai_api: conf
2390+
.processors
2391+
.request_log
2392+
.application_protocol_inference
2393+
.protocol_special_config
2394+
.openai_api
2395+
.clone(),
23852396
#[cfg(not(feature = "enterprise"))]
23862397
custom_app: CustomAppConfig::default(),
23872398
#[cfg(feature = "enterprise")]

agent/src/flow_generator/protocol_logs.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub(crate) mod dns;
1919
pub(crate) mod fastcgi;
2020
pub(crate) mod http;
2121
pub(crate) mod mq;
22+
pub(crate) mod openai_api;
2223
mod parser;
2324
pub mod pb_adapter;
2425
pub(crate) mod ping;
@@ -413,15 +414,30 @@ impl AppProtoLogsBaseInfo {
413414
}
414415

415416
// go http2 uprobe may merge multi times, if not req and resp merge can not set to session
417+
// Save whether this entry was already a Session before the merge so we can
418+
// decide whether to recompute rrt below.
419+
let was_already_session = self.head.msg_type == LogMessageType::Session;
416420
if self.head.msg_type != log.head.msg_type {
417421
self.head.msg_type = LogMessageType::Session;
418422
}
419423

420-
self.head.rrt = if self.end_time > self.start_time {
421-
(self.end_time.as_micros() - self.start_time.as_micros()) as u64
422-
} else {
423-
0
424-
};
424+
// Freeze rrt after the first req→resp merge.
425+
//
426+
// On the initial merge (Request + Response → Session), end_time equals the
427+
// first-response packet time, so `end_time - start_time` naturally gives
428+
// first-response latency — the same semantics as non-streaming HTTP.
429+
//
430+
// For multi-merge protocols (SSE streaming, Go HTTP2 uprobe), each
431+
// continuation packet would push end_time forward and make rrt equal to
432+
// the total stream duration instead. Skipping the recomputation once the
433+
// entry is already a Session preserves the first-response latency value.
434+
if !was_already_session {
435+
self.head.rrt = if self.end_time > self.start_time {
436+
(self.end_time.as_micros() - self.start_time.as_micros()) as u64
437+
} else {
438+
0
439+
};
440+
}
425441

426442
if self.biz_type == 0 {
427443
self.biz_type = log.biz_type;

agent/src/flow_generator/protocol_logs/consts.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub const HTTP_STATUS_CLIENT_ERROR_MIN: u16 = 400;
3333
pub const HTTP_STATUS_CLIENT_ERROR_MAX: u16 = 499;
3434
pub const HTTP_STATUS_SERVER_ERROR_MIN: u16 = 500;
3535
pub const HTTP_STATUS_SERVER_ERROR_MAX: u16 = 600;
36-
pub const HTTP_RESP_MIN_LEN: usize = 13; // 响应行:"HTTP/1.1 200 "
36+
pub const HTTP_RESP_MIN_LEN: usize = 12; // 响应行:"HTTP/1.1 200"(reason phrase 可省略,RFC 7230 允许)
3737

3838
pub const HTTP_HOST_OFFSET: usize = 6;
3939
pub const HTTP_CONTENT_LENGTH_OFFSET: usize = 16;

0 commit comments

Comments
 (0)