feat: propagate alert forwarding trace ids

This commit is contained in:
zxr
2026-07-21 17:19:39 +08:00
parent c263825cbc
commit 567dda70c0
3 changed files with 26 additions and 5 deletions

View File

@@ -2,6 +2,7 @@ package ingest
import ( import (
"bytes" "bytes"
"crypto/sha256"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
@@ -14,7 +15,10 @@ import (
"git.apinb.com/ops/logs/internal/config" "git.apinb.com/ops/logs/internal/config"
) )
const alertResponseBodyLimit = 1 << 20 const (
alertResponseBodyLimit = 1 << 20
maxAlertTraceIDLength = 96
)
var errAlertForwardDisabled = errors.New("Alert 转发未启用或 base_url 为空") var errAlertForwardDisabled = errors.New("Alert 转发未启用或 base_url 为空")
@@ -36,6 +40,7 @@ type AlertReceiveBody struct {
PolicyID uint `json:"policy_id"` PolicyID uint `json:"policy_id"`
State string `json:"state,omitempty"` State string `json:"state,omitempty"`
SourceEventKey string `json:"source_event_key"` SourceEventKey string `json:"source_event_key"`
TraceID string `json:"trace_id"`
OccurredAt time.Time `json:"occurred_at"` OccurredAt time.Time `json:"occurred_at"`
RawData json.RawMessage `json:"raw_data"` RawData json.RawMessage `json:"raw_data"`
} }
@@ -70,11 +75,12 @@ func forwardAlert(body AlertReceiveBody) error {
if body.PolicyID == 0 && cfg.DefaultPolicyID > 0 { if body.PolicyID == 0 && cfg.DefaultPolicyID > 0 {
body.PolicyID = cfg.DefaultPolicyID body.PolicyID = cfg.DefaultPolicyID
} }
body.TraceID = ensureAlertTraceID(body.TraceID, body.SourceEventKey)
raw, err := json.Marshal(body) raw, err := json.Marshal(body)
if err != nil { if err != nil {
return err return err
} }
result, err := postAlertPayload(cfg, "/Alert/v1/alerts/receive", raw) result, err := postAlertPayload(cfg, "/Alert/v1/alerts/receive", raw, body.TraceID)
if err != nil { if err != nil {
return err return err
} }
@@ -101,12 +107,13 @@ func forwardAlert(body AlertReceiveBody) error {
return nil return nil
} }
func postAlertPayload(cfg *config.AlertForwardConf, path string, payload []byte) (*alertForwardResponse, error) { func postAlertPayload(cfg *config.AlertForwardConf, path string, payload []byte, traceID string) (*alertForwardResponse, error) {
req, err := http.NewRequest(http.MethodPost, cfg.BaseURL+path, bytes.NewReader(payload)) req, err := http.NewRequest(http.MethodPost, cfg.BaseURL+path, bytes.NewReader(payload))
if err != nil { if err != nil {
return nil, fmt.Errorf("创建 Alert 转发请求失败:%w", err) return nil, fmt.Errorf("创建 Alert 转发请求失败:%w", err)
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Trace-Id", traceID)
if cfg.InternalKey != "" { if cfg.InternalKey != "" {
req.Header.Set("X-Internal-Key", cfg.InternalKey) req.Header.Set("X-Internal-Key", cfg.InternalKey)
} }
@@ -173,9 +180,19 @@ func buildRawEventIngestBody(body AlertReceiveBody, parseStatus string) RawEvent
Annotations: annotations, Annotations: annotations,
ParseStatus: parseStatus, ParseStatus: parseStatus,
RawPayload: body.RawData, RawPayload: body.RawData,
TraceID: ensureAlertTraceID(body.TraceID, body.SourceEventKey),
} }
} }
func ensureAlertTraceID(traceID, sourceEventKey string) string {
traceID = strings.TrimSpace(traceID)
if traceID != "" && len(traceID) <= maxAlertTraceIDLength {
return traceID
}
sum := sha256.Sum256([]byte(strings.TrimSpace(sourceEventKey)))
return hex.EncodeToString(sum[:16])
}
func validFingerprint(value string) bool { func validFingerprint(value string) bool {
if len(value) != 64 { if len(value) != 64 {
return false return false

View File

@@ -25,6 +25,7 @@ const (
) )
func enqueueAlertWithDB(db *gorm.DB, logEventID uint, body AlertReceiveBody) (uint, error) { func enqueueAlertWithDB(db *gorm.DB, logEventID uint, body AlertReceiveBody) (uint, error) {
body.TraceID = ensureAlertTraceID(body.TraceID, body.SourceEventKey)
payload, err := json.Marshal(body) payload, err := json.Marshal(body)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -193,11 +194,12 @@ func forwardRawEvent(body RawEventIngestBody) error {
if len(body.RawPayload) == 0 { if len(body.RawPayload) == 0 {
return fmt.Errorf("raw_payload 不能为空") return fmt.Errorf("raw_payload 不能为空")
} }
body.TraceID = ensureAlertTraceID(body.TraceID, body.SourceEventKey)
raw, err := json.Marshal(body) raw, err := json.Marshal(body)
if err != nil { if err != nil {
return err return err
} }
result, err := postAlertPayload(cfg, "/Alert/v1/raw-events/ingest", raw) result, err := postAlertPayload(cfg, "/Alert/v1/raw-events/ingest", raw, body.TraceID)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -53,9 +53,10 @@ func BuildReplayRawEventPayload(ev models.LogEvent) (RawEventIngestBody, error)
labels["resource_uid"] = resourceUID labels["resource_uid"] = resourceUID
labels["resource_category"] = category labels["resource_category"] = category
labels["service_identity"] = identity labels["service_identity"] = identity
sourceEventKey := fmt.Sprintf("logs:replay:%d:%d", ev.ID, now.UnixNano())
return RawEventIngestBody{ return RawEventIngestBody{
SourceType: sourceType, SourceType: sourceType,
SourceEventKey: fmt.Sprintf("logs:replay:%d:%d", ev.ID, now.UnixNano()), SourceEventKey: sourceEventKey,
State: "firing", State: "firing",
ResourceUID: resourceUID, ResourceUID: resourceUID,
EventTime: now, EventTime: now,
@@ -69,6 +70,7 @@ func BuildReplayRawEventPayload(ev models.LogEvent) (RawEventIngestBody, error)
}, },
ParseStatus: "parsed", ParseStatus: "parsed",
RawPayload: rawBytes, RawPayload: rawBytes,
TraceID: ensureAlertTraceID("", sourceEventKey),
}, nil }, nil
} }