Skip to content

Commit 20ba085

Browse files
authored
feat: improve code (#193)
* feat: improve code * feat: improve code * feat: improve code
1 parent 2ca478c commit 20ba085

7 files changed

Lines changed: 248 additions & 76 deletions

File tree

internal/api/api.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,14 +403,15 @@ func (r *RequestClient) CreateRecord(parent string, rc *openDpsV1alpha1Resource.
403403

404404
// record's labels may have not been ensured
405405
recordLabels := rc.GetLabels()
406+
ensuredLabels := make([]*openDpsV1alpha1Resource.Label, 0, len(recordLabels))
406407
for _, label := range recordLabels {
407408
l, err := r.ensureLabel(parent, label.GetDisplayName())
408409
if err != nil {
409410
return nil, err
410411
}
411-
recordLabels = append(recordLabels, l)
412+
ensuredLabels = append(ensuredLabels, l)
412413
}
413-
rc.SetLabels(recordLabels)
414+
rc.SetLabels(ensuredLabels)
414415

415416
req := openDpsV1alpha1Service.CreateRecordRequest{
416417
Parent: parent,

internal/collector/collector.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -234,10 +234,18 @@ func handleRecordCaches(uploadChan chan string, reqClient *api.RequestClient, co
234234
if rcName, ok := rc.Record["name"].(string); ok && rcName != "" {
235235
select {
236236
case uploadChan <- rc.GetRecordCachePath():
237-
log.Infof("Record cache %s is ready to upload", rcName)
237+
log.WithFields(log.Fields{
238+
"recordCache": rc.GetRecordCachePath(),
239+
"recordName": rcName,
240+
"project": rc.ProjectName,
241+
}).Infof("record cache is ready to upload")
238242
default:
239243
recordSet.Release(rc.GetRecordCachePath())
240-
log.Infof("Upload channel is full, skip uploading record cache %s", rcName)
244+
log.WithFields(log.Fields{
245+
"recordCache": rc.GetRecordCachePath(),
246+
"recordName": rcName,
247+
"project": rc.ProjectName,
248+
}).Infof("upload channel is full, skip enqueueing record cache")
241249
}
242250
} else {
243251
recordSet.Release(rc.GetRecordCachePath())
@@ -552,6 +560,17 @@ func createRecord(deviceInfo *openDpsV1alpha1Resource.Device, recordCache *model
552560

553561
title := getRecordTitle(recordCache)
554562
description := getRecordDescription(recordCache)
563+
ruleName, _ := recordCache.DiagnosisTask["rule_name"].(string)
564+
ruleDisplayName, _ := recordCache.DiagnosisTask["rule_display_name"].(string)
565+
recordLog := log.WithFields(log.Fields{
566+
"recordCache": recordCache.GetRecordCachePath(),
567+
"project": recordCache.ProjectName,
568+
"title": title,
569+
"ruleName": ruleName,
570+
"ruleDisplayName": ruleDisplayName,
571+
"device": deviceInfo.GetName(),
572+
"totalFiles": len(recordCache.OriginalFiles),
573+
})
555574

556575
labels := make([]*openDpsV1alpha1Resource.Label, 0)
557576
for _, label := range recordCache.Labels {
@@ -573,18 +592,18 @@ func createRecord(deviceInfo *openDpsV1alpha1Resource.Device, recordCache *model
573592
Device: deviceInfo,
574593
Metadata: metadata,
575594
}
576-
ruleName, ok := recordCache.DiagnosisTask["rule_name"].(string)
577-
if ok {
595+
if ruleName != "" {
578596
record.Rules = []*openDpsV1alpha1Resource.DiagnosisRule{
579597
{
580598
Name: ruleName,
581599
},
582600
}
583601
}
584602

603+
recordLog.Infof("creating cloud record")
585604
record, err := reqClient.CreateRecord(recordCache.ProjectName, record)
586605
if err != nil {
587-
log.Errorf("create record failed: %v", err)
606+
recordLog.Errorf("create record failed: %v", err)
588607
return
589608
}
590609
recordCache.Record = map[string]interface{}{
@@ -604,7 +623,9 @@ func createRecord(deviceInfo *openDpsV1alpha1Resource.Device, recordCache *model
604623
latest.Record = recordCache.Record
605624
return nil
606625
}); err != nil {
607-
log.Errorf("save record cache failed: %v", err)
626+
recordLog.WithField("recordName", record.GetName()).Errorf("save record cache failed after record creation: %v", err)
627+
} else {
628+
recordLog.WithField("recordName", record.GetName()).Infof("created cloud record and saved record cache")
608629
}
609630
}
610631

internal/mod/http/server/rules.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ import (
1919
"encoding/json"
2020
"net/http"
2121
"slices"
22+
"sync/atomic"
2223

2324
"github.com/ThreeDotsLabs/watermill"
2425
gcmessage "github.com/ThreeDotsLabs/watermill/message"
2526
"github.com/ThreeDotsLabs/watermill/pubsub/gochannel"
2627
"github.com/coscene-io/coscout/pkg/constant"
2728
"github.com/coscene-io/coscout/pkg/rule_engine"
28-
mapset "github.com/deckarep/golang-set/v2"
2929
log "github.com/sirupsen/logrus"
3030
"golang.org/x/time/rate"
3131
)
@@ -106,7 +106,9 @@ func RulesHandler(pubSub *gochannel.GoChannel) func(w http.ResponseWriter, r *ht
106106
}
107107

108108
func ActiveTopicsHandler(ctx context.Context, pubSub *gochannel.GoChannel) func(w http.ResponseWriter, r *http.Request) {
109-
activeTopics := mapset.NewSet[string]()
109+
var activeTopics atomic.Value
110+
activeTopics.Store([]string{})
111+
110112
go func() {
111113
msg, err := pubSub.Subscribe(ctx, constant.TopicConfigTopicsMsg)
112114
if err != nil {
@@ -131,14 +133,19 @@ func ActiveTopicsHandler(ctx context.Context, pubSub *gochannel.GoChannel) func(
131133
continue
132134
}
133135
log.Infof("Received active topics: %v", topics)
134-
activeTopics = mapset.NewSet(topics...)
136+
activeTopics.Store(topics)
135137
}
136138
}
137139
}()
138140

139141
return func(w http.ResponseWriter, r *http.Request) {
142+
topics, ok := activeTopics.Load().([]string)
143+
if !ok {
144+
log.Errorf("Unexpected active topics value type")
145+
topics = []string{}
146+
}
140147
res := ActiveTopicsResponse{
141-
Topics: activeTopics.ToSlice(),
148+
Topics: topics,
142149
}
143150
bytes, err := json.Marshal(res)
144151
if err != nil {

internal/mod/rule/engine.go

Lines changed: 127 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package rule
1717
import (
1818
"encoding/json"
1919
"strconv"
20+
"sync"
2021
"time"
2122

2223
"buf.build/gen/go/coscene-io/coscene-openapi/protocolbuffers/go/coscene/openapi/dataplatform/v1alpha1/resources"
@@ -42,9 +43,10 @@ const (
4243

4344
// Engine represents the rule engine that processes messages against rules.
4445
type Engine struct {
45-
reqClient api.RequestClient
46-
deviceName string
46+
reqClient api.RequestClient
4747

48+
mu sync.RWMutex
49+
deviceName string
4850
rules []*rule_engine.Rule
4951
activeTopics mapset.Set[string]
5052

@@ -120,28 +122,62 @@ func (e *Engine) UpdateRules(apiRules []*resources.DiagnosisRule, configTopics [
120122
}
121123
}
122124
}
123-
e.rules = rules
124-
125125
configTopicSet := mapset.NewSet(configTopics...)
126+
var newActiveTopics mapset.Set[string]
126127
if hasWildcardTopicRule {
127-
e.activeTopics = mapset.NewSet[string]()
128+
newActiveTopics = mapset.NewSet[string]()
128129
} else {
129-
e.activeTopics = activeTopics.Intersect(configTopicSet)
130+
newActiveTopics = activeTopics.Intersect(configTopicSet)
130131
}
131132

133+
e.mu.Lock()
134+
e.rules = rules
135+
e.activeTopics = newActiveTopics
136+
e.mu.Unlock()
137+
132138
log.Infof("Updated %d valid rules", len(rules))
133139
}
134140

135141
// ActiveTopics returns all the active topics in the rule engine.
136-
func (e *Engine) ActiveTopics() mapset.Set[string] {
137-
return e.activeTopics
142+
func (e *Engine) ActiveTopics() []string {
143+
e.mu.RLock()
144+
defer e.mu.RUnlock()
145+
if e.activeTopics == nil {
146+
return []string{}
147+
}
148+
return e.activeTopics.ToSlice()
149+
}
150+
151+
func (e *Engine) activeTopicSet() mapset.Set[string] {
152+
e.mu.RLock()
153+
defer e.mu.RUnlock()
154+
if e.activeTopics == nil {
155+
return mapset.NewSet[string]()
156+
}
157+
return e.activeTopics.Clone()
158+
}
159+
160+
func (e *Engine) SetDeviceName(name string) {
161+
e.mu.Lock()
162+
defer e.mu.Unlock()
163+
e.deviceName = name
164+
}
165+
166+
func (e *Engine) getDeviceName() string {
167+
e.mu.RLock()
168+
defer e.mu.RUnlock()
169+
return e.deviceName
138170
}
139171

140172
// ConsumeNext shows how to process a message through the rule engine.
141173
func (e *Engine) ConsumeNext(item rule_engine.RuleItem) {
142174
log.Debugf("consuming message: %+v", item)
143175

144-
for _, rule := range e.rules {
176+
e.mu.RLock()
177+
rules := e.rules
178+
e.mu.RUnlock()
179+
180+
for _, rule := range rules {
145181
if rule.Topics.Cardinality() > 0 && !rule.Topics.Contains(item.Topic) {
146182
log.Debugf("rule %s topics %v does not match topic %s, skipping", rule.Metadata["rule_display_name"], rule.Topics, item.Topic)
147183
continue
@@ -169,9 +205,17 @@ func (e *Engine) ConsumeNext(item rule_engine.RuleItem) {
169205

170206
log.Debugf("rule %s: isActive: %v, preActivationTime: %v, msgTs: %v", rule.Metadata["rule_display_name"], isActive, prevActivationTime, msgTs)
171207
if isActive {
172-
log.Infof("rule %s met, save collect info", rule.Metadata["rule_display_name"])
173-
174208
collectInfoId := uuid.New().String()
209+
ruleLog := log.WithFields(log.Fields{
210+
"collectID": collectInfoId,
211+
"ruleName": rule.Metadata["rule_name"],
212+
"ruleDisplayName": rule.Metadata["rule_display_name"],
213+
"topic": item.Topic,
214+
"source": item.Source,
215+
"ts": item.Ts,
216+
})
217+
ruleLog.Infof("rule met, save collect info")
218+
175219
additionalArgs := map[string]interface{}{}
176220
for k, v := range rule.Metadata {
177221
additionalArgs[k] = v
@@ -180,11 +224,13 @@ func (e *Engine) ConsumeNext(item rule_engine.RuleItem) {
180224

181225
for _, action := range rule.Actions {
182226
if err := action.Run(curActivation, additionalArgs); err != nil {
183-
log.Errorf("failed to run action %s: %v", action.Name, err)
227+
ruleLog.WithField("action", action.Name).Errorf("failed to run action: %v", err)
184228
}
185229
}
186230
if err := model.PublishCollectInfo(collectInfoId); err != nil {
187-
log.Errorf("failed to publish collect info %s: %v", collectInfoId, err)
231+
ruleLog.Errorf("failed to publish collect info: %v", err)
232+
} else {
233+
ruleLog.Infof("published collect info")
188234
}
189235
}
190236
}
@@ -239,8 +285,6 @@ func (e *Engine) cleanupDebounceTime() {
239285
// uploadActionImpl is the implementation of "upload" function,
240286
// it stores the collect info in the database.
241287
func (e *Engine) uploadActionImpl(kwargs map[string]interface{}) error {
242-
log.Infof("triggered saving collect info")
243-
244288
// Extract required fields with type assertions
245289
beforeStr, ok := kwargs["before"].(string)
246290
if !ok {
@@ -325,9 +369,20 @@ func (e *Engine) uploadActionImpl(kwargs map[string]interface{}) error {
325369
return errors.Errorf("rule must be a DiagnosisRule object")
326370
}
327371

328-
canUpload := e.canUpload(uploadLimit, rule)
329-
if err := e.reqClient.HitDiagnosisRule(rule, e.deviceName, canUpload); err != nil {
330-
return errors.Wrap(err, "failed to hit diagnosis rule")
372+
deviceName := e.getDeviceName()
373+
actionLog := log.WithFields(log.Fields{
374+
"collectID": collectInfoId,
375+
"ruleName": ruleName,
376+
"ruleDisplayName": ruleDisplayName,
377+
"project": projectName,
378+
"device": deviceName,
379+
"triggerTs": triggerTsFloat,
380+
})
381+
actionLog.Infof("triggered saving collect info")
382+
383+
canUpload := e.canUpload(uploadLimit, rule, deviceName)
384+
if err := e.reqClient.HitDiagnosisRule(rule, deviceName, canUpload); err != nil {
385+
actionLog.Errorf("failed to hit diagnosis rule, continuing with save: %v", err)
331386
}
332387

333388
// Calculate start and end times
@@ -360,43 +415,86 @@ func (e *Engine) uploadActionImpl(kwargs map[string]interface{}) error {
360415
WhiteList: whiteList,
361416
}
362417
collectInfo.Skip = !canUpload
418+
actionLog.WithFields(log.Fields{
419+
"canUpload": canUpload,
420+
"start": collectInfo.Cut.Start,
421+
"end": collectInfo.Cut.End,
422+
"labels": labels,
423+
}).Infof("prepared collect info")
363424

364425
// Save the collect info
365426
if err = collectInfo.SaveDraft(); err != nil {
366427
return errors.Wrap(err, "failed to save collect info")
367428
}
368-
log.Infof("saved collect info %s", collectInfoId)
429+
actionLog.Infof("saved collect info draft")
369430

370431
return nil
371432
}
372433

373-
func (e *Engine) canUpload(uploadLimit *resources.UploadLimit, rule *resources.DiagnosisRule) bool {
434+
func (e *Engine) canUpload(uploadLimit *resources.UploadLimit, rule *resources.DiagnosisRule, deviceName string) bool {
435+
ruleName := rule.GetName()
436+
ruleDisplayName := rule.GetDisplayName()
374437
if uploadLimit.HasDevice() {
375-
count, err := e.reqClient.CountDiagnosisRuleHits(rule, e.deviceName)
438+
count, err := e.reqClient.CountDiagnosisRuleHits(rule, deviceName)
376439
if err != nil {
377-
log.Errorf("failed to count diagnosis rule hits: %v, skipping", err)
378-
return false
440+
log.WithFields(log.Fields{
441+
"ruleName": ruleName,
442+
"ruleDisplayName": ruleDisplayName,
443+
"device": deviceName,
444+
"scope": "device",
445+
}).Errorf("failed to count diagnosis rule hits, defaulting to allow upload: %v", err)
446+
return true
379447
}
380448

381-
log.Infof("device count: %d, limit: %d", count, uploadLimit.GetDevice().GetTimes())
449+
log.WithFields(log.Fields{
450+
"ruleName": ruleName,
451+
"ruleDisplayName": ruleDisplayName,
452+
"device": deviceName,
453+
"scope": "device",
454+
"count": count,
455+
"limit": uploadLimit.GetDevice().GetTimes(),
456+
}).Infof("checked diagnosis rule upload limit")
382457

383458
if count >= uploadLimit.GetDevice().GetTimes() {
384-
log.Infof("device count %d exceeds limit %d, skipping", count, uploadLimit.GetDevice().GetTimes())
459+
log.WithFields(log.Fields{
460+
"ruleName": ruleName,
461+
"ruleDisplayName": ruleDisplayName,
462+
"device": deviceName,
463+
"scope": "device",
464+
"count": count,
465+
"limit": uploadLimit.GetDevice().GetTimes(),
466+
}).Infof("diagnosis rule upload limit reached, skipping upload")
385467
return false
386468
}
387469
}
388470

389471
if uploadLimit.HasGlobal() {
390472
count, err := e.reqClient.CountDiagnosisRuleHits(rule, "")
391473
if err != nil {
392-
log.Errorf("failed to count diagnosis rule hits: %v, skipping", err)
393-
return false
474+
log.WithFields(log.Fields{
475+
"ruleName": ruleName,
476+
"ruleDisplayName": ruleDisplayName,
477+
"scope": "global",
478+
}).Errorf("failed to count diagnosis rule hits, defaulting to allow upload: %v", err)
479+
return true
394480
}
395481

396-
log.Infof("global count: %d, limit: %d", count, uploadLimit.GetGlobal().GetTimes())
482+
log.WithFields(log.Fields{
483+
"ruleName": ruleName,
484+
"ruleDisplayName": ruleDisplayName,
485+
"scope": "global",
486+
"count": count,
487+
"limit": uploadLimit.GetGlobal().GetTimes(),
488+
}).Infof("checked diagnosis rule upload limit")
397489

398490
if count >= uploadLimit.GetGlobal().GetTimes() {
399-
log.Infof("global count %d exceeds limit %d, skipping", count, uploadLimit.GetGlobal().GetTimes())
491+
log.WithFields(log.Fields{
492+
"ruleName": ruleName,
493+
"ruleDisplayName": ruleDisplayName,
494+
"scope": "global",
495+
"count": count,
496+
"limit": uploadLimit.GetGlobal().GetTimes(),
497+
}).Infof("diagnosis rule upload limit reached, skipping upload")
400498
return false
401499
}
402500
}

0 commit comments

Comments
 (0)