17
17
package dtap
18
18
19
19
import (
20
+ "context"
21
+ "strings"
22
+ "time"
23
+
24
+ log "github.com/sirupsen/logrus"
25
+
20
26
"fmt"
21
27
"reflect"
22
28
"strconv"
@@ -33,21 +39,71 @@ type DnstapPrometheusOutput struct {
33
39
}
34
40
35
41
type DnstapPrometheusOutputMetrics struct {
36
- Vec * prometheus.CounterVec
37
- Labels []string
42
+ Name string
43
+ Vec * prometheus.CounterVec
44
+ LabelKeys []string
45
+ LabelValues map [string ]* DnstapPrometheusOutputMetricsValues
46
+ Interval int
47
+ Expire int
48
+ CancelFunc context.CancelFunc
49
+ }
50
+
51
+ func (d * DnstapPrometheusOutputMetrics ) GetInterval () int {
52
+ if d .Interval <= 0 {
53
+ return 0
54
+ }
55
+ return d .Interval
56
+ }
57
+
58
+ func (d * DnstapPrometheusOutputMetrics ) GetExpire () int {
59
+ if d .Expire <= 0 {
60
+ return 0
61
+ }
62
+ return d .Expire
63
+ }
64
+
65
+ type DnstapPrometheusOutputMetricsValues struct {
66
+ Values []string
67
+ LastUpdate time.Time
68
+ }
69
+
70
+ func NewDnstapPrometheusOutputMetrics (counterConfig OutputPrometheusMetrics ) * DnstapPrometheusOutputMetrics {
71
+ return & DnstapPrometheusOutputMetrics {
72
+ Name : counterConfig .GetName (),
73
+ Vec : promauto .NewCounterVec (prometheus.CounterOpts {
74
+ Name : counterConfig .GetName (),
75
+ Help : counterConfig .GetHelp (),
76
+ }, counterConfig .GetLabels ()),
77
+ LabelKeys : counterConfig .GetLabels (),
78
+ LabelValues : map [string ]* DnstapPrometheusOutputMetricsValues {},
79
+ Expire : counterConfig .GetExpireSec (),
80
+ Interval : counterConfig .GetExpireInterval (),
81
+ }
38
82
}
39
83
40
- func NewDtapCounterVec (opts prometheus.CounterOpts ) * prometheus.CounterVec {
41
- s := DnstapFlatT {}
42
- t := reflect .TypeOf (s )
43
- var labels []string
84
+ func (d * DnstapPrometheusOutputMetrics ) Inc (values []string ) {
85
+ d .Vec .WithLabelValues (values ... ).Inc ()
86
+ d .LabelValues [strings .Join (values , "," )] = & DnstapPrometheusOutputMetricsValues {
87
+ Values : values ,
88
+ LastUpdate : time .Now (),
89
+ }
90
+ }
44
91
45
- for i := 0 ; i < t .NumField (); i ++ {
46
- field := t .Field (i )
47
- j := field .Tag .Get ("json" )
48
- labels = append (labels , j )
92
+ func (d * DnstapPrometheusOutputMetrics ) Flush (ctx context.Context ) {
93
+ ticker := time .NewTicker (time .Second * time .Duration (d .Interval ))
94
+ for {
95
+ select {
96
+ case <- ctx .Done ():
97
+ return
98
+ case <- ticker .C :
99
+ for k , value := range d .LabelValues {
100
+ if time .Now ().Sub (value .LastUpdate ) > time .Second * time .Duration (d .Expire ) {
101
+ d .Vec .DeleteLabelValues (value .Values ... )
102
+ delete (d .LabelValues , k )
103
+ }
104
+ }
105
+ }
49
106
}
50
- return promauto .NewCounterVec (opts , labels )
51
107
}
52
108
53
109
func NewDnstapPrometheusOutput (config * OutputPrometheus , params * DnstapOutputParams ) * DnstapOutput {
@@ -56,20 +112,20 @@ func NewDnstapPrometheusOutput(config *OutputPrometheus, params *DnstapOutputPar
56
112
Metrics : []* DnstapPrometheusOutputMetrics {},
57
113
}
58
114
for _ , counterConfig := range config .GetCounters () {
59
- counter := & DnstapPrometheusOutputMetrics {
60
- Vec : NewDtapCounterVec (prometheus.CounterOpts {
61
- Name : counterConfig .GetName (),
62
- Help : counterConfig .GetHelp (),
63
- }),
64
- Labels : counterConfig .GetLabels (),
65
- }
66
- p .Metrics = append (p .Metrics , counter )
115
+ p .Metrics = append (p .Metrics , NewDnstapPrometheusOutputMetrics (counterConfig ))
67
116
}
68
117
params .Handler = p
69
118
return NewDnstapOutput (params )
70
119
}
71
120
72
121
func (o * DnstapPrometheusOutput ) open () error {
122
+ for _ , metrics := range o .Metrics {
123
+ if metrics .GetInterval () > 0 && metrics .GetExpire () > 0 {
124
+ ctx , cancelFunc := context .WithCancel (context .Background ())
125
+ metrics .CancelFunc = cancelFunc
126
+ go metrics .Flush (ctx )
127
+ }
128
+ }
73
129
return nil
74
130
}
75
131
@@ -85,31 +141,47 @@ func (o *DnstapPrometheusOutput) write(frame []byte) error {
85
141
e := reflect .ValueOf (data ).Elem ()
86
142
m := make (map [string ]string )
87
143
for i := 0 ; i < e .NumField (); i ++ {
88
- field := e .Type ().Field (i ).Tag . Get ( "json" )
144
+ field := e .Type ().Field (i ).Name
89
145
value := e .Field (i ).Interface ()
146
+ if value == nil {
147
+ continue
148
+ }
90
149
switch v := value .(type ) {
91
150
case string :
92
151
m [field ] = v
93
152
case uint32 :
94
153
m [field ] = strconv .Itoa (int (v ))
95
154
case uint16 :
96
155
m [field ] = strconv .Itoa (int (v ))
156
+ case bool :
157
+ if v {
158
+ m [field ] = "1"
159
+ } else {
160
+ m [field ] = "0"
161
+ }
97
162
case fmt.Stringer :
98
163
m [field ] = v .String ()
99
164
}
100
165
}
101
166
102
167
for _ , counter := range o .Metrics {
103
- labels := map [ string ]string {}
104
- for _ , l := range counter .Labels {
168
+ labelValues := make ([ ]string , 0 , len ( counter . LabelKeys ))
169
+ for _ , l := range counter .LabelKeys {
105
170
if v , ok := m [l ]; ok {
106
- labels [l ] = v
171
+ labelValues = append (labelValues , v )
172
+ } else {
173
+ log .Warnf ("can't get metrics: %v, %v" , l , counter .Name )
107
174
}
108
175
}
109
- counter .Vec . With ( labels ). Inc ()
176
+ counter .Inc (labelValues )
110
177
}
111
178
return nil
112
179
}
113
180
114
181
func (o * DnstapPrometheusOutput ) close () {
182
+ for _ , metrics := range o .Metrics {
183
+ if metrics .CancelFunc != nil {
184
+ metrics .CancelFunc ()
185
+ }
186
+ }
115
187
}
0 commit comments