Files
x/recorder/redis.go
T
ginuerzh 64a16fafbc fix(recorder): propagate context, handle short writes, add nil guards, idempotent Close, and doc comments
- httpRecorder: use http.NewRequestWithContext instead of http.NewRequest
- tcpRecorder: loop Write to avoid short-write data loss
- fileRecorder: guard against nil out in Record and Close
- plugin/grpc, plugin/http: handle json.Marshal errors instead of ignoring
- fileRecorder, redis*Recorder: idempotent Close via sync.Once
- All exported symbols: add doc comments
2026-05-24 23:58:28 +08:00

191 lines
4.4 KiB
Go

package recorder
import (
"context"
"sync"
"github.com/go-gost/core/metrics"
"github.com/go-gost/core/recorder"
xmetrics "github.com/go-gost/x/metrics"
"github.com/go-redis/redis/v8"
)
type redisRecorderOptions struct {
recorder string
db int
username string
password string
key string
}
// RedisRecorderOption configures Redis recorder options.
type RedisRecorderOption func(opts *redisRecorderOptions)
// RecorderRedisRecorderOption sets the recorder name for metrics labeling.
func RecorderRedisRecorderOption(recorder string) RedisRecorderOption {
return func(opts *redisRecorderOptions) {
opts.recorder = recorder
}
}
// DBRedisRecorderOption sets the Redis database number.
func DBRedisRecorderOption(db int) RedisRecorderOption {
return func(opts *redisRecorderOptions) {
opts.db = db
}
}
// UsernameRedisRecorderOption sets the Redis username for authentication.
func UsernameRedisRecorderOption(username string) RedisRecorderOption {
return func(opts *redisRecorderOptions) {
opts.username = username
}
}
// PasswordRedisRecorderOption sets the Redis password for authentication.
func PasswordRedisRecorderOption(password string) RedisRecorderOption {
return func(opts *redisRecorderOptions) {
opts.password = password
}
}
// KeyRedisRecorderOption sets the Redis key for storing records.
func KeyRedisRecorderOption(key string) RedisRecorderOption {
return func(opts *redisRecorderOptions) {
opts.key = key
}
}
type redisSetRecorder struct {
recorder string
client *redis.Client
key string
closeOnce sync.Once
}
// RedisSetRecorder records data to a redis set.
func RedisSetRecorder(addr string, opts ...RedisRecorderOption) recorder.Recorder {
var options redisRecorderOptions
for _, opt := range opts {
opt(&options)
}
return &redisSetRecorder{
recorder: options.recorder,
client: redis.NewClient(&redis.Options{
Addr: addr,
Username: options.username,
Password: options.password,
DB: options.db,
}),
key: options.key,
}
}
func (r *redisSetRecorder) Record(ctx context.Context, b []byte, opts ...recorder.RecordOption) error {
xmetrics.GetCounter(xmetrics.MetricRecorderRecordsCounter, metrics.Labels{"recorder": r.recorder}).Inc()
if r.key == "" {
return nil
}
return r.client.SAdd(ctx, r.key, b).Err()
}
func (r *redisSetRecorder) Close() error {
var err error
r.closeOnce.Do(func() {
err = r.client.Close()
})
return err
}
type redisListRecorder struct {
recorder string
client *redis.Client
key string
closeOnce sync.Once
}
// RedisListRecorder records data to a redis list.
func RedisListRecorder(addr string, opts ...RedisRecorderOption) recorder.Recorder {
var options redisRecorderOptions
for _, opt := range opts {
opt(&options)
}
return &redisListRecorder{
recorder: options.recorder,
client: redis.NewClient(&redis.Options{
Addr: addr,
Username: options.username,
Password: options.password,
DB: options.db,
}),
key: options.key,
}
}
func (r *redisListRecorder) Record(ctx context.Context, b []byte, opts ...recorder.RecordOption) error {
xmetrics.GetCounter(xmetrics.MetricRecorderRecordsCounter, metrics.Labels{"recorder": r.recorder}).Inc()
if r.key == "" {
return nil
}
return r.client.LPush(ctx, r.key, b).Err()
}
func (r *redisListRecorder) Close() error {
var err error
r.closeOnce.Do(func() {
err = r.client.Close()
})
return err
}
type redisSortedSetRecorder struct {
recorder string
client *redis.Client
key string
closeOnce sync.Once
}
// RedisSortedSetRecorder records data to a redis sorted set.
func RedisSortedSetRecorder(addr string, opts ...RedisRecorderOption) recorder.Recorder {
var options redisRecorderOptions
for _, opt := range opts {
opt(&options)
}
return &redisSortedSetRecorder{
recorder: options.recorder,
client: redis.NewClient(&redis.Options{
Addr: addr,
Username: options.username,
Password: options.password,
DB: options.db,
}),
key: options.key,
}
}
func (r *redisSortedSetRecorder) Record(ctx context.Context, b []byte, opts ...recorder.RecordOption) error {
xmetrics.GetCounter(xmetrics.MetricRecorderRecordsCounter, metrics.Labels{"recorder": r.recorder}).Inc()
if r.key == "" {
return nil
}
return r.client.ZIncr(ctx, r.key, &redis.Z{
Score: 1,
Member: b,
}).Err()
}
func (r *redisSortedSetRecorder) Close() error {
var err error
r.closeOnce.Do(func() {
err = r.client.Close()
})
return err
}