feat service: introduce labels for logs/recorder (#104)

This commit is contained in:
Denis Galeev
2026-06-13 08:05:14 +03:00
committed by GitHub
parent eaeac2763e
commit 9f610fd163
6 changed files with 81 additions and 10 deletions
+4
View File
@@ -92,4 +92,8 @@ const (
// MDKeyDialTimeout sets the dial timeout for outbound connections.
MDKeyDialTimeout = "dialTimeout"
// MDKeyLabels holds static key/value labels attached to a service's
// records and logs.
MDKeyLabels = "labels"
)
+10
View File
@@ -115,6 +115,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
var observerPeriod time.Duration
var netnsIn, netnsOut string
var dialTimeout time.Duration
var labels map[string]string
var limiterRefreshInterval time.Duration
var limiterCleanupInterval time.Duration
@@ -150,6 +151,14 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
limiterRefreshInterval = mdutil.GetDuration(md, parsing.MDKeyLimiterRefreshInterval)
limiterCleanupInterval = mdutil.GetDuration(md, parsing.MDKeyLimiterCleanupInterval)
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{
@@ -349,6 +358,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
xservice.ObserverOption(registry.ObserverRegistry().Get(cfg.Observer)),
xservice.ObserverPeriodOption(observerPeriod),
xservice.LoggerOption(serviceLogger),
xservice.LabelsOption(labels),
)
serviceLogger.Infof("listening on %s/%s", s.Addr().String(), s.Addr().Network())
+14
View File
@@ -100,3 +100,17 @@ func ClientIDFromContext(ctx context.Context) ClientID {
v, _ := ctx.Value(clientIDKey{}).(ClientID)
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
}
+24
View File
@@ -3,6 +3,7 @@ package ctx
import (
"context"
"net"
"reflect"
"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) {
ctx := context.Background()
ctx = ContextWithSrcAddr(ctx, &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 8080})
+16 -10
View File
@@ -7,6 +7,7 @@ import (
"time"
"github.com/go-gost/core/recorder"
xctx "github.com/go-gost/x/ctx"
)
const (
@@ -82,16 +83,17 @@ type DNSRecorderObject struct {
// level — addresses, protocols, transferred bytes, and optional sub-records
// for HTTP, WebSocket, TLS, and DNS traffic.
type HandlerRecorderObject struct {
Node string `json:"node,omitempty"`
Service string `json:"service"`
Network string `json:"network"`
RemoteAddr string `json:"remote"`
LocalAddr string `json:"local"`
ClientAddr string `json:"client"`
SrcAddr string `json:"src"`
DstAddr string `json:"dst"`
Host string `json:"host"`
Proto string `json:"proto,omitempty"`
Node string `json:"node,omitempty"`
Service string `json:"service"`
Labels map[string]string `json:"labels,omitempty"`
Network string `json:"network"`
RemoteAddr string `json:"remote"`
LocalAddr string `json:"local"`
ClientAddr string `json:"client"`
SrcAddr string `json:"src"`
DstAddr string `json:"dst"`
Host string `json:"host"`
Proto string `json:"proto,omitempty"`
ClientIP string `json:"clientIP"`
ClientID string `json:"clientID,omitempty"`
HTTP *HTTPRecorderObject `json:"http,omitempty"`
@@ -115,6 +117,10 @@ func (p *HandlerRecorderObject) Record(ctx context.Context, r recorder.Recorder)
return nil
}
if p.Labels == nil {
p.Labels = xctx.LabelsFromContext(ctx)
}
data, err := json.Marshal(p)
if err != nil {
return err
+13
View File
@@ -43,6 +43,7 @@ type options struct {
observer observer.Observer
observerPeriod time.Duration
logger logger.Logger
labels map[string]string
}
// 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
// logger is used.
func LoggerOption(logger logger.Logger) Option {
@@ -245,6 +254,10 @@ func (s *defaultService) Serve() error {
sid := xid.New().String()
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{
"sid": sid,
})