-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement OPcache metrics See merge request crosscone/tool/opcache-exporter!1
- Loading branch information
Showing
9 changed files
with
402 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/opcache_exporter |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.PHONY: build | ||
|
||
build: | ||
go build -o opcache_exporter ./cmd/exporter |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"io/ioutil" | ||
"sync" | ||
|
||
"github.com/prometheus/client_golang/prometheus" | ||
|
||
fcgiclient "github.com/tomasen/fcgi_client" | ||
) | ||
|
||
const ( | ||
namespace = "opcache" | ||
) | ||
|
||
var ( | ||
enabledDesc = newMetric("enabled", "Is OPcache enabled.") | ||
cacheFullDesc = newMetric("cache_full", "Is OPcache full.") | ||
restartPendingDesc = newMetric("restart_pending", "Is restart pending.") | ||
restartInProgressDesc = newMetric("restart_in_progress", "Is restart in progress.") | ||
|
||
memoryUsageUsedMemoryDesc = newMetric("memory_usage_used_memory", "OPcache used memory.") | ||
memoryUsageFreeMemoryDesc = newMetric("memory_usage_free_memory", "OPcache free memory.") | ||
memoryUsageWastedMemoryDesc = newMetric("memory_usage_wasted_memory", "OPcache wasted memory.") | ||
memoryUsageCurrentWastedPercentageDesc = newMetric("memory_usage_current_wasted_percentage", "OPcache current wasted percentage.") | ||
|
||
internedStringsUsageBufferSizeDesc = newMetric("interned_strings_usage_buffer_size", "OPcache interned string buffer size.") | ||
internedStringsUsageUsedMemoryDesc = newMetric("interned_strings_usage_used_memory", "OPcache interned string used memory.") | ||
internedStringsUsageUsedFreeMemory = newMetric("interned_strings_usage_free_memory", "OPcache interned string free memory.") | ||
internedStringsUsageUsedNumerOfStrings = newMetric("interned_strings_usage_number_of_strings", "OPcache interned string number of strings.") | ||
|
||
statisticsNumCachedScripts = newMetric("statistics_num_cached_scripts", "OPcache statistics, number of cached scripts.") | ||
statisticsNumCachedKeys = newMetric("statistics_num_cached_keys", "OPcache statistics, number of cached keys.") | ||
statisticsMaxCachedKeys = newMetric("statistics_max_cached_keys", "OPcache statistics, max cached keys.") | ||
statisticsHits = newMetric("statistics_hits", "OPcache statistics, hits.") | ||
statisticsStartTime = newMetric("statistics_start_time", "OPcache statistics, start time.") | ||
statisticsLastRestartTime = newMetric("statistics_last_restart_time", "OPcache statistics, last restart time") | ||
statisticsOOMRestarts = newMetric("statistics_oom_restarts", "OPcache statistics, oom restarts") | ||
statisticsHashRestarts = newMetric("statistics_hash_restarts", "OPcache statistics, hash restarts") | ||
statisticsManualRestarts = newMetric("statistics_manual_restarts", "OPcache statistics, manual restarts") | ||
statisticsMisses = newMetric("statistics_misses", "OPcache statistics, misses") | ||
statisticsBlacklistMisses = newMetric("statistics_blacklist_misses", "OPcache statistics, blacklist misses") | ||
statisticsBlacklistMissRatio = newMetric("statistics_blacklist_miss_ratio", "OPcache statistics, blacklist miss ratio") | ||
statisticsHitRate = newMetric("statistics_hit_rate", "OPcache statistics, opcache hit rate") | ||
) | ||
|
||
func newMetric(metricName, metricDesc string) *prometheus.Desc { | ||
return prometheus.NewDesc(prometheus.BuildFQName(namespace, "", metricName), metricDesc, nil, nil) | ||
} | ||
|
||
func boolMetric(value bool) float64 { | ||
return map[bool]float64{true: 1, false: 0}[value] | ||
} | ||
|
||
func intMetric(value int64) float64 { | ||
return float64(value) | ||
} | ||
|
||
// Exporter collects OPcache status from the given FastCGI URI and exports them using | ||
// the prometheus metrics package. | ||
type Exporter struct { | ||
mutex sync.RWMutex | ||
|
||
uri string | ||
scriptPath string | ||
} | ||
|
||
// NewExporter returns an initialized Exporter. | ||
func NewExporter(uri, scriptPath string) (*Exporter, error) { | ||
exporter := &Exporter{ | ||
uri: uri, | ||
scriptPath: scriptPath, | ||
} | ||
|
||
return exporter, nil | ||
} | ||
|
||
// Describe describes all the metrics ever exported by the OPcache exporter. | ||
// Implements prometheus.Collector. | ||
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { | ||
ch <- enabledDesc | ||
ch <- cacheFullDesc | ||
ch <- restartPendingDesc | ||
ch <- restartInProgressDesc | ||
ch <- memoryUsageUsedMemoryDesc | ||
ch <- memoryUsageFreeMemoryDesc | ||
ch <- memoryUsageWastedMemoryDesc | ||
ch <- memoryUsageCurrentWastedPercentageDesc | ||
ch <- internedStringsUsageBufferSizeDesc | ||
ch <- internedStringsUsageUsedMemoryDesc | ||
ch <- internedStringsUsageUsedFreeMemory | ||
ch <- internedStringsUsageUsedNumerOfStrings | ||
ch <- statisticsNumCachedScripts | ||
ch <- statisticsNumCachedKeys | ||
ch <- statisticsMaxCachedKeys | ||
ch <- statisticsHits | ||
ch <- statisticsStartTime | ||
ch <- statisticsLastRestartTime | ||
ch <- statisticsOOMRestarts | ||
ch <- statisticsHashRestarts | ||
ch <- statisticsManualRestarts | ||
ch <- statisticsMisses | ||
ch <- statisticsBlacklistMisses | ||
ch <- statisticsBlacklistMissRatio | ||
ch <- statisticsHitRate | ||
} | ||
|
||
// Collect collects metrics of OPcache stats. | ||
// Implements prometheus.Collector. | ||
func (e *Exporter) Collect(ch chan<- prometheus.Metric) { | ||
e.mutex.Lock() // To protect metrics from concurrent collects. | ||
defer e.mutex.Unlock() | ||
|
||
status, err := e.getOpcacheStatus() | ||
if err != nil { | ||
status = new(OPcacheStatus) | ||
} | ||
|
||
ch <- prometheus.MustNewConstMetric(enabledDesc, prometheus.GaugeValue, boolMetric(status.OPcacheEnabled)) | ||
ch <- prometheus.MustNewConstMetric(cacheFullDesc, prometheus.GaugeValue, boolMetric(status.CacheFull)) | ||
ch <- prometheus.MustNewConstMetric(restartPendingDesc, prometheus.GaugeValue, boolMetric(status.RestartPending)) | ||
ch <- prometheus.MustNewConstMetric(restartInProgressDesc, prometheus.GaugeValue, boolMetric(status.RestartInProgress)) | ||
ch <- prometheus.MustNewConstMetric(memoryUsageUsedMemoryDesc, prometheus.GaugeValue, intMetric(status.MemoryUsage.UsedMemory)) | ||
ch <- prometheus.MustNewConstMetric(memoryUsageFreeMemoryDesc, prometheus.GaugeValue, intMetric(status.MemoryUsage.FreeMemory)) | ||
ch <- prometheus.MustNewConstMetric(memoryUsageWastedMemoryDesc, prometheus.GaugeValue, intMetric(status.MemoryUsage.WastedMemory)) | ||
ch <- prometheus.MustNewConstMetric(memoryUsageCurrentWastedPercentageDesc, prometheus.GaugeValue, status.MemoryUsage.CurrentWastedPercentage) | ||
ch <- prometheus.MustNewConstMetric(internedStringsUsageBufferSizeDesc, prometheus.GaugeValue, intMetric(status.InternedStringsUsage.BufferSize)) | ||
ch <- prometheus.MustNewConstMetric(internedStringsUsageUsedMemoryDesc, prometheus.GaugeValue, intMetric(status.InternedStringsUsage.UsedMemory)) | ||
ch <- prometheus.MustNewConstMetric(internedStringsUsageUsedFreeMemory, prometheus.GaugeValue, intMetric(status.InternedStringsUsage.FreeMemory)) | ||
ch <- prometheus.MustNewConstMetric(statisticsNumCachedScripts, prometheus.GaugeValue, intMetric(status.OPcacheStatistics.NumCachedScripts)) | ||
ch <- prometheus.MustNewConstMetric(statisticsNumCachedKeys, prometheus.GaugeValue, intMetric(status.OPcacheStatistics.NumCachedKeys)) | ||
ch <- prometheus.MustNewConstMetric(statisticsMaxCachedKeys, prometheus.GaugeValue, intMetric(status.OPcacheStatistics.MaxCachedKeys)) | ||
ch <- prometheus.MustNewConstMetric(statisticsHits, prometheus.GaugeValue, intMetric(status.OPcacheStatistics.Hits)) | ||
ch <- prometheus.MustNewConstMetric(statisticsStartTime, prometheus.GaugeValue, intMetric(status.OPcacheStatistics.StartTime)) | ||
ch <- prometheus.MustNewConstMetric(statisticsLastRestartTime, prometheus.GaugeValue, intMetric(status.OPcacheStatistics.LastRestartTime)) | ||
ch <- prometheus.MustNewConstMetric(statisticsOOMRestarts, prometheus.GaugeValue, intMetric(status.OPcacheStatistics.OOMRestarts)) | ||
ch <- prometheus.MustNewConstMetric(statisticsHashRestarts, prometheus.GaugeValue, intMetric(status.OPcacheStatistics.HashRestarts)) | ||
ch <- prometheus.MustNewConstMetric(statisticsManualRestarts, prometheus.GaugeValue, intMetric(status.OPcacheStatistics.ManualRestarts)) | ||
ch <- prometheus.MustNewConstMetric(statisticsMisses, prometheus.GaugeValue, intMetric(status.OPcacheStatistics.Misses)) | ||
ch <- prometheus.MustNewConstMetric(statisticsBlacklistMisses, prometheus.GaugeValue, intMetric(status.OPcacheStatistics.BlacklistMisses)) | ||
ch <- prometheus.MustNewConstMetric(statisticsBlacklistMissRatio, prometheus.GaugeValue, status.OPcacheStatistics.BlacklistMissRatio) | ||
ch <- prometheus.MustNewConstMetric(statisticsHitRate, prometheus.GaugeValue, status.OPcacheStatistics.OPcacheHitRate) | ||
} | ||
|
||
func (e *Exporter) getOpcacheStatus() (*OPcacheStatus, error) { | ||
client, err := fcgiclient.Dial("tcp", e.uri) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
env := map[string]string{ | ||
"SCRIPT_FILENAME": e.scriptPath, | ||
} | ||
|
||
resp, err := client.Get(env) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
content, err := ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
status := new(OPcacheStatus) | ||
err = json.Unmarshal(content, status) | ||
if err != nil { | ||
return nil, errors.New(string(content)) | ||
} | ||
|
||
return status, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package main | ||
|
||
import ( | ||
"io/ioutil" | ||
"net/http" | ||
"os" | ||
"strings" | ||
|
||
"github.com/go-kit/kit/log/level" | ||
"github.com/prometheus/client_golang/prometheus" | ||
"github.com/prometheus/client_golang/prometheus/promhttp" | ||
"github.com/prometheus/common/promlog" | ||
"github.com/prometheus/common/promlog/flag" | ||
"github.com/prometheus/common/version" | ||
"gopkg.in/alecthomas/kingpin.v2" | ||
) | ||
|
||
func main() { | ||
var ( | ||
listenAddress = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9101").String() | ||
metricsPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String() | ||
fcgiURI = kingpin.Flag("opcache.fcgi-uri", "Connection string to FastCGI server.").Default("127.0.0.1:9000").String() | ||
scriptPath = kingpin.Flag("opcache.script-path", "Path to PHP script which echoes json-encoded OPcache status").Default("").String() | ||
scriptDir = kingpin.Flag("opcache.script-dir", "Path to directory where temporary PHP file will be created").Default("").String() | ||
) | ||
|
||
promlogConfig := &promlog.Config{} | ||
flag.AddFlags(kingpin.CommandLine, promlogConfig) | ||
kingpin.HelpFlag.Short('h') | ||
kingpin.Parse() | ||
|
||
logger := promlog.New(promlogConfig) | ||
|
||
if err := run(*listenAddress, *metricsPath, *fcgiURI, *scriptPath, *scriptDir); err != nil { | ||
level.Error(logger).Log("msg", "Error starting HTTP server", "err", err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func run(listenAddress, metricsPath, fcgiURI, scriptPath, scriptDir string) error { | ||
if len(scriptPath) == 0 { | ||
file, err := ioutil.TempFile(scriptDir, "opcache.*.php") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
file.Chmod(0777) | ||
|
||
payload := "<?php\necho(json_encode(opcache_get_status()));\n" | ||
_, err = file.WriteString(payload) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
scriptPath = file.Name() | ||
|
||
defer file.Close() | ||
defer os.Remove(file.Name()) | ||
} | ||
|
||
exporter, err := NewExporter(fcgiURI, scriptPath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
prometheus.MustRegister(exporter) | ||
prometheus.MustRegister(version.NewCollector("opcache_exporter")) | ||
|
||
html := strings.Join([]string{ | ||
`<html>`, | ||
` <head>`, | ||
` <title>OPcache Exporter</title>`, | ||
` </head>`, | ||
` <body>`, | ||
` <h1>OPcache Exporter</h1>`, | ||
` <p>`, | ||
` <a href="` + metricsPath + `">Metrics</a>`, | ||
` </p>`, | ||
` </body>`, | ||
`</html>`, | ||
}, "\n") | ||
|
||
http.Handle(metricsPath, promhttp.Handler()) | ||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||
w.Write([]byte(html)) | ||
}) | ||
|
||
return http.ListenAndServe(listenAddress, nil) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package main | ||
|
||
// OPcacheStatus contains information about OPcache | ||
type OPcacheStatus struct { | ||
OPcacheEnabled bool `json:"opcache_enabled"` | ||
CacheFull bool `json:"cache_full"` | ||
RestartPending bool `json:"restart_pending"` | ||
RestartInProgress bool `json:"restart_in_progress"` | ||
MemoryUsage MemoryUsage `json:"memory_usage"` | ||
InternedStringsUsage InternedStringsUsage `json:"interned_strings_usage"` | ||
OPcacheStatistics OPcacheStatistics `json:"opcache_statistics"` | ||
} | ||
|
||
// MemoryUsage contains information about OPcache memory usage | ||
type MemoryUsage struct { | ||
UsedMemory int64 `json:"used_memory"` | ||
FreeMemory int64 `json:"free_memory"` | ||
WastedMemory int64 `json:"wasted_memory"` | ||
CurrentWastedPercentage float64 `json:"current_wasted_percentage"` | ||
} | ||
|
||
// InternedStringsUsage contains information about OPcache interned strings usage | ||
type InternedStringsUsage struct { | ||
BufferSize int64 `json:"buffer_size"` | ||
UsedMemory int64 `json:"used_memory"` | ||
FreeMemory int64 `json:"free_memory"` | ||
NumerOfStrings int64 `json:"number_of_strings"` | ||
} | ||
|
||
// OPcacheStatistics contains information about OPcache statistics | ||
type OPcacheStatistics struct { | ||
NumCachedScripts int64 `json:"num_cached_scripts"` | ||
NumCachedKeys int64 `json:"num_cached_keys"` | ||
MaxCachedKeys int64 `json:"max_cached_keys"` | ||
Hits int64 `json:"hits"` | ||
StartTime int64 `json:"start_time"` | ||
LastRestartTime int64 `json:"last_restart_time"` | ||
OOMRestarts int64 `json:"oom_restarts"` | ||
HashRestarts int64 `json:"hash_restarts"` | ||
ManualRestarts int64 `json:"manual_restarts"` | ||
Misses int64 `json:"misses"` | ||
BlacklistMisses int64 `json:"blacklist_misses"` | ||
BlacklistMissRatio float64 `json:"blacklist_miss_ratio"` | ||
OPcacheHitRate float64 `json:"opcache_hit_rate"` | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module gitlab.com/crosscone/tool/opcache-exporter | ||
|
||
go 1.13 | ||
|
||
require ( | ||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect | ||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect | ||
github.com/go-kit/kit v0.8.0 | ||
github.com/prometheus/client_golang v0.9.1 | ||
github.com/prometheus/common v0.4.1 | ||
github.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19 | ||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 | ||
) |
Oops, something went wrong.