@@ -20,6 +20,8 @@ import (
20
20
"go.opentelemetry.io/collector/component"
21
21
"go.opentelemetry.io/collector/consumer"
22
22
"go.opentelemetry.io/collector/pdata/plog"
23
+ "go.opentelemetry.io/collector/pdata/pmetric"
24
+ "go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp"
23
25
"go.opentelemetry.io/collector/receiver"
24
26
"go.opentelemetry.io/collector/receiver/receiverhelper"
25
27
"go.uber.org/zap"
@@ -32,9 +34,11 @@ import (
32
34
const (
33
35
defaultServerTimeout = 20 * time .Second
34
36
35
- responseOK = "OK"
36
- responseInvalidMethod = "Only \" POST\" method is supported"
37
- responseInvalidContentType = "\" Content-Type\" must be \" application/x-protobuf\" "
37
+ responseOK = "OK"
38
+ responseInvalidMethod = "Only \" POST\" method is supported"
39
+ responseEventsInvalidContentType = "\" Content-Type\" must be \" application/x-protobuf\" "
40
+
41
+ responseInvalidContentType = "\" Content-Type\" must be either \" application/x-protobuf\" or \" application/x-protobuf;format=otlp\" "
38
42
responseInvalidEncoding = "\" Content-Encoding\" must be \" gzip\" or empty"
39
43
responseErrGzipReader = "Error on gzip body"
40
44
responseErrReadBody = "Failed to read message body"
@@ -45,22 +49,24 @@ const (
45
49
46
50
// Centralizing some HTTP and related string constants.
47
51
protobufContentType = "application/x-protobuf"
52
+ otlpProtobufContentType = "application/x-protobuf;format=otlp"
48
53
gzipEncoding = "gzip"
49
54
httpContentTypeHeader = "Content-Type"
50
55
httpContentEncodingHeader = "Content-Encoding"
51
56
)
52
57
53
58
var (
54
- okRespBody = initJSONResponse (responseOK )
55
- invalidMethodRespBody = initJSONResponse (responseInvalidMethod )
56
- invalidContentRespBody = initJSONResponse (responseInvalidContentType )
57
- invalidEncodingRespBody = initJSONResponse (responseInvalidEncoding )
58
- errGzipReaderRespBody = initJSONResponse (responseErrGzipReader )
59
- errReadBodyRespBody = initJSONResponse (responseErrReadBody )
60
- errUnmarshalBodyRespBody = initJSONResponse (responseErrUnmarshalBody )
61
- errNextConsumerRespBody = initJSONResponse (responseErrNextConsumer )
62
- errLogsNotConfigured = initJSONResponse (responseErrLogsNotConfigured )
63
- errMetricsNotConfigured = initJSONResponse (responseErrMetricsNotConfigured )
59
+ okRespBody = initJSONResponse (responseOK )
60
+ invalidMethodRespBody = initJSONResponse (responseInvalidMethod )
61
+ invalidContentRespBody = initJSONResponse (responseInvalidContentType )
62
+ invalidEventsContentRespBody = initJSONResponse (responseEventsInvalidContentType )
63
+ invalidEncodingRespBody = initJSONResponse (responseInvalidEncoding )
64
+ errGzipReaderRespBody = initJSONResponse (responseErrGzipReader )
65
+ errReadBodyRespBody = initJSONResponse (responseErrReadBody )
66
+ errUnmarshalBodyRespBody = initJSONResponse (responseErrUnmarshalBody )
67
+ errNextConsumerRespBody = initJSONResponse (responseErrNextConsumer )
68
+ errLogsNotConfigured = initJSONResponse (responseErrLogsNotConfigured )
69
+ errMetricsNotConfigured = initJSONResponse (responseErrMetricsNotConfigured )
64
70
65
71
translator = & signalfx.ToTranslator {}
66
72
)
@@ -166,16 +172,6 @@ func (r *sfxReceiver) Shutdown(context.Context) error {
166
172
}
167
173
168
174
func (r * sfxReceiver ) readBody (ctx context.Context , resp http.ResponseWriter , req * http.Request ) ([]byte , bool ) {
169
- if req .Method != http .MethodPost {
170
- r .failRequest (ctx , resp , http .StatusBadRequest , invalidMethodRespBody , nil )
171
- return nil , false
172
- }
173
-
174
- if req .Header .Get (httpContentTypeHeader ) != protobufContentType {
175
- r .failRequest (ctx , resp , http .StatusUnsupportedMediaType , invalidContentRespBody , nil )
176
- return nil , false
177
- }
178
-
179
175
encoding := req .Header .Get (httpContentEncodingHeader )
180
176
if encoding != "" && encoding != gzipEncoding {
181
177
r .failRequest (ctx , resp , http .StatusUnsupportedMediaType , invalidEncodingRespBody , nil )
@@ -221,40 +217,64 @@ func (r *sfxReceiver) handleDatapointReq(resp http.ResponseWriter, req *http.Req
221
217
return
222
218
}
223
219
224
- body , ok := r . readBody ( ctx , resp , req )
225
- if ! ok {
220
+ if req . Method != http . MethodPost {
221
+ r . failRequest ( ctx , resp , http . StatusBadRequest , invalidMethodRespBody , nil )
226
222
return
227
223
}
228
224
229
- msg := & sfxpb.DataPointUploadMessage {}
230
- if err := msg .Unmarshal (body ); err != nil {
231
- r .failRequest (ctx , resp , http .StatusBadRequest , errUnmarshalBodyRespBody , err )
225
+ otlpFormat := false
226
+ switch req .Header .Get (httpContentTypeHeader ) {
227
+ case protobufContentType :
228
+ case otlpProtobufContentType :
229
+ otlpFormat = true
230
+ default :
231
+ r .failRequest (ctx , resp , http .StatusUnsupportedMediaType , invalidContentRespBody , nil )
232
232
return
233
233
}
234
234
235
- if len (msg .Datapoints ) == 0 {
236
- r .obsrecv .EndMetricsOp (ctx , metadata .Type .String (), 0 , nil )
237
- _ , _ = resp .Write (okRespBody )
235
+ body , ok := r .readBody (ctx , resp , req )
236
+ if ! ok {
238
237
return
239
238
}
240
239
241
- md , err := translator .ToMetrics (msg .Datapoints )
242
- if err != nil {
243
- r .settings .Logger .Debug ("SignalFx conversion error" , zap .Error (err ))
244
- }
240
+ r .settings .Logger .Debug ("Handling metrics data" )
245
241
246
- if r .config .AccessTokenPassthrough {
247
- if accessToken := req .Header .Get (splunk .SFxAccessTokenHeader ); accessToken != "" {
248
- for i := 0 ; i < md .ResourceMetrics ().Len (); i ++ {
249
- rm := md .ResourceMetrics ().At (i )
250
- res := rm .Resource ()
251
- res .Attributes ().PutStr (splunk .SFxAccessTokenLabel , accessToken )
252
- }
242
+ var md pmetric.Metrics
243
+
244
+ if otlpFormat {
245
+ r .settings .Logger .Debug ("Received request is in OTLP format" )
246
+ otlpreq := pmetricotlp .NewExportRequest ()
247
+ if err := otlpreq .UnmarshalProto (body ); err != nil {
248
+ r .settings .Logger .Debug ("OTLP data unmarshalling failed" , zap .Error (err ))
249
+ r .failRequest (ctx , resp , http .StatusBadRequest , errUnmarshalBodyRespBody , err )
250
+ return
253
251
}
252
+ md = otlpreq .Metrics ()
253
+ } else {
254
+ msg := & sfxpb.DataPointUploadMessage {}
255
+ err := msg .Unmarshal (body )
256
+ if err != nil {
257
+ r .failRequest (ctx , resp , http .StatusBadRequest , errUnmarshalBodyRespBody , err )
258
+ return
259
+ }
260
+
261
+ md , err = translator .ToMetrics (msg .Datapoints )
262
+ if err != nil {
263
+ r .settings .Logger .Debug ("SignalFx conversion error" , zap .Error (err ))
264
+ }
265
+ }
266
+
267
+ dataPointCount := md .DataPointCount ()
268
+ if dataPointCount == 0 {
269
+ r .obsrecv .EndMetricsOp (ctx , metadata .Type .String (), 0 , nil )
270
+ _ , _ = resp .Write (okRespBody )
271
+ return
254
272
}
255
273
256
- err = r .metricsConsumer .ConsumeMetrics (ctx , md )
257
- r .obsrecv .EndMetricsOp (ctx , metadata .Type .String (), len (msg .Datapoints ), err )
274
+ r .addAccessTokenLabel (md , req )
275
+
276
+ err := r .metricsConsumer .ConsumeMetrics (ctx , md )
277
+ r .obsrecv .EndMetricsOp (ctx , metadata .Type .String (), dataPointCount , err )
258
278
259
279
r .writeResponse (ctx , resp , err )
260
280
}
@@ -267,6 +287,16 @@ func (r *sfxReceiver) handleEventReq(resp http.ResponseWriter, req *http.Request
267
287
return
268
288
}
269
289
290
+ if req .Method != http .MethodPost {
291
+ r .failRequest (ctx , resp , http .StatusBadRequest , invalidMethodRespBody , nil )
292
+ return
293
+ }
294
+
295
+ if req .Header .Get (httpContentTypeHeader ) != protobufContentType {
296
+ r .failRequest (ctx , resp , http .StatusUnsupportedMediaType , invalidEventsContentRespBody , nil )
297
+ return
298
+ }
299
+
270
300
body , ok := r .readBody (ctx , resp , req )
271
301
if ! ok {
272
302
return
@@ -336,6 +366,18 @@ func (r *sfxReceiver) failRequest(
336
366
)
337
367
}
338
368
369
+ func (r * sfxReceiver ) addAccessTokenLabel (md pmetric.Metrics , req * http.Request ) {
370
+ if r .config .AccessTokenPassthrough {
371
+ if accessToken := req .Header .Get (splunk .SFxAccessTokenHeader ); accessToken != "" {
372
+ for i := 0 ; i < md .ResourceMetrics ().Len (); i ++ {
373
+ rm := md .ResourceMetrics ().At (i )
374
+ res := rm .Resource ()
375
+ res .Attributes ().PutStr (splunk .SFxAccessTokenLabel , accessToken )
376
+ }
377
+ }
378
+ }
379
+ }
380
+
339
381
func initJSONResponse (s string ) []byte {
340
382
respBody , err := json .Marshal (s )
341
383
if err != nil {
0 commit comments