Add php & server info metrics

This commit is contained in:
Philipp Holzer 2024-11-15 15:25:57 +01:00
parent 08888165fd
commit 64b1e9bf15
Signed by: nupplaPhil
GPG key ID: 24A7501396EB5432
4 changed files with 268 additions and 1 deletions

View file

@ -6,6 +6,7 @@ import (
"friendica-exporter/serverinfo"
"github.com/prometheus/client_golang/prometheus"
"github.com/sirupsen/logrus"
"strconv"
)
const (
@ -97,6 +98,30 @@ var (
metricPrefix+"reports_closed",
"Number of closed reports.",
nil, nil)
serverInfoDesc = prometheus.NewDesc(
metricPrefix+"server_info",
"Contains meta information about Server as labels. Value is always 1.",
[]string{"version"}, nil)
phpInfoDesc = prometheus.NewDesc(
metricPrefix+"php_info",
"Contains meta information about PHP as labels. Value is always 1.",
[]string{"version"}, nil)
phpMemoryLimitDesc = prometheus.NewDesc(
metricPrefix+"php_memory_limit_bytes",
"Configured PHP memory limit in bytes.",
nil, nil)
phpMaxUploadSizeDesc = prometheus.NewDesc(
metricPrefix+"php_upload_max_size_bytes",
"Configured maximum upload size in bytes.",
nil, nil)
phpMaxPostSizeDesc = prometheus.NewDesc(
metricPrefix+"php_post_max_size_bytes",
"Configured maximum post size in bytes.",
nil, nil)
databaseMaxAllowedPacketDesc = prometheus.NewDesc(
metricPrefix+"php_post_max_allowed_packet",
"Configured maximum allowed packet length to send to or receive from the server.",
nil, nil)
)
type friendicaCollector struct {
@ -148,6 +173,12 @@ func (c *friendicaCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- reportsNewest
ch <- reportsOpen
ch <- reportsClosed
ch <- serverInfoDesc
ch <- phpInfoDesc
ch <- phpMaxUploadSizeDesc
ch <- phpMaxPostSizeDesc
ch <- phpMemoryLimitDesc
ch <- databaseMaxAllowedPacketDesc
}
func (c *friendicaCollector) Collect(ch chan<- prometheus.Metric) {
@ -212,6 +243,10 @@ func readMetrics(ch chan<- prometheus.Metric, status *serverinfo.ServerInfo) err
return err
}
if err := collectPhpMetrics(ch, status); err != nil {
return err
}
return nil
}
@ -532,3 +567,60 @@ func collectPacketsPerDirection(ch chan<- prometheus.Metric, status *serverinfo.
return nil
}
func collectPhpMetrics(ch chan<- prometheus.Metric, status *serverinfo.ServerInfo) error {
metric, err := prometheus.NewConstMetric(phpInfoDesc, prometheus.GaugeValue, 1, status.Server.PHP.Version)
if err != nil {
return fmt.Errorf("error creating metric for %s: %w", phpInfoDesc, err)
}
ch <- metric
metric, err = prometheus.NewConstMetric(serverInfoDesc, prometheus.GaugeValue, 1, status.Server.Version)
if err != nil {
return fmt.Errorf("error creating metric for %s: %w", serverInfoDesc, err)
}
ch <- metric
metricVal, err := strconv.ParseFloat(status.Server.Database.MaxAllowedPacket, 64)
if err != nil {
return fmt.Errorf("error converting value to byte for %s: %w", databaseMaxAllowedPacketDesc, err)
}
metric, err = prometheus.NewConstMetric(databaseMaxAllowedPacketDesc, prometheus.GaugeValue, metricVal)
if err != nil {
return fmt.Errorf("error creating metric for %s: %w", databaseMaxAllowedPacketDesc, err)
}
ch <- metric
metrics := []struct {
desc *prometheus.Desc
value string
}{
{
desc: phpMemoryLimitDesc,
value: status.Server.PHP.MemoryLimit,
},
{
desc: phpMaxPostSizeDesc,
value: status.Server.PHP.PostMaxSize,
},
{
desc: phpMaxUploadSizeDesc,
value: status.Server.PHP.UploadMaxFilesize,
},
}
for _, m := range metrics {
metricVal, err := ConvertMemoryToBytes(m.value)
if err != nil {
return fmt.Errorf("error converting value to byte for %s: %w", m.desc, err)
}
metric, err = prometheus.NewConstMetric(m.desc, prometheus.GaugeValue, float64(metricVal))
if err != nil {
return fmt.Errorf("error creating metric for %s: %w", m.desc, err)
}
ch <- metric
}
return nil
}

View file

@ -0,0 +1,51 @@
package metrics
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
)
// ConvertMemoryToBytes converts a memory limit string into bytes.
// Supported units are: K (Kilobytes), M (Megabytes), G (Gigabytes), case-insensitive.
// Anything else is interpreted as bytes.
func ConvertMemoryToBytes(memory string) (uint64, error) {
// Trim whitespace
memory = strings.TrimSpace(memory)
// Regular expression to match the pattern (number + optional unit)
re := regexp.MustCompile(`(?i)^([\d.]+)([KMG]?)$`)
matches := re.FindStringSubmatch(memory)
if matches == nil {
return 0, errors.New("invalid memory format")
}
// Extract the numeric part and unit
numberStr := matches[1]
unit := strings.ToUpper(matches[2])
// Parse the number, truncate fractional parts as PHP does
number, err := strconv.ParseFloat(numberStr, 64)
if err != nil {
return 0, fmt.Errorf("invalid numeric value: %v", err)
}
numberInt := uint64(number) // Truncate fractional part by casting to uint64
// Convert to bytes based on the unit
var multiplier uint64
switch unit {
case "K":
multiplier = 1024 // Kilobytes
case "M":
multiplier = 1024 * 1024 // Megabytes
case "G":
multiplier = 1024 * 1024 * 1024 // Gigabytes
default:
multiplier = 1 // Bytes
}
// Calculate the result
return numberInt * multiplier, nil
}

View file

@ -0,0 +1,124 @@
package metrics
import (
"errors"
"friendica-exporter/internal/testutil"
"testing"
)
func TestConvertMemoryToBytes(t *testing.T) {
tt := []struct {
desc string
memory string
wantErr error
wantEqual uint64
}{
{
desc: "1 kilobyte (upper case)",
memory: "1K",
wantErr: nil,
wantEqual: 1024,
},
{
desc: "1 kilobyte (lower case)",
memory: "1k",
wantErr: nil,
wantEqual: 1024,
},
{
desc: "1 megabyte (upper case)",
memory: "1M",
wantErr: nil,
wantEqual: 1048576,
},
{
desc: "1 megabyte (lower case)",
memory: "1m",
wantErr: nil,
wantEqual: 1048576,
},
{
desc: "1 gigabyte (upper case)",
memory: "1G",
wantErr: nil,
wantEqual: 1073741824,
},
{
desc: "1 gigabyte (lower case)",
memory: "1g",
wantErr: nil,
wantEqual: 1073741824,
},
{
desc: "2 Kilobytes",
memory: "2K",
wantErr: nil,
wantEqual: 2048,
},
{
desc: "5 gigabytes",
memory: "5g",
wantErr: nil,
wantEqual: 5368709120,
},
{
desc: "0.5 Megabyte",
memory: "0.5M",
wantErr: nil,
wantEqual: 0,
},
{
desc: "10.7 Kilobytes",
memory: "10.7k",
wantErr: nil,
wantEqual: 10240,
},
{
desc: "12345234 Bytes",
memory: "12345234",
wantErr: nil,
wantEqual: 12345234,
},
{
desc: "Invalid",
memory: "invalid",
wantErr: errors.New("invalid memory format"),
wantEqual: 0,
},
{
desc: "1234X",
memory: "1234X",
wantErr: errors.New("invalid memory format"),
wantEqual: 0,
},
{
desc: "512 Megabytes",
memory: "512M",
wantErr: nil,
wantEqual: 536870912,
},
{
desc: "CopyPasteError",
memory: "512M512M",
wantErr: errors.New("invalid memory format"),
wantEqual: 0,
},
}
for _, tc := range tt {
tc := tc
t.Run(tc.desc, func(t *testing.T) {
t.Parallel()
equal, err := ConvertMemoryToBytes(tc.memory)
if !testutil.EqualErrorMessage(err, tc.wantErr) {
t.Errorf("got error %q, want %q", err, tc.wantErr)
}
if equal != tc.wantEqual {
t.Errorf("got equal %v, want %v", equal, tc.wantEqual)
}
})
}
}

View file

@ -108,7 +108,7 @@ type Update struct {
type PHP struct {
Version string `json:"version"`
UploadMaxFilesize string `json:"upload_max_filesize"`
PostMaxSize string `json:"post_max_filesize"`
PostMaxSize string `json:"post_max_size"`
MemoryLimit string `json:"memory_limit"`
}