123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- package frankenphp
- import (
- "path/filepath"
- "regexp"
- "sync"
- "time"
- "github.com/prometheus/client_golang/prometheus"
- )
- var metricsNameRegex = regexp.MustCompile(`\W+`)
- var metricsNameFixRegex = regexp.MustCompile(`^_+|_+$`)
- const (
- StopReasonCrash = iota
- StopReasonRestart
- StopReasonShutdown
- )
- type StopReason int
- type Metrics interface {
- // StartWorker collects started workers
- StartWorker(name string)
- // ReadyWorker collects ready workers
- ReadyWorker(name string)
- // StopWorker collects stopped workers
- StopWorker(name string, reason StopReason)
- // 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 collects started worker requests
- StartWorkerRequest(name string)
- Shutdown()
- }
- type nullMetrics struct{}
- func (n nullMetrics) StartWorker(string) {
- }
- func (n nullMetrics) ReadyWorker(string) {
- }
- func (n nullMetrics) StopWorker(string, StopReason) {
- }
- func (n nullMetrics) TotalWorkers(string, int) {
- }
- func (n nullMetrics) TotalThreads(int) {
- }
- func (n nullMetrics) StartRequest() {
- }
- func (n nullMetrics) StopRequest() {
- }
- func (n nullMetrics) StopWorkerRequest(string, time.Duration) {
- }
- func (n nullMetrics) StartWorkerRequest(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
- readyWorkers map[string]prometheus.Gauge
- workerCrashes map[string]prometheus.Counter
- workerRestarts map[string]prometheus.Counter
- 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) ReadyWorker(name string) {
- if _, ok := m.totalWorkers[name]; !ok {
- return
- }
- m.readyWorkers[name].Inc()
- }
- func (m *PrometheusMetrics) StopWorker(name string, reason StopReason) {
- m.busyThreads.Dec()
- // tests do not register workers before starting them
- if _, ok := m.totalWorkers[name]; !ok {
- return
- }
- m.totalWorkers[name].Dec()
- m.readyWorkers[name].Dec()
- if reason == StopReasonCrash {
- m.workerCrashes[name].Inc()
- } else if reason == StopReasonRestart {
- m.workerRestarts[name].Inc()
- } else if reason == StopReasonShutdown {
- 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, _ 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.workerCrashes[identity]; !ok {
- m.workerCrashes[identity] = prometheus.NewCounter(prometheus.CounterOpts{
- Namespace: "frankenphp",
- Subsystem: subsystem,
- Name: "worker_crashes",
- Help: "Number of PHP worker crashes for this worker",
- })
- m.registry.MustRegister(m.workerCrashes[identity])
- }
- if _, ok := m.workerRestarts[identity]; !ok {
- m.workerRestarts[identity] = prometheus.NewCounter(prometheus.CounterOpts{
- Namespace: "frankenphp",
- Subsystem: subsystem,
- Name: "worker_restarts",
- Help: "Number of PHP worker restarts for this worker",
- })
- m.registry.MustRegister(m.workerRestarts[identity])
- }
- if _, ok := m.readyWorkers[identity]; !ok {
- m.readyWorkers[identity] = prometheus.NewGauge(prometheus.GaugeOpts{
- Namespace: "frankenphp",
- Subsystem: subsystem,
- Name: "ready_workers",
- Help: "Running workers that have successfully called frankenphp_handle_request at least once",
- })
- m.registry.MustRegister(m.readyWorkers[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)
- }
- for _, c := range m.workerCrashes {
- m.registry.Unregister(c)
- }
- for _, c := range m.workerRestarts {
- m.registry.Unregister(c)
- }
- for _, g := range m.readyWorkers {
- m.registry.Unregister(g)
- }
- 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.workerRestarts = map[string]prometheus.Counter{}
- m.workerCrashes = map[string]prometheus.Counter{}
- m.readyWorkers = map[string]prometheus.Gauge{}
- 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{},
- workerRestarts: map[string]prometheus.Counter{},
- workerCrashes: map[string]prometheus.Counter{},
- readyWorkers: map[string]prometheus.Gauge{},
- }
- m.registry.MustRegister(m.totalThreads)
- m.registry.MustRegister(m.busyThreads)
- return m
- }
|