@@ -5,113 +5,153 @@ import (
5
5
"reflect"
6
6
"regexp"
7
7
"strings"
8
+ "sync"
8
9
9
10
"github.com/prometheus/client_golang/prometheus"
10
11
11
12
"github.com/c9s/bbgo/pkg/fixedpoint"
12
- "github.com/c9s/bbgo/pkg/types"
13
13
)
14
14
15
- var dynamicStrategyConfigMetrics = map [string ]any {}
15
+ var matchFirstCapRE = regexp .MustCompile ("(.)([A-Z][a-z]+)" )
16
+ var matchAllCap = regexp .MustCompile ("([a-z0-9])([A-Z])" )
16
17
17
- func InitializeConfigMetrics (id , instanceId string , s types.StrategyID ) error {
18
- matchFirstCapRE := regexp .MustCompile ("(.)([A-Z][a-z]+)" )
19
- matchAllCap := regexp .MustCompile ("([a-z0-9])([A-Z])" )
18
+ var dynamicStrategyConfigMetrics = map [string ]* prometheus.GaugeVec {}
19
+ var dynamicStrategyConfigMetricsMutex sync.Mutex
20
20
21
- tv := reflect .TypeOf (s ).Elem ()
22
- sv := reflect .Indirect (reflect .ValueOf (s ))
21
+ func getOrCreateMetric (id , fieldName string ) (* prometheus.GaugeVec , string , error ) {
22
+ metricName := id + "_config_" + fieldName
23
+
24
+ dynamicStrategyConfigMetricsMutex .Lock ()
25
+ metric , ok := dynamicStrategyConfigMetrics [metricName ]
26
+ defer dynamicStrategyConfigMetricsMutex .Unlock ()
27
+
28
+ if ! ok {
29
+ metric = prometheus .NewGaugeVec (
30
+ prometheus.GaugeOpts {
31
+ Name : metricName ,
32
+ Help : id + " config value of " + fieldName ,
33
+ },
34
+ []string {"strategy_type" , "strategy_id" , "symbol" },
35
+ )
36
+
37
+ if err := prometheus .Register (metric ); err != nil {
38
+ return nil , "" , fmt .Errorf ("unable to register metrics on field %+v, error: %+v" , fieldName , err )
39
+ }
40
+
41
+ dynamicStrategyConfigMetrics [metricName ] = metric
42
+ }
43
+
44
+ return metric , metricName , nil
45
+ }
46
+
47
+ func toSnakeCase (input string ) string {
48
+ input = matchFirstCapRE .ReplaceAllString (input , "${1}_${2}" )
49
+ input = matchAllCap .ReplaceAllString (input , "${1}_${2}" )
50
+ return strings .ToLower (input )
51
+ }
52
+
53
+ func castToFloat64 (valInf any ) (float64 , bool ) {
54
+ var val float64
55
+ switch tt := valInf .(type ) {
56
+
57
+ case fixedpoint.Value :
58
+ val = tt .Float64 ()
59
+ case * fixedpoint.Value :
60
+ if tt != nil {
61
+ val = tt .Float64 ()
62
+ }
63
+ case float64 :
64
+ val = tt
65
+ case int :
66
+ val = float64 (tt )
67
+ case int32 :
68
+ val = float64 (tt )
69
+ case int64 :
70
+ val = float64 (tt )
71
+ case bool :
72
+ if tt {
73
+ val = 1.0
74
+ } else {
75
+ val = 0.0
76
+ }
77
+ default :
78
+ return 0.0 , false
79
+ }
80
+
81
+ return val , true
82
+ }
83
+
84
+ func InitializeConfigMetrics (id , instanceId string , st any ) error {
85
+ _ , err := initializeConfigMetricsWithFieldPrefix (id , instanceId , "" , st )
86
+ return err
87
+ }
88
+
89
+ func initializeConfigMetricsWithFieldPrefix (id , instanceId , fieldPrefix string , st any ) ([]string , error ) {
90
+ var metricNames []string
91
+ tv := reflect .TypeOf (st ).Elem ()
92
+
93
+ vv := reflect .ValueOf (st )
94
+ if vv .IsNil () {
95
+ return nil , nil
96
+ }
97
+
98
+ sv := reflect .Indirect (vv )
23
99
24
100
symbolField := sv .FieldByName ("Symbol" )
25
101
hasSymbolField := symbolField .IsValid ()
26
102
27
- nextStructField:
28
103
for i := 0 ; i < tv .NumField (); i ++ {
29
104
field := tv .Field (i )
105
+ if ! field .IsExported () {
106
+ continue
107
+ }
108
+
30
109
jsonTag := field .Tag .Get ("json" )
31
110
if jsonTag == "" {
32
- continue nextStructField
111
+ continue
33
112
}
34
113
35
114
tagAttrs := strings .Split (jsonTag , "," )
36
115
if len (tagAttrs ) == 0 {
37
- continue nextStructField
116
+ continue
38
117
}
39
118
40
- fieldName := tagAttrs [0 ]
41
- fieldName = matchFirstCapRE .ReplaceAllString (fieldName , "${1}_${2}" )
42
- fieldName = matchAllCap .ReplaceAllString (fieldName , "${1}_${2}" )
43
- fieldName = strings .ToLower (fieldName )
119
+ fieldName := fieldPrefix + toSnakeCase (tagAttrs [0 ])
120
+ if field .Type .Kind () == reflect .Pointer && field .Type .Elem ().Kind () == reflect .Struct {
121
+ subMetricNames , err := initializeConfigMetricsWithFieldPrefix (id , instanceId , fieldName + "_" , sv .Field (i ).Interface ())
122
+ if err != nil {
123
+ return nil , err
124
+ }
44
125
45
- isStr := false
126
+ metricNames = append (metricNames , subMetricNames ... )
127
+ continue
128
+ }
46
129
47
130
val := 0.0
48
131
valInf := sv .Field (i ).Interface ()
49
- switch tt := valInf .(type ) {
50
- case string :
51
- isStr = true
52
-
53
- case fixedpoint.Value :
54
- val = tt .Float64 ()
55
- case * fixedpoint.Value :
56
- if tt != nil {
57
- val = tt .Float64 ()
58
- }
59
- case float64 :
60
- val = tt
61
- case int :
62
- val = float64 (tt )
63
- case int32 :
64
- val = float64 (tt )
65
- case int64 :
66
- val = float64 (tt )
67
- case bool :
68
- if tt {
69
- val = 1.0
70
- } else {
71
- val = 0.0
72
- }
73
- default :
74
- continue nextStructField
75
- }
76
-
77
- if isStr {
78
- continue nextStructField
132
+ val , ok := castToFloat64 (valInf )
133
+ if ! ok {
134
+ continue
79
135
}
80
136
81
137
symbol := ""
82
138
if hasSymbolField {
83
139
symbol = symbolField .String ()
84
140
}
85
141
86
- metricName := id + "_config_" + fieldName
87
- anyMetric , ok := dynamicStrategyConfigMetrics [metricName ]
88
- if ! ok {
89
- gaugeMetric := prometheus .NewGaugeVec (
90
- prometheus.GaugeOpts {
91
- Name : metricName ,
92
- Help : id + " config value of " + field .Name ,
93
- },
94
- []string {"strategy_type" , "strategy_id" , "symbol" },
95
- )
96
- if err := prometheus .Register (gaugeMetric ); err != nil {
97
- return fmt .Errorf ("unable to register metrics on field %+v, error: %+v" , field .Name , err )
98
- }
99
-
100
- anyMetric = gaugeMetric
101
- dynamicStrategyConfigMetrics [metricName ] = anyMetric
142
+ metric , metricName , err := getOrCreateMetric (id , fieldName )
143
+ if err != nil {
144
+ return nil , err
102
145
}
103
146
104
- if anyMetric != nil {
105
- switch metric := anyMetric .(type ) {
106
- case * prometheus.GaugeVec :
107
- metric .With (prometheus.Labels {
108
- "strategy_type" : id ,
109
- "strategy_id" : instanceId ,
110
- "symbol" : symbol ,
111
- }).Set (val )
112
- }
113
- }
147
+ metric .With (prometheus.Labels {
148
+ "strategy_type" : id ,
149
+ "strategy_id" : instanceId ,
150
+ "symbol" : symbol ,
151
+ }).Set (val )
152
+
153
+ metricNames = append (metricNames , metricName )
114
154
}
115
155
116
- return nil
156
+ return metricNames , nil
117
157
}
0 commit comments