forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 1
/
json.go
164 lines (142 loc) · 4.17 KB
/
json.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package json
import (
"encoding/json"
"io/ioutil"
"net/http"
"strconv"
"strings"
"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/common/cfgwarn"
"github.com/elastic/beats/metricbeat/helper"
"github.com/elastic/beats/metricbeat/mb"
"github.com/elastic/beats/metricbeat/mb/parse"
)
// init registers the MetricSet with the central registry.
// The New method will be called after the setup of the module and before starting to fetch data
func init() {
if err := mb.Registry.AddMetricSet("http", "json", New, hostParser); err != nil {
panic(err)
}
}
const (
// defaultScheme is the default scheme to use when it is not specified in the host config.
defaultScheme = "http"
// defaultPath is the dto use when it is not specified in the host config.
defaultPath = ""
)
var (
hostParser = parse.URLHostParserBuilder{
DefaultScheme: defaultScheme,
PathConfigKey: "path",
DefaultPath: defaultPath,
}.Build()
)
// MetricSet type defines all fields of the MetricSet
// As a minimum it must inherit the mb.BaseMetricSet fields, but can be extended with
// additional entries. These variables can be used to persist data or configuration between
// multiple fetch calls.
type MetricSet struct {
mb.BaseMetricSet
namespace string
http *helper.HTTP
method string
body string
requestEnabled bool
responseEnabled bool
deDotEnabled bool
}
// New create a new instance of the MetricSet
// Part of new is also setting up the configuration by processing additional
// configuration entries if needed.
func New(base mb.BaseMetricSet) (mb.MetricSet, error) {
cfgwarn.Beta("The http json metricset is in beta.")
config := struct {
Namespace string `config:"namespace" validate:"required"`
Method string `config:"method"`
Body string `config:"body"`
RequestEnabled bool `config:"request.enabled"`
ResponseEnabled bool `config:"response.enabled"`
DeDotEnabled bool `config:"dedot.enabled"`
}{
Method: "GET",
Body: "",
RequestEnabled: false,
ResponseEnabled: false,
DeDotEnabled: false,
}
if err := base.Module().UnpackConfig(&config); err != nil {
return nil, err
}
http := helper.NewHTTP(base)
http.SetMethod(config.Method)
http.SetBody([]byte(config.Body))
return &MetricSet{
BaseMetricSet: base,
namespace: config.Namespace,
method: config.Method,
body: config.Body,
http: http,
requestEnabled: config.RequestEnabled,
responseEnabled: config.ResponseEnabled,
deDotEnabled: config.DeDotEnabled,
}, nil
}
// Fetch methods implements the data gathering and data conversion to the right format
// It returns the event which is then forward to the output. In case of an error, a
// descriptive error must be returned.
func (m *MetricSet) Fetch() (common.MapStr, error) {
response, err := m.http.FetchResponse()
if err != nil {
return nil, err
}
defer response.Body.Close()
var jsonBody map[string]interface{}
var event map[string]interface{}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
err = json.Unmarshal(body, &jsonBody)
if err != nil {
return nil, err
}
if m.deDotEnabled {
event = common.DeDotJSON(jsonBody).(map[string]interface{})
} else {
event = jsonBody
}
if m.requestEnabled {
event[mb.ModuleDataKey] = common.MapStr{
"request": common.MapStr{
"headers": m.getHeaders(response.Request.Header),
"method": response.Request.Method,
"body": m.body,
},
}
}
if m.responseEnabled {
phrase := strings.TrimPrefix(response.Status, strconv.Itoa(response.StatusCode)+" ")
event[mb.ModuleDataKey] = common.MapStr{
"response": common.MapStr{
"code": response.StatusCode,
"phrase": phrase,
"headers": m.getHeaders(response.Header),
},
}
}
// Set dynamic namespace
event["_namespace"] = m.namespace
return event, nil
}
func (m *MetricSet) getHeaders(header http.Header) map[string]string {
headers := make(map[string]string)
for k, v := range header {
value := ""
for _, h := range v {
value += h + " ,"
}
value = strings.TrimRight(value, " ,")
headers[k] = value
}
return headers
}