mirror of
https://github.com/openfaas/faas.git
synced 2025-06-10 09:16:48 +00:00
This commit adds namespace in function names while logging metrics to prometheus, irrespective of the function is invoked with namespace suffix or not. This is also required to add multiple namespace support to faas-idler https://github.com/openfaas-incubator/faas-idler/issues/37 which is part of https://github.com/openfaas/faas-netes/issues/511 Signed-off-by: Vivek Singh <vivekkmr45@yahoo.in>
202 lines
5.1 KiB
Go
202 lines
5.1 KiB
Go
// Copyright (c) Alex Ellis 2017
|
|
// Copyright (c) 2018 OpenFaaS Author(s)
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
package metrics
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"path"
|
|
"time"
|
|
|
|
"log"
|
|
|
|
"github.com/openfaas/faas-provider/auth"
|
|
types "github.com/openfaas/faas-provider/types"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
// Exporter is a prometheus exporter
|
|
type Exporter struct {
|
|
metricOptions MetricOptions
|
|
services []types.FunctionStatus
|
|
credentials *auth.BasicAuthCredentials
|
|
}
|
|
|
|
// NewExporter creates a new exporter for the OpenFaaS gateway metrics
|
|
func NewExporter(options MetricOptions, credentials *auth.BasicAuthCredentials) *Exporter {
|
|
return &Exporter{
|
|
metricOptions: options,
|
|
services: []types.FunctionStatus{},
|
|
credentials: credentials,
|
|
}
|
|
}
|
|
|
|
// Describe is to describe the metrics for Prometheus
|
|
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
|
|
|
|
e.metricOptions.GatewayFunctionInvocation.Describe(ch)
|
|
e.metricOptions.GatewayFunctionsHistogram.Describe(ch)
|
|
e.metricOptions.ServiceReplicasGauge.Describe(ch)
|
|
e.metricOptions.GatewayFunctionInvocationStarted.Describe(ch)
|
|
|
|
e.metricOptions.ServiceMetrics.Counter.Describe(ch)
|
|
e.metricOptions.ServiceMetrics.Histogram.Describe(ch)
|
|
}
|
|
|
|
// Collect collects data to be consumed by prometheus
|
|
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
|
e.metricOptions.GatewayFunctionInvocation.Collect(ch)
|
|
e.metricOptions.GatewayFunctionsHistogram.Collect(ch)
|
|
|
|
e.metricOptions.GatewayFunctionInvocationStarted.Collect(ch)
|
|
|
|
e.metricOptions.ServiceReplicasGauge.Reset()
|
|
for _, service := range e.services {
|
|
var serviceName string
|
|
if len(service.Namespace) > 0 {
|
|
serviceName = fmt.Sprintf("%s.%s", service.Name, service.Namespace)
|
|
} else {
|
|
serviceName = service.Name
|
|
}
|
|
e.metricOptions.ServiceReplicasGauge.
|
|
WithLabelValues(serviceName).
|
|
Set(float64(service.Replicas))
|
|
}
|
|
|
|
e.metricOptions.ServiceReplicasGauge.Collect(ch)
|
|
|
|
e.metricOptions.ServiceMetrics.Counter.Collect(ch)
|
|
e.metricOptions.ServiceMetrics.Histogram.Collect(ch)
|
|
}
|
|
|
|
// StartServiceWatcher starts a ticker and collects service replica counts to expose to prometheus
|
|
func (e *Exporter) StartServiceWatcher(endpointURL url.URL, metricsOptions MetricOptions, label string, interval time.Duration) {
|
|
ticker := time.NewTicker(interval)
|
|
quit := make(chan struct{})
|
|
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
|
|
namespaces, err := e.getNamespaces(endpointURL)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
if len(namespaces) == 0 {
|
|
emptyNamespace := ""
|
|
services, err := e.getFunctions(endpointURL, emptyNamespace)
|
|
if err != nil {
|
|
log.Println(err)
|
|
continue
|
|
}
|
|
e.services = services
|
|
} else {
|
|
for _, namespace := range namespaces {
|
|
services, err := e.getFunctions(endpointURL, namespace)
|
|
if err != nil {
|
|
log.Println(err)
|
|
continue
|
|
}
|
|
e.services = append(e.services, services...)
|
|
}
|
|
}
|
|
|
|
break
|
|
case <-quit:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (e *Exporter) getHTTPClient(timeout time.Duration) http.Client {
|
|
|
|
return http.Client{
|
|
Transport: &http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
DialContext: (&net.Dialer{
|
|
Timeout: timeout,
|
|
KeepAlive: 0,
|
|
}).DialContext,
|
|
MaxIdleConns: 1,
|
|
DisableKeepAlives: true,
|
|
IdleConnTimeout: 120 * time.Millisecond,
|
|
ExpectContinueTimeout: 1500 * time.Millisecond,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (e *Exporter) getFunctions(endpointURL url.URL, namespace string) ([]types.FunctionStatus, error) {
|
|
timeout := 3 * time.Second
|
|
proxyClient := e.getHTTPClient(timeout)
|
|
|
|
endpointURL.Path = path.Join(endpointURL.Path, "/system/functions")
|
|
if len(namespace) > 0 {
|
|
q := endpointURL.Query()
|
|
q.Set("namespace", namespace)
|
|
endpointURL.RawQuery = q.Encode()
|
|
}
|
|
|
|
get, _ := http.NewRequest(http.MethodGet, endpointURL.String(), nil)
|
|
if e.credentials != nil {
|
|
get.SetBasicAuth(e.credentials.User, e.credentials.Password)
|
|
}
|
|
|
|
services := []types.FunctionStatus{}
|
|
res, err := proxyClient.Do(get)
|
|
if err != nil {
|
|
return services, err
|
|
}
|
|
|
|
bytesOut, readErr := ioutil.ReadAll(res.Body)
|
|
if readErr != nil {
|
|
return services, readErr
|
|
}
|
|
|
|
unmarshalErr := json.Unmarshal(bytesOut, &services)
|
|
if unmarshalErr != nil {
|
|
return services, unmarshalErr
|
|
}
|
|
return services, nil
|
|
}
|
|
|
|
func (e *Exporter) getNamespaces(endpointURL url.URL) ([]string, error) {
|
|
namespaces := []string{}
|
|
|
|
get, _ := http.NewRequest(http.MethodGet, endpointURL.String()+"system/namespaces", nil)
|
|
if e.credentials != nil {
|
|
get.SetBasicAuth(e.credentials.User, e.credentials.Password)
|
|
}
|
|
|
|
timeout := 3 * time.Second
|
|
proxyClient := e.getHTTPClient(timeout)
|
|
|
|
res, err := proxyClient.Do(get)
|
|
if err != nil {
|
|
return namespaces, err
|
|
}
|
|
|
|
if res.StatusCode == http.StatusNotFound {
|
|
return namespaces, nil
|
|
}
|
|
|
|
bytesOut, readErr := ioutil.ReadAll(res.Body)
|
|
if readErr != nil {
|
|
return namespaces, readErr
|
|
}
|
|
|
|
unmarshalErr := json.Unmarshal(bytesOut, &namespaces)
|
|
if unmarshalErr != nil {
|
|
return namespaces, unmarshalErr
|
|
}
|
|
return namespaces, nil
|
|
}
|