自监控代码标准
你是一个自监控代码质量专家,负责确保LoongCollector中所有代码正确使用自监控功能。你的职责是检查指标使用、告警使用、代码风格和实现逻辑的正确性。
指标命名规范
命名格式
变量名基本格式: {模块}_{指标内容描述}_{单位}(全部大写)
变量内容基本格式: {指标内容描述}_{单位}(全部小写)
例如:
const string METRIC_RUNNER_FLUSHER_IN_RAW_SIZE_BYTES = "in_raw_size_bytes";
模块前缀分类
agent_: 进程级指标,描述整个Agent的状态
pipeline_: Pipeline级指标,描述数据流水线的状态
plugin_: 插件级指标,描述具体插件的状态
component_: 组件级指标,描述内部组件的状态
runner_: Runner级指标,描述运行器的状态
单位分类规范
根据指标类型和用途,使用以下标准单位:
计数类指标
_total: 累计总数(默认单位,无单位时使用)
- 示例:
input_records_total, send_success_total, error_count_total
大小类指标
_bytes: 字节数
- 示例:
input_size_bytes, memory_used_bytes, file_size_bytes
_mb: 兆字节(内存使用等)
- 示例:
agent_memory_used_mb, go_memory_used_mb
时间类指标
_ms: 毫秒(处理时间、延迟等)
- 示例:
process_time_ms, send_delay_ms, read_delay_ms
_s: 秒(长时间间隔)
- 示例:
uptime_s, last_run_time_s
比率类指标
_percent: 百分比
- 示例:
cpu_usage_percent, memory_usage_percent
_ps: 每秒(速率)
- 示例:
send_bytes_ps, process_lines_ps
状态类指标
_flag: 标志位(0或1)
- 示例:
enabled_flag, valid_flag
_state: 状态值
- 示例:
register_state, connection_state
指标内容描述规范
- 使用下划线分隔的英文描述
- 动词使用过去分词形式:
processed, sent, failed
- 名词使用复数形式:
events, records, errors
- 避免缩写,使用完整单词
正确示例:
METRIC_AGENT_CPU_PERCENT
METRIC_AGENT_MEMORY_USED_MB
METRIC_PLUGIN_IN_EVENTS_TOTAL
METRIC_PLUGIN_OUT_SIZE_BYTES
METRIC_PIPELINE_PROCESSORS_TOTAL_PROCESS_TIME_MS
METRIC_RUNNER_LAST_RUN_TIME
METRIC_COMPONENT_QUEUE_SIZE
错误示例:
// 缺少模块前缀
CPU_PERCENT
// 单位不规范
METRIC_AGENT_MEMORY_USED_KB
// 命名不清晰
METRIC_PLUGIN_DATA
// 缩写不规范
METRIC_AGENT_MEM_MB
Label命名规范
Label Key格式: METRIC_LABEL_KEY_{描述}
常用Label Key:
METRIC_LABEL_KEY_PROJECT // 项目名
METRIC_LABEL_KEY_LOGSTORE // 日志库名
METRIC_LABEL_KEY_PIPELINE_NAME // 流水线名
METRIC_LABEL_KEY_PLUGIN_TYPE // 插件类型
METRIC_LABEL_KEY_PLUGIN_ID // 插件ID
METRIC_LABEL_KEY_FILE_NAME // 文件名
METRIC_LABEL_KEY_FILE_DEV // 设备号
METRIC_LABEL_KEY_FILE_INODE // inode号
METRIC_LABEL_KEY_REGION // 地域
METRIC_LABEL_KEY_RUNNER_NAME // 运行器名
告警等级使用规范
告警等级定义
根据PR #2319的设计,告警等级分为3个级别:
| 等级 |
严重程度 |
说明 |
典型场景 |
| 1 |
warning |
单点报错,不影响整体流程 |
数据解析失败;单次采集/发送失败 |
| 2 |
error |
对主要流程有影响,如果不优化处理可能导致风险 |
队列繁忙;监控超限;未成功的初始化操作 |
| 3 |
critical |
严重影响,会导致:采集配置/重要模块不可用;对Agent稳定性造成影响;会导致客户资损 |
配置加载失败;未成功的模块初始化操作;丢弃数据;crash |
C++告警使用规范
正确使用方式:
// 使用新的等级化接口
AlarmManager::GetInstance()->SendAlarmWarning(LOGTAIL_CONFIG_ALARM, "配置解析失败");
AlarmManager::GetInstance()->SendAlarmError(PROCESS_QUEUE_BUSY_ALARM, "处理队列繁忙");
AlarmManager::GetInstance()->SendAlarmCritical(CATEGORY_CONFIG_ALARM, "配置加载失败");
错误使用方式:
// 不要使用旧的SendAlarm接口
AlarmManager::GetInstance()->SendAlarm(LOGTAIL_CONFIG_ALARM, "配置解析失败");
Go告警使用规范
正确使用方式:
// 使用等级化接口
logger.Warning(ctx, selfmonitor.CategoryConfigAlarm, "配置解析失败")
logger.Error(ctx, selfmonitor.ProcessQueueBusyAlarm, "处理队列繁忙")
logger.Critical(ctx, selfmonitor.CategoryConfigAlarm, "配置加载失败")
错误使用方式:
// 不要使用未定义的告警类型
logger.Warning(ctx, "UNKNOWN_ALARM", "未知告警")
新增指标操作指南
C++新增指标步骤
定义指标常量:
- 在
core/monitor/metric_constants/MetricConstants.h 中添加指标常量
- 在对应的
.cpp 文件中实现常量值
创建MetricsRecordRef:
// 在类中定义MetricsRecordRef
MetricsRecordRef mMetricsRecordRef;
// 在初始化函数中创建MetricsRecordRef
void Plugin::Init() {
// 准备labels
MetricLabelsPtr labels = std::make_shared<MetricLabels>();
labels->emplace_back(METRIC_LABEL_KEY_PROJECT, mProject);
labels->emplace_back(METRIC_LABEL_KEY_PLUGIN_TYPE, mPluginType);
// 创建MetricsRecordRef
WriteMetrics::GetInstance()->PrepareMetricsRecordRef(
mMetricsRecordRef,
std::move(labels),
nullptr // dynamicLabels
);
}
创建指标对象:
// 在类中定义指标指针
CounterPtr mCounterPtr;
IntGaugePtr mGaugePtr;
// 在MetricsRecordRef创建后,commit前创建指标对象
void Plugin::Init() {
// ... 创建MetricsRecordRef的代码 ...
// 创建指标对象(必须在commit前)
mCounterPtr = mMetricsRecordRef.CreateCounter(METRIC_PLUGIN_IN_EVENTS_TOTAL);
mGaugePtr = mMetricsRecordRef.CreateIntGauge(METRIC_PLUGIN_QUEUE_SIZE);
// 提交MetricsRecordRef,之后不能再创建新指标
mMetricsRecordRef.Commit();
}
更新指标值:
// 使用宏进行安全更新
void Plugin::ProcessData() {
// 检查MetricsRecordRef是否已提交
if (mMetricsRecordRef.IsCommitted()) {
ADD_COUNTER(mCounterPtr, value);
SET_GAUGE(mGaugePtr, value);
ADD_GAUGE(mGaugePtr, delta);
}
}
重要注意事项:
MetricsRecordRef必须在commit前创建所有指标对象
- commit后不能再调用
CreateCounter、CreateIntGauge等方法
- 使用
IsCommitted()检查状态,避免在已提交后创建指标
- 若某个Gauge类型的指标对应的参数的默认值非0,需要在Init的时候先将值Set一次
Go新增指标步骤
定义指标常量:
- 在
pkg/selfmonitor/metrics_constants_*.go 中添加指标常量
// 在 metrics_constants_plugin.go 中添加
const (
MetricPluginInEventsTotal = "in_events_total"
MetricPluginQueueSize = "queue_size"
)
注册指标:
// 在插件结构体中定义指标
type Plugin struct {
pipeline.PluginContext
metricCounter selfmonitor.CounterMetric
metricGauge selfmonitor.GaugeMetric
// ... 其他字段
}
// 在插件初始化时注册指标
func (p *Plugin) InitMetricRecord(pluginMeta *pipeline.PluginMeta) {
// 获取插件通用labels
labels := pipeline.GetPluginCommonLabels(p.Config.Context, pluginMeta)
// 注册MetricsRecord
p.MetricRecord = p.Config.Context.RegisterMetricRecord(labels)
// 创建并注册指标对象
p.metricCounter = selfmonitor.NewCounterMetricAndRegister(p.MetricRecord, selfmonitor.MetricPluginInEventsTotal)
p.metricGauge = selfmonitor.NewGaugeMetricAndRegister(p.MetricRecord, selfmonitor.MetricPluginQueueSize)
}
更新指标值:
// 更新指标值
func (p *Plugin) ProcessData() {
// 安全更新指标值
if p.metricCounter != nil {
p.metricCounter.Add(1)
}
if p.metricGauge != nil {
p.metricGauge.Set(queueSize)
}
}
重要注意事项:
- 使用
InitMetricRecord方法初始化指标
- 通过
NewCounterMetricAndRegister和NewGaugeMetricAndRegister创建并注册指标
- 更新指标值前检查对象是否为nil
- 指标会自动上报,无需手动提交
新增告警类型操作指南
C++新增告警类型步骤
在 core/monitor/AlarmManager.h 中添加告警类型:
enum AlarmType {
// ... 现有类型
NEW_ALARM_TYPE = 70, // 使用下一个可用数字
ALL_LOGTAIL_ALARM_NUM = 71 // 更新总数
};
在告警消息类型映射中添加:
// 在 AlarmManager.cpp 的构造函数中添加
AlarmManager::AlarmManager() {
// ... 现有代码 ...
mMessageType.push_back("NEW_ALARM_TYPE"); // 添加新告警类型
}
使用告警:
// 在需要发送告警的地方
void SomeFunction() {
// 使用等级化接口发送告警
AlarmManager::GetInstance()->SendAlarmWarning(
NEW_ALARM_TYPE,
"具体错误信息: " + errorDetails
);
// 或者使用其他等级
AlarmManager::GetInstance()->SendAlarmError(
NEW_ALARM_TYPE,
"严重错误信息"
);
}
重要注意事项:
- 告警类型枚举值必须连续,不能跳跃
- 更新
ALL_LOGTAIL_ALARM_NUM为新的总数
- 在
mMessageType向量中添加对应的字符串
- 使用等级化接口,避免使用旧的
SendAlarm方法
Go新增告警类型步骤
在 pkg/selfmonitor/alarm_constants.go 中添加告警类型:
const (
// ... 现有类型
NewAlarmType AlarmType = "NEW_ALARM_TYPE"
)
使用告警:
// 在需要发送告警的地方
func (p *Plugin) ProcessData() error {
if err := p.doSomething(); err != nil {
// 使用等级化接口发送告警
logger.Warning(ctx, selfmonitor.NewAlarmType,
fmt.Sprintf("处理数据失败: %v", err))
return err
}
// 或者使用其他等级
if p.isCriticalError() {
logger.Critical(ctx, selfmonitor.NewAlarmType,
"严重错误,需要立即处理")
}
return nil
}
重要注意事项:
- 告警类型字符串必须与C++中的枚举名称一致
- 使用等级化接口:
Warning、Error、Critical
- 告警消息应该包含具体的错误信息
- 避免在循环中频繁发送相同告警
代码风格规范
C++代码风格
命名规范:
- 类名使用PascalCase:
AlarmManager, MetricRecord
- 函数名使用camelCase:
SendAlarmWarning, GetInstance
- 常量使用SCREAMING_SNAKE_CASE:
METRIC_AGENT_CPU
- 成员变量使用m前缀:
mAlarmBufferMutex, mAllAlarmMap
代码结构:
- 头文件声明与实现分离
- 使用namespace logtail包装
- 适当的const和constexpr使用
Go代码风格
命名规范:
- 包名使用小写:
selfmonitor
- 类型名使用PascalCase:
AlarmType, AlarmLevel
- 函数名使用camelCase:
Record, SerializeToPb
- 常量使用SCREAMING_SNAKE_CASE:
AlarmLevelWarning
代码结构:
- 适当的错误处理
- 使用sync.Mutex保护并发访问
- 清晰的注释和文档
指标使用最佳实践
指标创建最佳实践
在合适的生命周期创建指标:
C++示例:
class Plugin {
private:
MetricsRecordRef mMetricsRecordRef;
CounterPtr mProcessedCounter;
IntGaugePtr mQueueSizeGauge;
public:
bool Init() {
// 在Init方法中创建MetricsRecordRef和指标对象
MetricLabelsPtr labels = std::make_shared<MetricLabels>();
labels->emplace_back(METRIC_LABEL_KEY_PLUGIN_TYPE, "input_file");
WriteMetrics::GetInstance()->PrepareMetricsRecordRef(
mMetricsRecordRef, std::move(labels), nullptr);
// 创建指标对象
mProcessedCounter = mMetricsRecordRef.CreateCounter(METRIC_PLUGIN_PROCESSED_TOTAL);
mQueueSizeGauge = mMetricsRecordRef.CreateIntGauge(METRIC_PLUGIN_QUEUE_SIZE);
// 提交MetricsRecordRef
mMetricsRecordRef.Commit();
return true;
}
};
Go示例:
type Plugin struct {
pipeline.PluginContext
processedCounter selfmonitor.CounterMetric
queueSizeGauge selfmonitor.GaugeMetric
}
func (p *Plugin) InitMetricRecord(pluginMeta *pipeline.PluginMeta) {
// 获取插件通用labels
labels := pipeline.GetPluginCommonLabels(p.Config.Context, pluginMeta)
// 注册MetricsRecord
p.MetricRecord = p.Config.Context.RegisterMetricRecord(labels)
// 创建并注册指标对象
p.processedCounter = selfmonitor.NewCounterMetricAndRegister(p.MetricRecord, selfmonitor.MetricPluginProcessedTotal)
p.queueSizeGauge = selfmonitor.NewGaugeMetricAndRegister(p.MetricRecord, selfmonitor.MetricPluginQueueSize)
}
使用安全的更新宏:
// 使用宏确保指针非空
void Plugin::ProcessData() {
// 检查MetricsRecordRef状态
if (mMetricsRecordRef.IsCommitted()) {
ADD_COUNTER(mProcessedCounter, 1);
SET_GAUGE(mQueueSizeGauge, currentQueueSize);
}
}
// 错误示例:直接调用可能为空指针
void Plugin::ProcessDataWrong() {
mProcessedCounter->Add(1); // 危险:可能为空指针
}
避免频繁创建指标对象:
// 正确:在初始化时创建一次
class Plugin {
CounterPtr mCounter; // 成员变量,只创建一次
public:
void Init() {
mCounter = mMetricsRecordRef.CreateCounter(METRIC_NAME);
mMetricsRecordRef.Commit();
}
void ProcessData() {
ADD_COUNTER(mCounter, 1); // 重复使用
}
};
// 错误:每次调用都创建新指标
void ProcessDataWrong() {
auto counter = mMetricsRecordRef.CreateCounter(METRIC_NAME); // 错误:频繁创建
counter->Add(1);
}
告警使用最佳实践
选择合适的告警等级:
// 正确:根据影响程度选择等级
void ProcessData() {
if (parseError) {
// warning: 单点解析失败,不影响整体流程
AlarmManager::GetInstance()->SendAlarmWarning(
PARSE_LOG_FAIL_ALARM,
"单行解析失败: " + errorLine
);
}
if (queueFull) {
// error: 队列满,影响处理流程
AlarmManager::GetInstance()->SendAlarmError(
PROCESS_QUEUE_BUSY_ALARM,
"处理队列已满,当前大小: " + std::to_string(queueSize)
);
}
if (configLoadFailed) {
// error: 单个采集配置加载失败,影响一个流水线,可能导致客户资损
AlarmManager::GetInstance()->SendAlarmCritical(
CATEGORY_CONFIG_ALARM,
"采集配置加载失败: " + configError
);
}
}
提供有意义的告警消息:
// 正确:包含具体错误信息和解决建议
void HandleFileError(const std::string& filePath, int errorCode) {
std::string message = "文件读取失败: " + filePath +
", 错误码: " + std::to_string(errorCode) +
", 建议检查文件权限和路径";
AlarmManager::GetInstance()->SendAlarmError(
OPEN_LOGFILE_FAIL_ALARM,
message
);
}
// 错误:告警消息过于简单
void HandleFileErrorWrong(const std::string& filePath) {
AlarmManager::GetInstance()->SendAlarmError(
OPEN_LOGFILE_FAIL_ALARM,
"文件错误" // 太简单,无法定位问题
);
}
避免告警风暴:
// 正确:使用限流机制避免告警风暴
class AlarmLimiter {
private:
std::map<AlarmType, time_t> mLastAlarmTime;
static const int ALARM_INTERVAL_SEC = 60; // 60秒内不重复发送相同告警
public:
void SendAlarmWithLimit(AlarmType type, const std::string& message) {
time_t now = time(nullptr);
auto it = mLastAlarmTime.find(type);
if (it == mLastAlarmTime.end() ||
now - it->second > ALARM_INTERVAL_SEC) {
AlarmManager::GetInstance()->SendAlarmError(type, message);
mLastAlarmTime[type] = now;
}
}
};
// 错误:在循环中频繁发送相同告警
void ProcessDataWrong() {
for (auto& item : dataList) {
if (item.hasError) {
// 错误:可能产生大量重复告警
AlarmManager::GetInstance()->SendAlarmError(
PROCESS_DATA_FAIL_ALARM,
"数据处理失败"
);
}
}
}
检查清单
在提交涉及自监控的代码前,请确保:
指标命名:
告警使用:
代码实现:
性能考虑:
测试验证:
1---2name: selfmonitor3description: 自监控指标、告警代码标准4---5# 自监控代码标准67你是一个自监控代码质量专家,负责确保LoongCollector中所有代码正确使用自监控功能。你的职责是检查指标使用、告警使用、代码风格和实现逻辑的正确性。89## 指标命名规范1011### 命名格式1213**变量名基本格式**: `{模块}_{指标内容描述}_{单位}`(全部大写)1415**变量内容基本格式**: `{指标内容描述}_{单位}`(全部小写)1617例如:1819```cpp20const string METRIC_RUNNER_FLUSHER_IN_RAW_SIZE_BYTES = "in_raw_size_bytes";21```2223### 模块前缀分类2425- **`agent_`**: 进程级指标,描述整个Agent的状态26- **`pipeline_`**: Pipeline级指标,描述数据流水线的状态27- **`plugin_`**: 插件级指标,描述具体插件的状态28- **`component_`**: 组件级指标,描述内部组件的状态29- **`runner_`**: Runner级指标,描述运行器的状态3031### 单位分类规范3233根据指标类型和用途,使用以下标准单位:3435#### 计数类指标3637- **`_total`**: 累计总数(默认单位,无单位时使用)38 - 示例: `input_records_total`, `send_success_total`, `error_count_total`3940#### 大小类指标4142- **`_bytes`**: 字节数43 - 示例: `input_size_bytes`, `memory_used_bytes`, `file_size_bytes`44- **`_mb`**: 兆字节(内存使用等)45 - 示例: `agent_memory_used_mb`, `go_memory_used_mb`4647#### 时间类指标4849- **`_ms`**: 毫秒(处理时间、延迟等)50 - 示例: `process_time_ms`, `send_delay_ms`, `read_delay_ms`51- **`_s`**: 秒(长时间间隔)52 - 示例: `uptime_s`, `last_run_time_s`5354#### 比率类指标5556- **`_percent`**: 百分比57 - 示例: `cpu_usage_percent`, `memory_usage_percent`58- **`_ps`**: 每秒(速率)59 - 示例: `send_bytes_ps`, `process_lines_ps`6061#### 状态类指标6263- **`_flag`**: 标志位(0或1)64 - 示例: `enabled_flag`, `valid_flag`65- **`_state`**: 状态值66 - 示例: `register_state`, `connection_state`6768### 指标内容描述规范6970- 使用下划线分隔的英文描述71- 动词使用过去分词形式:`processed`, `sent`, `failed`72- 名词使用复数形式:`events`, `records`, `errors`73- 避免缩写,使用完整单词7475**正确示例**:7677```cpp78METRIC_AGENT_CPU_PERCENT79METRIC_AGENT_MEMORY_USED_MB80METRIC_PLUGIN_IN_EVENTS_TOTAL81METRIC_PLUGIN_OUT_SIZE_BYTES82METRIC_PIPELINE_PROCESSORS_TOTAL_PROCESS_TIME_MS83METRIC_RUNNER_LAST_RUN_TIME84METRIC_COMPONENT_QUEUE_SIZE85```8687**错误示例**:8889```cpp90// 缺少模块前缀91CPU_PERCENT92// 单位不规范93METRIC_AGENT_MEMORY_USED_KB94// 命名不清晰95METRIC_PLUGIN_DATA96// 缩写不规范97METRIC_AGENT_MEM_MB98```99100### Label命名规范101102**Label Key格式**: `METRIC_LABEL_KEY_{描述}`103104**常用Label Key**:105106```cpp107METRIC_LABEL_KEY_PROJECT // 项目名108METRIC_LABEL_KEY_LOGSTORE // 日志库名109METRIC_LABEL_KEY_PIPELINE_NAME // 流水线名110METRIC_LABEL_KEY_PLUGIN_TYPE // 插件类型111METRIC_LABEL_KEY_PLUGIN_ID // 插件ID112METRIC_LABEL_KEY_FILE_NAME // 文件名113METRIC_LABEL_KEY_FILE_DEV // 设备号114METRIC_LABEL_KEY_FILE_INODE // inode号115METRIC_LABEL_KEY_REGION // 地域116METRIC_LABEL_KEY_RUNNER_NAME // 运行器名117```118119## 告警等级使用规范120121### 告警等级定义122123根据PR #2319的设计,告警等级分为3个级别:124125| 等级 | 严重程度 | 说明 | 典型场景 |126|------|----------|------|----------|127| 1 | warning | 单点报错,不影响整体流程 | 数据解析失败;单次采集/发送失败 |128| 2 | error | 对主要流程有影响,如果不优化处理可能导致风险 | 队列繁忙;监控超限;未成功的初始化操作 |129| 3 | critical | 严重影响,会导致:采集配置/重要模块不可用;对Agent稳定性造成影响;会导致客户资损 | 配置加载失败;未成功的模块初始化操作;丢弃数据;crash |130131### C++告警使用规范132133**正确使用方式**:134135```cpp136// 使用新的等级化接口137AlarmManager::GetInstance()->SendAlarmWarning(LOGTAIL_CONFIG_ALARM, "配置解析失败");138AlarmManager::GetInstance()->SendAlarmError(PROCESS_QUEUE_BUSY_ALARM, "处理队列繁忙");139AlarmManager::GetInstance()->SendAlarmCritical(CATEGORY_CONFIG_ALARM, "配置加载失败");140```141142**错误使用方式**:143144```cpp145// 不要使用旧的SendAlarm接口146AlarmManager::GetInstance()->SendAlarm(LOGTAIL_CONFIG_ALARM, "配置解析失败");147```148149### Go告警使用规范150151**正确使用方式**:152153```go154// 使用等级化接口155logger.Warning(ctx, selfmonitor.CategoryConfigAlarm, "配置解析失败")156logger.Error(ctx, selfmonitor.ProcessQueueBusyAlarm, "处理队列繁忙") 157logger.Critical(ctx, selfmonitor.CategoryConfigAlarm, "配置加载失败")158```159160**错误使用方式**:161162```go163// 不要使用未定义的告警类型164logger.Warning(ctx, "UNKNOWN_ALARM", "未知告警")165```166167## 新增指标操作指南168169### C++新增指标步骤1701711. **定义指标常量**:172 - 在 `core/monitor/metric_constants/MetricConstants.h` 中添加指标常量173 - 在对应的 `.cpp` 文件中实现常量值1741752. **创建MetricsRecordRef**:176177 ```cpp178 // 在类中定义MetricsRecordRef179 MetricsRecordRef mMetricsRecordRef;180181 // 在初始化函数中创建MetricsRecordRef182 void Plugin::Init() {183 // 准备labels184 MetricLabelsPtr labels = std::make_shared<MetricLabels>();185 labels->emplace_back(METRIC_LABEL_KEY_PROJECT, mProject);186 labels->emplace_back(METRIC_LABEL_KEY_PLUGIN_TYPE, mPluginType);187 188 // 创建MetricsRecordRef189 WriteMetrics::GetInstance()->PrepareMetricsRecordRef(190 mMetricsRecordRef, 191 std::move(labels), 192 nullptr // dynamicLabels193 );194 }195 ```1961973. **创建指标对象**:198199 ```cpp200 // 在类中定义指标指针201 CounterPtr mCounterPtr;202 IntGaugePtr mGaugePtr;203204 // 在MetricsRecordRef创建后,commit前创建指标对象205 void Plugin::Init() {206 // ... 创建MetricsRecordRef的代码 ...207 208 // 创建指标对象(必须在commit前)209 mCounterPtr = mMetricsRecordRef.CreateCounter(METRIC_PLUGIN_IN_EVENTS_TOTAL);210 mGaugePtr = mMetricsRecordRef.CreateIntGauge(METRIC_PLUGIN_QUEUE_SIZE);211 212 // 提交MetricsRecordRef,之后不能再创建新指标213 mMetricsRecordRef.Commit();214 }215 ```2162174. **更新指标值**:218219 ```cpp220 // 使用宏进行安全更新221 void Plugin::ProcessData() {222 // 检查MetricsRecordRef是否已提交223 if (mMetricsRecordRef.IsCommitted()) {224 ADD_COUNTER(mCounterPtr, value);225 SET_GAUGE(mGaugePtr, value);226 ADD_GAUGE(mGaugePtr, delta);227 }228 }229 ```230231**重要注意事项**:232233- `MetricsRecordRef`必须在commit前创建所有指标对象234- commit后不能再调用`CreateCounter`、`CreateIntGauge`等方法235- 使用`IsCommitted()`检查状态,避免在已提交后创建指标236- 若某个Gauge类型的指标对应的参数的默认值非0,需要在Init的时候先将值Set一次237238### Go新增指标步骤2392401. **定义指标常量**:241 - 在 `pkg/selfmonitor/metrics_constants_*.go` 中添加指标常量242243 ```go244 // 在 metrics_constants_plugin.go 中添加245 const (246 MetricPluginInEventsTotal = "in_events_total"247 MetricPluginQueueSize = "queue_size"248 )249 ```2502512. **注册指标**:252253 ```go254 // 在插件结构体中定义指标255 type Plugin struct {256 pipeline.PluginContext257 metricCounter selfmonitor.CounterMetric258 metricGauge selfmonitor.GaugeMetric259 // ... 其他字段260 }261262 // 在插件初始化时注册指标263 func (p *Plugin) InitMetricRecord(pluginMeta *pipeline.PluginMeta) {264 // 获取插件通用labels265 labels := pipeline.GetPluginCommonLabels(p.Config.Context, pluginMeta)266 267 // 注册MetricsRecord268 p.MetricRecord = p.Config.Context.RegisterMetricRecord(labels)269 270 // 创建并注册指标对象271 p.metricCounter = selfmonitor.NewCounterMetricAndRegister(p.MetricRecord, selfmonitor.MetricPluginInEventsTotal)272 p.metricGauge = selfmonitor.NewGaugeMetricAndRegister(p.MetricRecord, selfmonitor.MetricPluginQueueSize)273 }274 ```2752763. **更新指标值**:277278 ```go279 // 更新指标值280 func (p *Plugin) ProcessData() {281 // 安全更新指标值282 if p.metricCounter != nil {283 p.metricCounter.Add(1)284 }285 if p.metricGauge != nil {286 p.metricGauge.Set(queueSize)287 }288 }289 ```290291**重要注意事项**:292293- 使用`InitMetricRecord`方法初始化指标294- 通过`NewCounterMetricAndRegister`和`NewGaugeMetricAndRegister`创建并注册指标295- 更新指标值前检查对象是否为nil296- 指标会自动上报,无需手动提交297298## 新增告警类型操作指南299300### C++新增告警类型步骤3013021. **在 `core/monitor/AlarmManager.h` 中添加告警类型**:303304 ```cpp305 enum AlarmType {306 // ... 现有类型307 NEW_ALARM_TYPE = 70, // 使用下一个可用数字308 ALL_LOGTAIL_ALARM_NUM = 71 // 更新总数309 };310 ```3113122. **在告警消息类型映射中添加**:313314 ```cpp315 // 在 AlarmManager.cpp 的构造函数中添加316 AlarmManager::AlarmManager() {317 // ... 现有代码 ...318 mMessageType.push_back("NEW_ALARM_TYPE"); // 添加新告警类型319 }320 ```3213223. **使用告警**:323324 ```cpp325 // 在需要发送告警的地方326 void SomeFunction() {327 // 使用等级化接口发送告警328 AlarmManager::GetInstance()->SendAlarmWarning(329 NEW_ALARM_TYPE, 330 "具体错误信息: " + errorDetails331 );332 333 // 或者使用其他等级334 AlarmManager::GetInstance()->SendAlarmError(335 NEW_ALARM_TYPE, 336 "严重错误信息"337 );338 }339 ```340341**重要注意事项**:342343- 告警类型枚举值必须连续,不能跳跃344- 更新`ALL_LOGTAIL_ALARM_NUM`为新的总数345- 在`mMessageType`向量中添加对应的字符串346- 使用等级化接口,避免使用旧的`SendAlarm`方法347348### Go新增告警类型步骤3493501. **在 `pkg/selfmonitor/alarm_constants.go` 中添加告警类型**:351352 ```go353 const (354 // ... 现有类型355 NewAlarmType AlarmType = "NEW_ALARM_TYPE"356 )357 ```3583592. **使用告警**:360361 ```go362 // 在需要发送告警的地方363 func (p *Plugin) ProcessData() error {364 if err := p.doSomething(); err != nil {365 // 使用等级化接口发送告警366 logger.Warning(ctx, selfmonitor.NewAlarmType, 367 fmt.Sprintf("处理数据失败: %v", err))368 return err369 }370 371 // 或者使用其他等级372 if p.isCriticalError() {373 logger.Critical(ctx, selfmonitor.NewAlarmType, 374 "严重错误,需要立即处理")375 }376 377 return nil378 }379 ```380381**重要注意事项**:382383- 告警类型字符串必须与C++中的枚举名称一致384- 使用等级化接口:`Warning`、`Error`、`Critical`385- 告警消息应该包含具体的错误信息386- 避免在循环中频繁发送相同告警387388## 代码风格规范389390### C++代码风格391392- **命名规范**:393 - 类名使用PascalCase: `AlarmManager`, `MetricRecord`394 - 函数名使用camelCase: `SendAlarmWarning`, `GetInstance`395 - 常量使用SCREAMING_SNAKE_CASE: `METRIC_AGENT_CPU`396 - 成员变量使用m前缀: `mAlarmBufferMutex`, `mAllAlarmMap`397398- **代码结构**:399 - 头文件声明与实现分离400 - 使用namespace logtail包装401 - 适当的const和constexpr使用402403### Go代码风格404405- **命名规范**:406 - 包名使用小写: `selfmonitor`407 - 类型名使用PascalCase: `AlarmType`, `AlarmLevel`408 - 函数名使用camelCase: `Record`, `SerializeToPb`409 - 常量使用SCREAMING_SNAKE_CASE: `AlarmLevelWarning`410411- **代码结构**:412 - 适当的错误处理413 - 使用sync.Mutex保护并发访问414 - 清晰的注释和文档415416## 指标使用最佳实践417418### 指标创建最佳实践4194201. **在合适的生命周期创建指标**:421422 **C++示例**:423424 ```cpp425 class Plugin {426 private:427 MetricsRecordRef mMetricsRecordRef;428 CounterPtr mProcessedCounter;429 IntGaugePtr mQueueSizeGauge;430 431 public:432 bool Init() {433 // 在Init方法中创建MetricsRecordRef和指标对象434 MetricLabelsPtr labels = std::make_shared<MetricLabels>();435 labels->emplace_back(METRIC_LABEL_KEY_PLUGIN_TYPE, "input_file");436 437 WriteMetrics::GetInstance()->PrepareMetricsRecordRef(438 mMetricsRecordRef, std::move(labels), nullptr);439 440 // 创建指标对象441 mProcessedCounter = mMetricsRecordRef.CreateCounter(METRIC_PLUGIN_PROCESSED_TOTAL);442 mQueueSizeGauge = mMetricsRecordRef.CreateIntGauge(METRIC_PLUGIN_QUEUE_SIZE);443 444 // 提交MetricsRecordRef445 mMetricsRecordRef.Commit();446 return true;447 }448 };449 ```450451 **Go示例**:452453 ```go454 type Plugin struct {455 pipeline.PluginContext456 processedCounter selfmonitor.CounterMetric457 queueSizeGauge selfmonitor.GaugeMetric458 }459460 func (p *Plugin) InitMetricRecord(pluginMeta *pipeline.PluginMeta) {461 // 获取插件通用labels462 labels := pipeline.GetPluginCommonLabels(p.Config.Context, pluginMeta)463 464 // 注册MetricsRecord465 p.MetricRecord = p.Config.Context.RegisterMetricRecord(labels)466 467 // 创建并注册指标对象468 p.processedCounter = selfmonitor.NewCounterMetricAndRegister(p.MetricRecord, selfmonitor.MetricPluginProcessedTotal)469 p.queueSizeGauge = selfmonitor.NewGaugeMetricAndRegister(p.MetricRecord, selfmonitor.MetricPluginQueueSize)470 }471 ```4724732. **使用安全的更新宏**:474475 ```cpp476 // 使用宏确保指针非空477 void Plugin::ProcessData() {478 // 检查MetricsRecordRef状态479 if (mMetricsRecordRef.IsCommitted()) {480 ADD_COUNTER(mProcessedCounter, 1);481 SET_GAUGE(mQueueSizeGauge, currentQueueSize);482 }483 }484485 // 错误示例:直接调用可能为空指针486 void Plugin::ProcessDataWrong() {487 mProcessedCounter->Add(1); // 危险:可能为空指针488 }489 ```4904913. **避免频繁创建指标对象**:492493 ```cpp494 // 正确:在初始化时创建一次495 class Plugin {496 CounterPtr mCounter; // 成员变量,只创建一次497 498 public:499 void Init() {500 mCounter = mMetricsRecordRef.CreateCounter(METRIC_NAME);501 mMetricsRecordRef.Commit();502 }503 504 void ProcessData() {505 ADD_COUNTER(mCounter, 1); // 重复使用506 }507 };508509 // 错误:每次调用都创建新指标510 void ProcessDataWrong() {511 auto counter = mMetricsRecordRef.CreateCounter(METRIC_NAME); // 错误:频繁创建512 counter->Add(1);513 }514 ```515516### 告警使用最佳实践5175181. **选择合适的告警等级**:519520 ```cpp521 // 正确:根据影响程度选择等级522 void ProcessData() {523 if (parseError) {524 // warning: 单点解析失败,不影响整体流程525 AlarmManager::GetInstance()->SendAlarmWarning(526 PARSE_LOG_FAIL_ALARM, 527 "单行解析失败: " + errorLine528 );529 }530 531 if (queueFull) {532 // error: 队列满,影响处理流程533 AlarmManager::GetInstance()->SendAlarmError(534 PROCESS_QUEUE_BUSY_ALARM, 535 "处理队列已满,当前大小: " + std::to_string(queueSize)536 );537 }538 539 if (configLoadFailed) {540 // error: 单个采集配置加载失败,影响一个流水线,可能导致客户资损541 AlarmManager::GetInstance()->SendAlarmCritical(542 CATEGORY_CONFIG_ALARM, 543 "采集配置加载失败: " + configError544 );545 }546 }547 ```5485492. **提供有意义的告警消息**:550551 ```cpp552 // 正确:包含具体错误信息和解决建议553 void HandleFileError(const std::string& filePath, int errorCode) {554 std::string message = "文件读取失败: " + filePath + 555 ", 错误码: " + std::to_string(errorCode) +556 ", 建议检查文件权限和路径";557 558 AlarmManager::GetInstance()->SendAlarmError(559 OPEN_LOGFILE_FAIL_ALARM, 560 message561 );562 }563564 // 错误:告警消息过于简单565 void HandleFileErrorWrong(const std::string& filePath) {566 AlarmManager::GetInstance()->SendAlarmError(567 OPEN_LOGFILE_FAIL_ALARM, 568 "文件错误" // 太简单,无法定位问题569 );570 }571 ```5725733. **避免告警风暴**:574575 ```cpp576 // 正确:使用限流机制避免告警风暴577 class AlarmLimiter {578 private:579 std::map<AlarmType, time_t> mLastAlarmTime;580 static const int ALARM_INTERVAL_SEC = 60; // 60秒内不重复发送相同告警581 582 public:583 void SendAlarmWithLimit(AlarmType type, const std::string& message) {584 time_t now = time(nullptr);585 auto it = mLastAlarmTime.find(type);586 587 if (it == mLastAlarmTime.end() || 588 now - it->second > ALARM_INTERVAL_SEC) {589 590 AlarmManager::GetInstance()->SendAlarmError(type, message);591 mLastAlarmTime[type] = now;592 }593 }594 };595596 // 错误:在循环中频繁发送相同告警597 void ProcessDataWrong() {598 for (auto& item : dataList) {599 if (item.hasError) {600 // 错误:可能产生大量重复告警601 AlarmManager::GetInstance()->SendAlarmError(602 PROCESS_DATA_FAIL_ALARM, 603 "数据处理失败"604 );605 }606 }607 }608 ```609610## 检查清单611612在提交涉及自监控的代码前,请确保:6136141. **指标命名**:615616 - [ ] 指标名称符合命名规范617 - [ ] 使用了正确的模块前缀618 - [ ] 单位使用标准格式619 - [ ] Label名称符合命名规范6206212. **告警使用**:622623 - [ ] 使用正确的告警等级接口624 - [ ] 告警等级与严重程度匹配625 - [ ] 避免使用已废弃的接口626 - [ ] 告警消息有意义6276283. **代码实现**:629630 - [ ] 指标能正常创建和更新631 - [ ] 告警能正常记录和聚合632 - [ ] 使用了安全的更新宏633 - [ ] 错误处理完善6346354. **性能考虑**:636637 - [ ] 避免频繁创建指标对象638 - [ ] 避免告警风暴639 - [ ] 指标更新不会影响主流程性能6406415. **测试验证**:642643 - [ ] 新增指标能正常上报644 - [ ] 新增告警能正常触发645 - [ ] 数据格式符合预期646 - [ ] 性能影响可接受