Skip to content

Commit e103ef2

Browse files
committed
Updated metrics
1 parent 3ee5142 commit e103ef2

File tree

15 files changed

+194
-34
lines changed

15 files changed

+194
-34
lines changed

cmd/server/service.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
httpserver "github.com/mutablelogic/go-server/pkg/httpserver/config"
2424
ldap "github.com/mutablelogic/go-server/pkg/ldap/config"
2525
logger "github.com/mutablelogic/go-server/pkg/logger/config"
26+
metrics "github.com/mutablelogic/go-server/pkg/metrics/config"
2627
pg "github.com/mutablelogic/go-server/pkg/pgmanager/config"
2728
pgqueue "github.com/mutablelogic/go-server/pkg/pgqueue/config"
2829
)
@@ -220,6 +221,19 @@ func (cmd *ServiceRunCommand) Run(app server.Cmd) error {
220221
return nil
221222
}))
222223

224+
err = errors.Join(err, provider.Load("metrics", "main", func(ctx context.Context, label string, config server.Plugin) error {
225+
metrics := config.(*metrics.Config)
226+
227+
// Set the router
228+
if router, ok := ref.Provider(ctx).Task(ctx, "httprouter.main").(server.HTTPRouter); !ok || router == nil {
229+
return httpresponse.ErrInternalError.With("Invalid router")
230+
} else {
231+
metrics.Router = router
232+
}
233+
234+
return nil
235+
}))
236+
223237
if err != nil {
224238
return err
225239
}

pkg/httpresponse/write.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package httpresponse
2+
3+
import (
4+
"net/http"
5+
6+
// Packages
7+
"github.com/mutablelogic/go-server/pkg/types"
8+
)
9+
10+
///////////////////////////////////////////////////////////////////////////////
11+
// PUBLIC METHODS
12+
13+
// Write a custom response to the writer with a HTTP status code,
14+
// leave the actual writing of the response to the caller
15+
func Write(w http.ResponseWriter, code int, contentType string) {
16+
if w.Header().Get(types.ContentTypeHeader) == "" {
17+
w.Header().Set(types.ContentTypeHeader, contentType)
18+
}
19+
20+
// Modify the status code if it is not already set
21+
if code == 0 {
22+
code = http.StatusOK
23+
}
24+
25+
// Write the status code
26+
w.WriteHeader(code)
27+
}

pkg/logger/term.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ const (
5656
func (h *TermHandler) Handle(ctx context.Context, r slog.Record) error {
5757
var parts []any
5858

59+
parts = append(parts, r.Time.Format(timeFormat))
60+
5961
level := r.Level.String()
6062
switch r.Level {
6163
case slog.LevelDebug:
@@ -136,5 +138,5 @@ func attrs(attrs []slog.Attr, r slog.Record) ([]byte, error) {
136138
return true
137139
})
138140

139-
return json.MarshalIndent(kv, "", " ")
141+
return json.Marshal(kv)
140142
}

pkg/metrics/config/config.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package config
2+
3+
import (
4+
"context"
5+
6+
// Packages
7+
server "github.com/mutablelogic/go-server"
8+
metrics "github.com/mutablelogic/go-server/pkg/metrics"
9+
handler "github.com/mutablelogic/go-server/pkg/metrics/handler"
10+
schema "github.com/mutablelogic/go-server/pkg/metrics/schema"
11+
)
12+
13+
////////////////////////////////////////////////////////////////////////////////
14+
// TYPES
15+
16+
type Config struct {
17+
Router server.HTTPRouter `kong:"-"` // HTTP Router
18+
Path string `default:"metrics" help:"Path prefix"`
19+
Tags map[string]any `help:"Tags to add to all metrics"`
20+
}
21+
22+
type task struct {
23+
}
24+
25+
var _ server.Plugin = Config{}
26+
var _ server.Task = (*task)(nil)
27+
28+
////////////////////////////////////////////////////////////////////////////////
29+
// LIFECYCLE
30+
31+
func (c Config) New(ctx context.Context) (server.Task, error) {
32+
// Create a new metrics manager
33+
manager, err := metrics.New()
34+
if err != nil {
35+
return nil, err
36+
}
37+
38+
// Register HTTP handlers
39+
if c.Router != nil {
40+
handler.RegisterMetrics(ctx, c.Router, c.Path, manager)
41+
}
42+
43+
// Return the task
44+
return &task{}, nil
45+
}
46+
47+
////////////////////////////////////////////////////////////////////////////////
48+
// PRIVATE METHODS
49+
50+
func (task) Run(ctx context.Context) error {
51+
<-ctx.Done()
52+
return nil
53+
}
54+
55+
////////////////////////////////////////////////////////////////////////////////
56+
// MODULE
57+
58+
func (c Config) Name() string {
59+
return schema.SchemaName
60+
}
61+
62+
func (c Config) Description() string {
63+
return "Prometheus metrics"
64+
}

pkg/metrics/handler/metrics.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import (
77
// Packages
88
server "github.com/mutablelogic/go-server"
99
httpresponse "github.com/mutablelogic/go-server/pkg/httpresponse"
10-
"github.com/mutablelogic/go-server/pkg/metrics"
10+
metrics "github.com/mutablelogic/go-server/pkg/metrics"
11+
schema "github.com/mutablelogic/go-server/pkg/metrics/schema"
1112
)
1213

1314
///////////////////////////////////////////////////////////////////////////////
@@ -21,9 +22,14 @@ func RegisterMetrics(ctx context.Context, router server.HTTPRouter, prefix strin
2122
// Handle method
2223
switch r.Method {
2324
case http.MethodGet:
24-
_ = manager.Write(w)
25+
_ = writeMetrics(w, r, manager)
2526
default:
2627
_ = httpresponse.Error(w, httpresponse.Err(http.StatusMethodNotAllowed), r.Method)
2728
}
2829
})
2930
}
31+
32+
func writeMetrics(w http.ResponseWriter, _ *http.Request, manager *metrics.MetricManager) error {
33+
httpresponse.Write(w, http.StatusOK, schema.ContentTypeMetrics)
34+
return manager.Write(w)
35+
}

pkg/metrics/manager.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,25 @@ import (
1515
// MetricManager allows a number of metric families to be registered
1616
type MetricManager struct {
1717
sync.RWMutex
18+
labels []string
1819
names map[string]bool
1920
families []*schema.Family
2021
}
2122

2223
////////////////////////////////////////////////////////////////////////
2324
// LIFECYCLE
2425

25-
func New() *MetricManager {
26+
func New(labels ...any) (*MetricManager, error) {
2627
manager := new(MetricManager)
2728
manager.names = make(map[string]bool, 10)
2829
manager.families = make([]*schema.Family, 0, 10)
29-
return manager
30+
if labels, err := schema.LabelSet(labels...); err != nil {
31+
return nil, err
32+
} else {
33+
manager.labels = labels
34+
}
35+
36+
return manager, nil
3037
}
3138

3239
////////////////////////////////////////////////////////////////////////

pkg/metrics/manager_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import (
1313
func Test_Manager_00(t *testing.T) {
1414
assert := assert.New(t)
1515

16-
manager := metrics.New()
16+
manager, err := metrics.New()
17+
if !assert.NoError(err) {
18+
t.FailNow()
19+
}
1720
assert.NotNil(manager)
1821

1922
t.Run("00", func(t *testing.T) {

pkg/metrics/schema/family.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func NewMetricFamily(name string, opts ...Opt) (*Family, error) {
4949
/////////////////////////////////////////////////////////////////////
5050
// PUBLIC METHODS
5151

52-
func (f *Family) Write(w io.Writer) error {
52+
func (f *Family) Write(w io.Writer, labels ...string) error {
5353
var buf bytes.Buffer
5454
buf.WriteString("# TYPE " + f.nameWithUnit() + " " + f.Type + "\n")
5555
if f.Unit != "" {
@@ -61,7 +61,7 @@ func (f *Family) Write(w io.Writer) error {
6161

6262
// Write samples
6363
for _, sample := range f.Samples {
64-
if err := sample.Write(f.Name, &buf); err != nil {
64+
if err := sample.Write(&buf, f.Name); err != nil {
6565
return err
6666
}
6767
}

pkg/metrics/schema/labels.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package schema
2+
3+
import (
4+
"fmt"
5+
6+
// Packages
7+
httpresponse "github.com/mutablelogic/go-server/pkg/httpresponse"
8+
)
9+
10+
/////////////////////////////////////////////////////////////////////
11+
// PRIVATE METHODS
12+
13+
func LabelSet(labels ...any) ([]string, error) {
14+
labelset := make([]string, 0, len(labels)>>1)
15+
for i := 0; i < len(labels); i += 2 {
16+
key, ok := labels[i].(string)
17+
if !ok || key == "" {
18+
return nil, httpresponse.ErrBadRequest.Withf("Invalid label: %q", labels[i])
19+
} else if !reLabelName.MatchString(key) {
20+
return nil, httpresponse.ErrBadRequest.Withf("Invalid label: %q", key)
21+
}
22+
labelset = append(labelset, fmt.Sprintf("%v=%q", key, labels[i+1]))
23+
}
24+
return labelset, nil
25+
}

pkg/metrics/schema/sample.go

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func newSample(name string, fv *float64, iv *int64, labels ...any) (*Sample, err
8484
}
8585
}
8686

87-
labelset, err := labelSet(labels...)
87+
labelset, err := LabelSet(labels...)
8888
if err != nil {
8989
return nil, err
9090
}
@@ -100,7 +100,7 @@ func newSample(name string, fv *float64, iv *int64, labels ...any) (*Sample, err
100100
/////////////////////////////////////////////////////////////////////
101101
// PUBLIC METHODS
102102

103-
func (s *Sample) Write(prefix string, w io.Writer) error {
103+
func (s *Sample) Write(w io.Writer, prefix string) error {
104104
var buf bytes.Buffer
105105

106106
buf.WriteString(prefix)
@@ -136,20 +136,3 @@ func (s *Sample) Write(prefix string, w io.Writer) error {
136136
return nil
137137
}
138138
}
139-
140-
/////////////////////////////////////////////////////////////////////
141-
// PRIVATE METHODS
142-
143-
func labelSet(labels ...any) ([]string, error) {
144-
labelset := make([]string, 0, len(labels)>>1)
145-
for i := 0; i < len(labels); i += 2 {
146-
key, ok := labels[i].(string)
147-
if !ok || key == "" {
148-
return nil, httpresponse.ErrBadRequest.Withf("Invalid label: %q", labels[i])
149-
} else if !reLabelName.MatchString(key) {
150-
return nil, httpresponse.ErrBadRequest.Withf("Invalid label: %q", key)
151-
}
152-
labelset = append(labelset, fmt.Sprintf("%v=%q", key, labels[i+1]))
153-
}
154-
return labelset, nil
155-
}

0 commit comments

Comments
 (0)