-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Beats state reporting #7075
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ func Start(cfg *common.Config) { | |
|
||
// register handlers | ||
mux.HandleFunc("/", rootHandler()) | ||
mux.HandleFunc("/state", stateHandler) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if it would make more sense to call this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm thinking of also adding information about the datasets, the output used. But not sure where it will evolve into. We probably need soon a more general discussion about api endpoint naming and structure. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SGTM, I guess we don't have a hard commit on these names, so we can change them later if needed (?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, API endpoints are not documented so far and we can still change them. |
||
mux.HandleFunc("/stats", statsHandler) | ||
mux.HandleFunc("/dataset", datasetHandler) | ||
|
||
|
@@ -61,12 +62,21 @@ func rootHandler() func(http.ResponseWriter, *http.Request) { | |
|
||
w.Header().Set("Content-Type", "application/json; charset=utf-8") | ||
|
||
data := monitoring.CollectStructSnapshot(monitoring.GetNamespace("state").GetRegistry(), monitoring.Full, false) | ||
data := monitoring.CollectStructSnapshot(monitoring.GetNamespace("info").GetRegistry(), monitoring.Full, false) | ||
|
||
print(w, data, r.URL) | ||
} | ||
} | ||
|
||
// stateHandler reports state metrics | ||
func stateHandler(w http.ResponseWriter, r *http.Request) { | ||
w.Header().Set("Content-Type", "application/json; charset=utf-8") | ||
|
||
data := monitoring.CollectStructSnapshot(monitoring.GetNamespace("state").GetRegistry(), monitoring.Full, false) | ||
|
||
print(w, data, r.URL) | ||
} | ||
|
||
// statsHandler report expvar and all libbeat/monitoring metrics | ||
func statsHandler(w http.ResponseWriter, r *http.Request) { | ||
w.Header().Set("Content-Type", "application/json; charset=utf-8") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,35 +17,27 @@ | |
|
||
package monitoring | ||
|
||
import ( | ||
"sync" | ||
) | ||
import "sync" | ||
|
||
var namespaces = struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jsoriano This code change as I also already had it change in this PR. Let me now if my implementation also looks good to you. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LGTM |
||
sync.Mutex | ||
m map[string]*Namespace | ||
}{ | ||
m: make(map[string]*Namespace), | ||
} | ||
var namespaces = NewNamespaces() | ||
|
||
// Namespace contains the name of the namespace and it's registry | ||
type Namespace struct { | ||
name string | ||
registry *Registry | ||
} | ||
|
||
func newNamespace(name string) *Namespace { | ||
n := &Namespace{ | ||
name: name, | ||
} | ||
return n | ||
} | ||
|
||
// GetNamespace gets the namespace with the given name. | ||
// If the namespace does not exist yet, a new one is created. | ||
func GetNamespace(name string) *Namespace { | ||
namespaces.Lock() | ||
defer namespaces.Unlock() | ||
|
||
n, ok := namespaces.m[name] | ||
if !ok { | ||
n = &Namespace{name: name} | ||
namespaces.m[name] = n | ||
} | ||
return n | ||
return namespaces.Get(name) | ||
} | ||
|
||
// SetRegistry sets the registry of the namespace | ||
|
@@ -60,3 +52,28 @@ func (n *Namespace) GetRegistry() *Registry { | |
} | ||
return n.registry | ||
} | ||
|
||
// Namespaces is a list of Namespace structs | ||
type Namespaces struct { | ||
sync.Mutex | ||
namespaces map[string]*Namespace | ||
} | ||
|
||
// NewNamespaces creates a new namespaces list | ||
func NewNamespaces() *Namespaces { | ||
return &Namespaces{ | ||
namespaces: map[string]*Namespace{}, | ||
} | ||
} | ||
|
||
// Get returns the namespace for the given key. If the key does not exist, new namespace is created. | ||
func (n *Namespaces) Get(key string) *Namespace { | ||
n.Lock() | ||
defer n.Unlock() | ||
if namespace, ok := n.namespaces[key]; ok { | ||
return namespace | ||
} | ||
|
||
n.namespaces[key] = newNamespace(key) | ||
return n.namespaces[key] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ import ( | |
"fmt" | ||
|
||
"github.com/elastic/beats/libbeat/common" | ||
"github.com/elastic/beats/libbeat/logp" | ||
"github.com/elastic/beats/libbeat/monitoring/report" | ||
esout "github.com/elastic/beats/libbeat/outputs/elasticsearch" | ||
"github.com/elastic/beats/libbeat/publisher" | ||
|
@@ -38,7 +39,6 @@ var ( | |
actMonitoringBeats = common.MapStr{ | ||
"index": common.MapStr{ | ||
"_index": "", | ||
"_type": "beats_stats", | ||
"_routing": nil, | ||
}, | ||
} | ||
|
@@ -96,23 +96,57 @@ func (c *publishClient) Close() error { | |
|
||
func (c *publishClient) Publish(batch publisher.Batch) error { | ||
events := batch.Events() | ||
bulk := make([]interface{}, 0, 2*len(events)) | ||
var failed []publisher.Event | ||
var reason error | ||
for _, event := range events { | ||
bulk = append(bulk, | ||
actMonitoringBeats, report.Event{ | ||
|
||
// Extract time | ||
t, err := event.Content.Meta.GetValue("type") | ||
if err != nil { | ||
logp.Err("Type not available in monitoring reported. Please report this error: %s", err) | ||
continue | ||
} | ||
|
||
var params = map[string]string{} | ||
// Copy params | ||
for k, v := range c.params { | ||
params[k] = v | ||
} | ||
// Extract potential additional params | ||
p, err := event.Content.Meta.GetValue("params") | ||
if err == nil { | ||
p2, ok := p.(map[string]string) | ||
if ok { | ||
for k, v := range p2 { | ||
params[k] = v | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about us copying/allocating contents if required only? This loop rebuilds params every single time, for every event. If the batch contains multiple events, with only one event having params set, we might loose some params again. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is c.params and params via 'meta' always configured? If not, we can reduce some copying here:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As each event contains the interval param in meta and it's required at the moment, I think we have to reprocess it every time. What do you mean by we |
||
actMonitoringBeats.Put("index._type", t) | ||
|
||
bulk := [2]interface{}{ | ||
actMonitoringBeats, | ||
report.Event{ | ||
Timestamp: event.Content.Timestamp, | ||
Fields: event.Content.Fields, | ||
}) | ||
} | ||
}, | ||
} | ||
|
||
_, err := c.es.BulkWith("_xpack", "monitoring", c.params, nil, bulk) | ||
if err != nil { | ||
batch.Retry() | ||
return err | ||
// Currently one request per event is sent. Reason is that each event can contain different | ||
// interval params and X-Pack requires to send the interval param. | ||
_, err = c.es.BulkWith("_xpack", "monitoring", params, nil, bulk[:]) | ||
if err != nil { | ||
failed = append(failed, event) | ||
reason = err | ||
} | ||
} | ||
|
||
batch.ACK() | ||
return nil | ||
if len(failed) > 0 { | ||
batch.RetryEvents(failed) | ||
} else { | ||
batch.ACK() | ||
} | ||
return reason | ||
} | ||
|
||
func (c *publishClient) Test(d testing.Driver) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@urso I have second thoughts if we even want to add this to the config file, meaning should we even recommend users to change it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the reference config. I don't really see a problem in configuring the interval. Allows users to reduce number of documents for monitoring if required.