123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- package frankenphp
- import (
- "github.com/prometheus/client_golang/prometheus"
- "path/filepath"
- "regexp"
- "sync"
- "time"
- )
- var metricsNameRegex = regexp.MustCompile(`\W+`)
- var metricsNameFixRegex = regexp.MustCompile(`^_+|_+$`)
- type Metrics interface {
- // StartWorker Collects started workers
- StartWorker(name string)
- // StopWorker Collects stopped workers
- StopWorker(name string)
- // TotalWorkers Collects expected workers
- TotalWorkers(name string, num int)
- // TotalThreads Collects total threads
- TotalThreads(num int)
- // StartRequest Collects started requests
- StartRequest()
- // StopRequest Collects stopped requests
- StopRequest()
- // StopWorkerRequest Collects stopped worker requests
- StopWorkerRequest(name string, duration time.Duration)
- StartWorkerRequest(name string)
- Shutdown()
- }
- type nullMetrics struct{}
- func (n nullMetrics) StartWorker(name string) {
- }
- func (n nullMetrics) StopWorker(name string) {
- }
- func (n nullMetrics) TotalWorkers(name string, num int) {
- }
- func (n nullMetrics) TotalThreads(num int) {
- }
- func (n nullMetrics) StartRequest() {
- }
- func (n nullMetrics) StopRequest() {
- }
- func (n nullMetrics) StopWorkerRequest(name string, duration time.Duration) {
- }
- func (n nullMetrics) StartWorkerRequest(name string) {
- }
- func (n nullMetrics) Shutdown() {
- }
- type PrometheusMetrics struct {
- registry prometheus.Registerer
- totalThreads prometheus.Counter
- busyThreads prometheus.Gauge
- totalWorkers map[string]prometheus.Gauge
- busyWorkers map[string]prometheus.Gauge
- workerRequestTime map[string]prometheus.Counter
- workerRequestCount map[string]prometheus.Counter
- mu sync.Mutex
- }
- func (m *PrometheusMetrics) StartWorker(name string) {
- m.busyThreads.Inc()
- // tests do not register workers before starting them
- if _, ok := m.totalWorkers[name]; !ok {
- return
- }
- m.totalWorkers[name].Inc()
- }
- func (m *PrometheusMetrics) StopWorker(name string) {
- m.busyThreads.Dec()
- // tests do not register workers before starting them
- if _, ok := m.totalWorkers[name]; !ok {
- return
- }
- m.totalWorkers[name].Dec()
- }
- func (m *PrometheusMetrics) getIdentity(name string) (string, error) {
- actualName, err := filepath.Abs(name)
- if err != nil {
- return name, err
- }
- return actualName, nil
- }
- func (m *PrometheusMetrics) TotalWorkers(name string, num int) {
- m.mu.Lock()
- defer m.mu.Unlock()
- identity, err := m.getIdentity(name)
- if err != nil {
- // do not create metrics, let error propagate when worker is started
- return
- }
- subsystem := getWorkerNameForMetrics(name)
- if _, ok := m.totalWorkers[identity]; !ok {
- m.totalWorkers[identity] = prometheus.NewGauge(prometheus.GaugeOpts{
- Namespace: "frankenphp",
- Subsystem: subsystem,
- Name: "total_workers",
- Help: "Total number of PHP workers for this worker",
- })
- m.registry.MustRegister(m.totalWorkers[identity])
- }
- if _, ok := m.busyWorkers[identity]; !ok {
- m.busyWorkers[identity] = prometheus.NewGauge(prometheus.GaugeOpts{
- Namespace: "frankenphp",
- Subsystem: subsystem,
- Name: "busy_workers",
- Help: "Number of busy PHP workers for this worker",
- })
- m.registry.MustRegister(m.busyWorkers[identity])
- }
- if _, ok := m.workerRequestTime[identity]; !ok {
- m.workerRequestTime[identity] = prometheus.NewCounter(prometheus.CounterOpts{
- Namespace: "frankenphp",
- Subsystem: subsystem,
- Name: "worker_request_time",
- })
- m.registry.MustRegister(m.workerRequestTime[identity])
- }
- if _, ok := m.workerRequestCount[identity]; !ok {
- m.workerRequestCount[identity] = prometheus.NewCounter(prometheus.CounterOpts{
- Namespace: "frankenphp",
- Subsystem: subsystem,
- Name: "worker_request_count",
- })
- m.registry.MustRegister(m.workerRequestCount[identity])
- }
- }
- func (m *PrometheusMetrics) TotalThreads(num int) {
- m.totalThreads.Add(float64(num))
- }
- func (m *PrometheusMetrics) StartRequest() {
- m.busyThreads.Inc()
- }
- func (m *PrometheusMetrics) StopRequest() {
- m.busyThreads.Dec()
- }
- func (m *PrometheusMetrics) StopWorkerRequest(name string, duration time.Duration) {
- if _, ok := m.workerRequestTime[name]; !ok {
- return
- }
- m.workerRequestCount[name].Inc()
- m.busyWorkers[name].Dec()
- m.workerRequestTime[name].Add(duration.Seconds())
- }
- func (m *PrometheusMetrics) StartWorkerRequest(name string) {
- if _, ok := m.busyWorkers[name]; !ok {
- return
- }
- m.busyWorkers[name].Inc()
- }
- func (m *PrometheusMetrics) Shutdown() {
- m.registry.Unregister(m.totalThreads)
- m.registry.Unregister(m.busyThreads)
- for _, g := range m.totalWorkers {
- m.registry.Unregister(g)
- }
- for _, g := range m.busyWorkers {
- m.registry.Unregister(g)
- }
- for _, c := range m.workerRequestTime {
- m.registry.Unregister(c)
- }
- for _, c := range m.workerRequestCount {
- m.registry.Unregister(c)
- }
- m.totalThreads = prometheus.NewCounter(prometheus.CounterOpts{
- Name: "frankenphp_total_threads",
- Help: "Total number of PHP threads",
- })
- m.busyThreads = prometheus.NewGauge(prometheus.GaugeOpts{
- Name: "frankenphp_busy_threads",
- Help: "Number of busy PHP threads",
- })
- m.totalWorkers = map[string]prometheus.Gauge{}
- m.busyWorkers = map[string]prometheus.Gauge{}
- m.workerRequestTime = map[string]prometheus.Counter{}
- m.workerRequestCount = map[string]prometheus.Counter{}
- m.registry.MustRegister(m.totalThreads)
- m.registry.MustRegister(m.busyThreads)
- }
- func getWorkerNameForMetrics(name string) string {
- name = metricsNameRegex.ReplaceAllString(name, "_")
- name = metricsNameFixRegex.ReplaceAllString(name, "")
- return name
- }
- func NewPrometheusMetrics(registry prometheus.Registerer) *PrometheusMetrics {
- if registry == nil {
- registry = prometheus.NewRegistry()
- }
- m := &PrometheusMetrics{
- registry: registry,
- totalThreads: prometheus.NewCounter(prometheus.CounterOpts{
- Name: "frankenphp_total_threads",
- Help: "Total number of PHP threads",
- }),
- busyThreads: prometheus.NewGauge(prometheus.GaugeOpts{
- Name: "frankenphp_busy_threads",
- Help: "Number of busy PHP threads",
- }),
- totalWorkers: map[string]prometheus.Gauge{},
- busyWorkers: map[string]prometheus.Gauge{},
- workerRequestTime: map[string]prometheus.Counter{},
- workerRequestCount: map[string]prometheus.Counter{},
- }
- m.registry.MustRegister(m.totalThreads)
- m.registry.MustRegister(m.busyThreads)
- return m
- }
|