feat service: introduce labels for logs/recorder (#104)
This commit is contained in:
@@ -92,4 +92,8 @@ const (
|
|||||||
|
|
||||||
// MDKeyDialTimeout sets the dial timeout for outbound connections.
|
// MDKeyDialTimeout sets the dial timeout for outbound connections.
|
||||||
MDKeyDialTimeout = "dialTimeout"
|
MDKeyDialTimeout = "dialTimeout"
|
||||||
|
|
||||||
|
// MDKeyLabels holds static key/value labels attached to a service's
|
||||||
|
// records and logs.
|
||||||
|
MDKeyLabels = "labels"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
var observerPeriod time.Duration
|
var observerPeriod time.Duration
|
||||||
var netnsIn, netnsOut string
|
var netnsIn, netnsOut string
|
||||||
var dialTimeout time.Duration
|
var dialTimeout time.Duration
|
||||||
|
var labels map[string]string
|
||||||
|
|
||||||
var limiterRefreshInterval time.Duration
|
var limiterRefreshInterval time.Duration
|
||||||
var limiterCleanupInterval time.Duration
|
var limiterCleanupInterval time.Duration
|
||||||
@@ -150,6 +151,14 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
limiterRefreshInterval = mdutil.GetDuration(md, parsing.MDKeyLimiterRefreshInterval)
|
limiterRefreshInterval = mdutil.GetDuration(md, parsing.MDKeyLimiterRefreshInterval)
|
||||||
limiterCleanupInterval = mdutil.GetDuration(md, parsing.MDKeyLimiterCleanupInterval)
|
limiterCleanupInterval = mdutil.GetDuration(md, parsing.MDKeyLimiterCleanupInterval)
|
||||||
limiterScope = mdutil.GetString(md, parsing.MDKeyLimiterScope)
|
limiterScope = mdutil.GetString(md, parsing.MDKeyLimiterScope)
|
||||||
|
|
||||||
|
labels = mdutil.GetStringMapString(md, parsing.MDKeyLabels)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(labels) > 0 {
|
||||||
|
serviceLogger = serviceLogger.WithFields(map[string]any{
|
||||||
|
"labels": labels,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
listenerLogger := serviceLogger.WithFields(map[string]any{
|
listenerLogger := serviceLogger.WithFields(map[string]any{
|
||||||
@@ -349,6 +358,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
xservice.ObserverOption(registry.ObserverRegistry().Get(cfg.Observer)),
|
xservice.ObserverOption(registry.ObserverRegistry().Get(cfg.Observer)),
|
||||||
xservice.ObserverPeriodOption(observerPeriod),
|
xservice.ObserverPeriodOption(observerPeriod),
|
||||||
xservice.LoggerOption(serviceLogger),
|
xservice.LoggerOption(serviceLogger),
|
||||||
|
xservice.LabelsOption(labels),
|
||||||
)
|
)
|
||||||
|
|
||||||
serviceLogger.Infof("listening on %s/%s", s.Addr().String(), s.Addr().Network())
|
serviceLogger.Infof("listening on %s/%s", s.Addr().String(), s.Addr().Network())
|
||||||
|
|||||||
@@ -100,3 +100,17 @@ func ClientIDFromContext(ctx context.Context) ClientID {
|
|||||||
v, _ := ctx.Value(clientIDKey{}).(ClientID)
|
v, _ := ctx.Value(clientIDKey{}).(ClientID)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// labelsKey saves the static service labels.
|
||||||
|
type labelsKey struct{}
|
||||||
|
|
||||||
|
// ContextWithLabels returns a new context carrying the given service labels.
|
||||||
|
func ContextWithLabels(ctx context.Context, labels map[string]string) context.Context {
|
||||||
|
return context.WithValue(ctx, labelsKey{}, labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelsFromContext returns the service labels stored in the context, or nil.
|
||||||
|
func LabelsFromContext(ctx context.Context) map[string]string {
|
||||||
|
v, _ := ctx.Value(labelsKey{}).(map[string]string)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package ctx
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -149,6 +150,29 @@ func TestClientIDFromContext_WrongType(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextWithLabels(t *testing.T) {
|
||||||
|
labels := map[string]string{"tenant": "acme", "region": "eu"}
|
||||||
|
ctx := ContextWithLabels(context.Background(), labels)
|
||||||
|
|
||||||
|
got := LabelsFromContext(ctx)
|
||||||
|
if !reflect.DeepEqual(got, labels) {
|
||||||
|
t.Errorf("LabelsFromContext() = %v, want %v", got, labels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLabelsFromContext_Empty(t *testing.T) {
|
||||||
|
if got := LabelsFromContext(context.Background()); got != nil {
|
||||||
|
t.Errorf("LabelsFromContext(empty) = %v, want nil", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLabelsFromContext_WrongType(t *testing.T) {
|
||||||
|
ctx := context.WithValue(context.Background(), labelsKey{}, "not-a-map")
|
||||||
|
if got := LabelsFromContext(ctx); got != nil {
|
||||||
|
t.Errorf("LabelsFromContext(wrong type) = %v, want nil", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMultipleValuesInContext(t *testing.T) {
|
func TestMultipleValuesInContext(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = ContextWithSrcAddr(ctx, &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 8080})
|
ctx = ContextWithSrcAddr(ctx, &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 8080})
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-gost/core/recorder"
|
"github.com/go-gost/core/recorder"
|
||||||
|
xctx "github.com/go-gost/x/ctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -84,6 +85,7 @@ type DNSRecorderObject struct {
|
|||||||
type HandlerRecorderObject struct {
|
type HandlerRecorderObject struct {
|
||||||
Node string `json:"node,omitempty"`
|
Node string `json:"node,omitempty"`
|
||||||
Service string `json:"service"`
|
Service string `json:"service"`
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
Network string `json:"network"`
|
Network string `json:"network"`
|
||||||
RemoteAddr string `json:"remote"`
|
RemoteAddr string `json:"remote"`
|
||||||
LocalAddr string `json:"local"`
|
LocalAddr string `json:"local"`
|
||||||
@@ -115,6 +117,10 @@ func (p *HandlerRecorderObject) Record(ctx context.Context, r recorder.Recorder)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.Labels == nil {
|
||||||
|
p.Labels = xctx.LabelsFromContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(p)
|
data, err := json.Marshal(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ type options struct {
|
|||||||
observer observer.Observer
|
observer observer.Observer
|
||||||
observerPeriod time.Duration
|
observerPeriod time.Duration
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
|
labels map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option is a functional option for configuring a service.
|
// Option is a functional option for configuring a service.
|
||||||
@@ -113,6 +114,14 @@ func ObserverPeriodOption(period time.Duration) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LabelsOption sets the static labels attached to the service's records
|
||||||
|
// and logs.
|
||||||
|
func LabelsOption(labels map[string]string) Option {
|
||||||
|
return func(opts *options) {
|
||||||
|
opts.labels = labels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// LoggerOption sets the logger for the service. If not provided, a no-op
|
// LoggerOption sets the logger for the service. If not provided, a no-op
|
||||||
// logger is used.
|
// logger is used.
|
||||||
func LoggerOption(logger logger.Logger) Option {
|
func LoggerOption(logger logger.Logger) Option {
|
||||||
@@ -245,6 +254,10 @@ func (s *defaultService) Serve() error {
|
|||||||
sid := xid.New().String()
|
sid := xid.New().String()
|
||||||
ctx = xctx.ContextWithSid(ctx, xctx.Sid(sid))
|
ctx = xctx.ContextWithSid(ctx, xctx.Sid(sid))
|
||||||
|
|
||||||
|
if len(s.options.labels) > 0 {
|
||||||
|
ctx = xctx.ContextWithLabels(ctx, s.options.labels)
|
||||||
|
}
|
||||||
|
|
||||||
log := s.options.logger.WithFields(map[string]any{
|
log := s.options.logger.WithFields(map[string]any{
|
||||||
"sid": sid,
|
"sid": sid,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user