@@ -9,12 +9,33 @@ import (
9
9
)
10
10
11
11
type serializer struct {
12
- HecRouting bool
12
+ HecRouting bool
13
+ SplunkmetricMultiMetric bool
13
14
}
14
15
15
- func NewSerializer (splunkmetric_hec_routing bool ) (* serializer , error ) {
16
+ type CommonTags struct {
17
+ Time float64
18
+ Host string
19
+ Index string
20
+ Source string
21
+ Fields map [string ]interface {}
22
+ }
23
+
24
+ type HECTimeSeries struct {
25
+ Time float64 `json:"time"`
26
+ Event string `json:"event"`
27
+ Host string `json:"host,omitempty"`
28
+ Index string `json:"index,omitempty"`
29
+ Source string `json:"source,omitempty"`
30
+ Fields map [string ]interface {} `json:"fields"`
31
+ }
32
+
33
+ // NewSerializer Setup our new serializer
34
+ func NewSerializer (splunkmetric_hec_routing bool , splunkmetric_multimetric bool ) (* serializer , error ) {
35
+ /* Define output params */
16
36
s := & serializer {
17
- HecRouting : splunkmetric_hec_routing ,
37
+ HecRouting : splunkmetric_hec_routing ,
38
+ SplunkmetricMultiMetric : splunkmetric_multimetric ,
18
39
}
19
40
return s , nil
20
41
}
@@ -45,26 +66,61 @@ func (s *serializer) SerializeBatch(metrics []telegraf.Metric) ([]byte, error) {
45
66
return serialized , nil
46
67
}
47
68
48
- func (s * serializer ) createObject (metric telegraf.Metric ) (metricGroup []byte , err error ) {
69
+ func (s * serializer ) createMulti (metric telegraf.Metric , dataGroup HECTimeSeries , commonTags CommonTags ) (metricGroup []byte , err error ) {
70
+ /* When splunkmetric_multimetric is true, then we can write out multiple name=value pairs as part of the same
71
+ ** event payload. This only works when the time, host, and dimensions are the same for every name=value pair
72
+ ** in the timeseries data.
73
+ **
74
+ ** The format for multimetric data is 'metric_name:nameOfMetric = valueOfMetric'
75
+ */
76
+ var metricJSON []byte
77
+
78
+ // Set the event data from the commonTags above.
79
+ dataGroup .Event = "metric"
80
+ dataGroup .Time = commonTags .Time
81
+ dataGroup .Host = commonTags .Host
82
+ dataGroup .Index = commonTags .Index
83
+ dataGroup .Source = commonTags .Source
84
+ dataGroup .Fields = commonTags .Fields
85
+
86
+ // Stuff the metrid data into the structure.
87
+ for _ , field := range metric .FieldList () {
88
+ value , valid := verifyValue (field .Value )
49
89
50
- /* Splunk supports one metric json object, and does _not_ support an array of JSON objects.
51
- ** Splunk has the following required names for the metric store:
52
- ** metric_name: The name of the metric
53
- ** _value: The value for the metric
54
- ** time: The timestamp for the metric
55
- ** All other index fields become dimensions.
56
- */
57
- type HECTimeSeries struct {
58
- Time float64 `json:"time"`
59
- Event string `json:"event"`
60
- Host string `json:"host,omitempty"`
61
- Index string `json:"index,omitempty"`
62
- Source string `json:"source,omitempty"`
63
- Fields map [string ]interface {} `json:"fields"`
90
+ if ! valid {
91
+ log .Printf ("D! Can not parse value: %v for key: %v" , field .Value , field .Key )
92
+ continue
93
+ }
94
+
95
+ dataGroup .Fields ["metric_name:" + metric .Name ()+ "." + field .Key ] = value
64
96
}
65
97
66
- dataGroup := HECTimeSeries {}
67
- var metricJson []byte
98
+ // Manage the rest of the event details based upon HEC routing rules
99
+ switch s .HecRouting {
100
+ case true :
101
+ // Output the data as a fields array and host,index,time,source overrides for the HEC.
102
+ metricJSON , err = json .Marshal (dataGroup )
103
+ default :
104
+ // Just output the data and the time, useful for file based outuputs
105
+ dataGroup .Fields ["time" ] = dataGroup .Time
106
+ metricJSON , err = json .Marshal (dataGroup .Fields )
107
+ }
108
+ if err != nil {
109
+ return nil , err
110
+ }
111
+ // Let the JSON fall through to the return below
112
+ metricGroup = metricJSON
113
+
114
+ return metricGroup , nil
115
+ }
116
+
117
+ func (s * serializer ) createSingle (metric telegraf.Metric , dataGroup HECTimeSeries , commonTags CommonTags ) (metricGroup []byte , err error ) {
118
+ /* The default mode is to generate one JSON entitiy per metric (required for pre-8.0 Splunks)
119
+ **
120
+ ** The format for single metric is 'nameOfMetric = valueOfMetric'
121
+ */
122
+
123
+ var metricJSON []byte
68
124
69
125
for _ , field := range metric .FieldList () {
70
126
@@ -75,39 +131,30 @@ func (s *serializer) createObject(metric telegraf.Metric) (metricGroup []byte, e
75
131
continue
76
132
}
77
133
78
- obj := map [string ]interface {}{}
79
- obj ["metric_name" ] = metric .Name () + "." + field .Key
80
- obj ["_value" ] = value
81
-
82
134
dataGroup .Event = "metric"
83
- // Convert ns to float seconds since epoch.
84
- dataGroup .Time = float64 (metric .Time ().UnixNano ()) / float64 (1000000000 )
85
- dataGroup .Fields = obj
86
-
87
- // Break tags out into key(n)=value(t) pairs
88
- for n , t := range metric .Tags () {
89
- if n == "host" {
90
- dataGroup .Host = t
91
- } else if n == "index" {
92
- dataGroup .Index = t
93
- } else if n == "source" {
94
- dataGroup .Source = t
95
- } else {
96
- dataGroup .Fields [n ] = t
97
- }
98
- }
135
+
136
+ dataGroup .Time = commonTags .Time
137
+
138
+ // Apply the common tags from above to every record.
139
+ dataGroup .Host = commonTags .Host
140
+ dataGroup .Index = commonTags .Index
141
+ dataGroup .Source = commonTags .Source
142
+ dataGroup .Fields = commonTags .Fields
143
+
144
+ dataGroup .Fields ["metric_name" ] = metric .Name () + "." + field .Key
145
+ dataGroup .Fields ["_value" ] = value
99
146
100
147
switch s .HecRouting {
101
148
case true :
102
149
// Output the data as a fields array and host,index,time,source overrides for the HEC.
103
- metricJson , err = json .Marshal (dataGroup )
150
+ metricJSON , err = json .Marshal (dataGroup )
104
151
default :
105
152
// Just output the data and the time, useful for file based outuputs
106
153
dataGroup .Fields ["time" ] = dataGroup .Time
107
- metricJson , err = json .Marshal (dataGroup .Fields )
154
+ metricJSON , err = json .Marshal (dataGroup .Fields )
108
155
}
109
156
110
- metricGroup = append (metricGroup , metricJson ... )
157
+ metricGroup = append (metricGroup , metricJSON ... )
111
158
112
159
if err != nil {
113
160
return nil , err
@@ -117,6 +164,52 @@ func (s *serializer) createObject(metric telegraf.Metric) (metricGroup []byte, e
117
164
return metricGroup , nil
118
165
}
119
166
167
+ func (s * serializer ) createObject (metric telegraf.Metric ) (metricGroup []byte , err error ) {
168
+
169
+ /* Splunk supports one metric json object, and does _not_ support an array of JSON objects.
170
+ ** Splunk has the following required names for the metric store:
171
+ ** metric_name: The name of the metric
172
+ ** _value: The value for the metric
173
+ ** time: The timestamp for the metric
174
+ ** All other index fields become dimensions.
175
+ */
176
+
177
+ dataGroup := HECTimeSeries {}
178
+
179
+ // The tags are common to all events in this timeseries
180
+ commonTags := CommonTags {}
181
+
182
+ commonObj := map [string ]interface {}{}
183
+
184
+ commonObj ["config:hecRouting" ] = s .HecRouting
185
+ commonObj ["config:multiMetric" ] = s .SplunkmetricMultiMetric
186
+
187
+ commonTags .Fields = commonObj
188
+
189
+ // Break tags out into key(n)=value(t) pairs
190
+ for n , t := range metric .Tags () {
191
+ if n == "host" {
192
+ commonTags .Host = t
193
+ } else if n == "index" {
194
+ commonTags .Index = t
195
+ } else if n == "source" {
196
+ commonTags .Source = t
197
+ } else {
198
+ commonTags .Fields [n ] = t
199
+ }
200
+ }
201
+ commonTags .Time = float64 (metric .Time ().UnixNano ()) / float64 (1000000000 )
202
+ switch s .SplunkmetricMultiMetric {
203
+ case true :
204
+ metricGroup , _ = s .createMulti (metric , dataGroup , commonTags )
205
+ default :
206
+ metricGroup , _ = s .createSingle (metric , dataGroup , commonTags )
207
+ }
208
+
209
+ // Return the metric group regardless of if it's multimetric or single metric.
210
+ return metricGroup , nil
211
+ }
212
+
120
213
func verifyValue (v interface {}) (value interface {}, valid bool ) {
121
214
switch v .(type ) {
122
215
case string :
0 commit comments