Compare commits
8 Commits
v4.1.10
...
0144f175f0
Author | SHA1 | Date | |
---|---|---|---|
0144f175f0 | |||
b3539a32ab | |||
|
6a7223ea4a | ||
1a1b67866a | |||
b7c98da6d1 | |||
2c21cce076 | |||
c8946dcdc8 | |||
|
d342ff2626 |
48
.github/workflows/job_sync.yml
vendored
48
.github/workflows/job_sync.yml
vendored
@@ -18,34 +18,76 @@ jobs:
|
|||||||
echo "machine git.unistack.org login vtolstov password ${{ secrets.TOKEN_GITEA }}" >> /root/.netrc
|
echo "machine git.unistack.org login vtolstov password ${{ secrets.TOKEN_GITEA }}" >> /root/.netrc
|
||||||
echo "machine github.com login vtolstov password ${{ secrets.TOKEN_GITHUB }}" >> /root/.netrc
|
echo "machine github.com login vtolstov password ${{ secrets.TOKEN_GITHUB }}" >> /root/.netrc
|
||||||
|
|
||||||
|
- name: check master
|
||||||
|
id: check_master
|
||||||
|
run: |
|
||||||
|
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/master | cut -f1)
|
||||||
|
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/master | cut -f1)
|
||||||
|
echo "src_hash=$src_hash"
|
||||||
|
echo "dst_hash=$dst_hash"
|
||||||
|
if [ "$src_hash" != "$dst_hash" ]; then
|
||||||
|
echo "sync_needed=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "sync_needed=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
- name: sync master
|
- name: sync master
|
||||||
|
if: steps.check_master.outputs.sync_needed == 'true'
|
||||||
run: |
|
run: |
|
||||||
git clone --filter=blob:none --filter=tree:0 --branch master --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
|
git clone --filter=blob:none --filter=tree:0 --branch master --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
|
||||||
cd repo
|
cd repo
|
||||||
git remote add --no-tags --fetch --track master upstream https://github.com/${GITHUB_REPOSITORY}
|
git remote add --no-tags --fetch --track master upstream https://github.com/${GITHUB_REPOSITORY}
|
||||||
git merge upstream/master
|
git pull --rebase upstream master
|
||||||
git push upstream master --progress
|
git push upstream master --progress
|
||||||
git push origin master --progress
|
git push origin master --progress
|
||||||
cd ../
|
cd ../
|
||||||
rm -rf repo
|
rm -rf repo
|
||||||
|
|
||||||
|
- name: check v3
|
||||||
|
id: check_v3
|
||||||
|
run: |
|
||||||
|
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/v3 | cut -f1)
|
||||||
|
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/v3 | cut -f1)
|
||||||
|
echo "src_hash=$src_hash"
|
||||||
|
echo "dst_hash=$dst_hash"
|
||||||
|
if [ "$src_hash" != "$dst_hash" ]; then
|
||||||
|
echo "sync_needed=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "sync_needed=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
- name: sync v3
|
- name: sync v3
|
||||||
|
if: steps.check_v3.outputs.sync_needed == 'true'
|
||||||
run: |
|
run: |
|
||||||
git clone --filter=blob:none --filter=tree:0 --branch v3 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
|
git clone --filter=blob:none --filter=tree:0 --branch v3 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
|
||||||
cd repo
|
cd repo
|
||||||
git remote add --no-tags --fetch --track v3 upstream https://github.com/${GITHUB_REPOSITORY}
|
git remote add --no-tags --fetch --track v3 upstream https://github.com/${GITHUB_REPOSITORY}
|
||||||
git merge upstream/v3
|
git pull --rebase upstream v3
|
||||||
git push upstream v3 --progress
|
git push upstream v3 --progress
|
||||||
git push origin v3 --progress
|
git push origin v3 --progress
|
||||||
cd ../
|
cd ../
|
||||||
rm -rf repo
|
rm -rf repo
|
||||||
|
|
||||||
|
- name: check v4
|
||||||
|
id: check_v4
|
||||||
|
run: |
|
||||||
|
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/v4 | cut -f1)
|
||||||
|
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/v4 | cut -f1)
|
||||||
|
echo "src_hash=$src_hash"
|
||||||
|
echo "dst_hash=$dst_hash"
|
||||||
|
if [ "$src_hash" != "$dst_hash" ]; then
|
||||||
|
echo "sync_needed=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "sync_needed=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
- name: sync v4
|
- name: sync v4
|
||||||
|
if: steps.check_v4.outputs.sync_needed == 'true'
|
||||||
run: |
|
run: |
|
||||||
git clone --filter=blob:none --filter=tree:0 --branch v4 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
|
git clone --filter=blob:none --filter=tree:0 --branch v4 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
|
||||||
cd repo
|
cd repo
|
||||||
git remote add --no-tags --fetch --track v4 upstream https://github.com/${GITHUB_REPOSITORY}
|
git remote add --no-tags --fetch --track v4 upstream https://github.com/${GITHUB_REPOSITORY}
|
||||||
git merge upstream/v4
|
git pull --rebase upstream v4
|
||||||
git push upstream v4 --progress
|
git push upstream v4 --progress
|
||||||
git push origin v4 --progress
|
git push origin v4 --progress
|
||||||
cd ../
|
cd ../
|
||||||
|
@@ -4,18 +4,20 @@ package logger
|
|||||||
type Level int8
|
type Level int8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TraceLevel level usually used to find bugs, very verbose
|
// TraceLevel usually used to find bugs, very verbose
|
||||||
TraceLevel Level = iota - 2
|
TraceLevel Level = iota - 2
|
||||||
// DebugLevel level used only when enabled debugging
|
// DebugLevel used only when enabled debugging
|
||||||
DebugLevel
|
DebugLevel
|
||||||
// InfoLevel level used for general info about what's going on inside the application
|
// InfoLevel used for general info about what's going on inside the application
|
||||||
InfoLevel
|
InfoLevel
|
||||||
// WarnLevel level used for non-critical entries
|
// WarnLevel used for non-critical entries
|
||||||
WarnLevel
|
WarnLevel
|
||||||
// ErrorLevel level used for errors that should definitely be noted
|
// ErrorLevel used for errors that should definitely be noted
|
||||||
ErrorLevel
|
ErrorLevel
|
||||||
// FatalLevel level used for critical errors and then calls `os.Exit(1)`
|
// FatalLevel used for critical errors and then calls `os.Exit(1)`
|
||||||
FatalLevel
|
FatalLevel
|
||||||
|
// NoneLevel used to disable logging
|
||||||
|
NoneLevel
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns logger level string representation
|
// String returns logger level string representation
|
||||||
@@ -33,6 +35,8 @@ func (l Level) String() string {
|
|||||||
return "error"
|
return "error"
|
||||||
case FatalLevel:
|
case FatalLevel:
|
||||||
return "fatal"
|
return "fatal"
|
||||||
|
case NoneLevel:
|
||||||
|
return "none"
|
||||||
}
|
}
|
||||||
return "info"
|
return "info"
|
||||||
}
|
}
|
||||||
@@ -58,6 +62,8 @@ func ParseLevel(lvl string) Level {
|
|||||||
return ErrorLevel
|
return ErrorLevel
|
||||||
case FatalLevel.String():
|
case FatalLevel.String():
|
||||||
return FatalLevel
|
return FatalLevel
|
||||||
|
case NoneLevel.String():
|
||||||
|
return NoneLevel
|
||||||
}
|
}
|
||||||
return InfoLevel
|
return InfoLevel
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,7 @@ var (
|
|||||||
warnValue = slog.StringValue("warn")
|
warnValue = slog.StringValue("warn")
|
||||||
errorValue = slog.StringValue("error")
|
errorValue = slog.StringValue("error")
|
||||||
fatalValue = slog.StringValue("fatal")
|
fatalValue = slog.StringValue("fatal")
|
||||||
|
noneValue = slog.StringValue("none")
|
||||||
)
|
)
|
||||||
|
|
||||||
type wrapper struct {
|
type wrapper struct {
|
||||||
@@ -85,6 +86,8 @@ func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr {
|
|||||||
a.Value = errorValue
|
a.Value = errorValue
|
||||||
case lvl >= logger.FatalLevel:
|
case lvl >= logger.FatalLevel:
|
||||||
a.Value = fatalValue
|
a.Value = fatalValue
|
||||||
|
case lvl >= logger.NoneLevel:
|
||||||
|
a.Value = noneValue
|
||||||
default:
|
default:
|
||||||
a.Value = infoValue
|
a.Value = infoValue
|
||||||
}
|
}
|
||||||
@@ -316,6 +319,8 @@ func loggerToSlogLevel(level logger.Level) slog.Level {
|
|||||||
return slog.LevelDebug - 1
|
return slog.LevelDebug - 1
|
||||||
case logger.FatalLevel:
|
case logger.FatalLevel:
|
||||||
return slog.LevelError + 1
|
return slog.LevelError + 1
|
||||||
|
case logger.NoneLevel:
|
||||||
|
return slog.LevelError + 2
|
||||||
default:
|
default:
|
||||||
return slog.LevelInfo
|
return slog.LevelInfo
|
||||||
}
|
}
|
||||||
@@ -333,6 +338,8 @@ func slogToLoggerLevel(level slog.Level) logger.Level {
|
|||||||
return logger.TraceLevel
|
return logger.TraceLevel
|
||||||
case slog.LevelError + 1:
|
case slog.LevelError + 1:
|
||||||
return logger.FatalLevel
|
return logger.FatalLevel
|
||||||
|
case slog.LevelError + 2:
|
||||||
|
return logger.NoneLevel
|
||||||
default:
|
default:
|
||||||
return logger.InfoLevel
|
return logger.InfoLevel
|
||||||
}
|
}
|
||||||
|
@@ -36,6 +36,24 @@ func TestStacktrace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNoneLevel(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
l := NewLogger(logger.WithLevel(logger.NoneLevel), logger.WithOutput(buf),
|
||||||
|
WithHandlerFunc(slog.NewTextHandler),
|
||||||
|
logger.WithAddStacktrace(true),
|
||||||
|
)
|
||||||
|
if err := l.Init(logger.WithFields("key1", "val1")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Error(ctx, "msg1", errors.New("err"))
|
||||||
|
|
||||||
|
if buf.Len() != 0 {
|
||||||
|
t.Fatalf("logger none level not works, buf contains: %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDelayedBuffer(t *testing.T) {
|
func TestDelayedBuffer(t *testing.T) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
|
294
metadata/context.go
Normal file
294
metadata/context.go
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// In the metadata package, context and metadata are treated as immutable.
|
||||||
|
// Deep copies of metadata are made to keep things safe and correct.
|
||||||
|
// If a user takes a map and changes it across threads, it's their responsibility.
|
||||||
|
//
|
||||||
|
// 1. Incoming Context
|
||||||
|
//
|
||||||
|
// This context is provided by an external system and populated by the server or broker of the micro framework.
|
||||||
|
// It should not be modified. The idea is to extract all necessary data from it,
|
||||||
|
// validate the data, and transfer it into the current context.
|
||||||
|
// After that, only the current context should be used throughout the code.
|
||||||
|
//
|
||||||
|
// 2. Current Context
|
||||||
|
//
|
||||||
|
// This is the context used during the execution flow.
|
||||||
|
// You can add any needed metadata to it and pass it through your code.
|
||||||
|
//
|
||||||
|
// 3. Outgoing Context
|
||||||
|
//
|
||||||
|
// This context is for sending data to external systems.
|
||||||
|
// You can add what you need before sending it out.
|
||||||
|
// But it’s usually better to build and prepare this context right before making the external call,
|
||||||
|
// instead of changing it in many places.
|
||||||
|
//
|
||||||
|
// Execution Flow:
|
||||||
|
//
|
||||||
|
// [External System]
|
||||||
|
// ↓
|
||||||
|
// [Incoming Context]
|
||||||
|
// ↓
|
||||||
|
// [Extract & Validate Metadata from Incoming Context]
|
||||||
|
// ↓
|
||||||
|
// [Prepare Current Context]
|
||||||
|
// ↓
|
||||||
|
// [Enrich Current Context]
|
||||||
|
// ↓
|
||||||
|
// [Business Logic]
|
||||||
|
// ↓
|
||||||
|
// [Prepare Outgoing Context]
|
||||||
|
// ↓
|
||||||
|
// [External System Call]
|
||||||
|
|
||||||
|
type (
|
||||||
|
metadataCurrentKey struct{}
|
||||||
|
metadataIncomingKey struct{}
|
||||||
|
metadataOutgoingKey struct{}
|
||||||
|
|
||||||
|
rawMetadata struct {
|
||||||
|
md Metadata
|
||||||
|
added [][]string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewContext creates a new context with the provided Metadata attached.
|
||||||
|
// The Metadata must not be modified after calling this function.
|
||||||
|
func NewContext(ctx context.Context, md Metadata) context.Context {
|
||||||
|
return context.WithValue(ctx, metadataCurrentKey{}, rawMetadata{md: md})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIncomingContext creates a new context with the provided incoming Metadata attached.
|
||||||
|
// The Metadata must not be modified after calling this function.
|
||||||
|
func NewIncomingContext(ctx context.Context, md Metadata) context.Context {
|
||||||
|
return context.WithValue(ctx, metadataIncomingKey{}, rawMetadata{md: md})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOutgoingContext creates a new context with the provided outgoing Metadata attached.
|
||||||
|
// The Metadata must not be modified after calling this function.
|
||||||
|
func NewOutgoingContext(ctx context.Context, md Metadata) context.Context {
|
||||||
|
return context.WithValue(ctx, metadataOutgoingKey{}, rawMetadata{md: md})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendContext returns a new context with the provided key-value pairs (kv)
|
||||||
|
// merged with any existing metadata in the context. For a description of kv,
|
||||||
|
// please refer to the Pairs documentation.
|
||||||
|
func AppendContext(ctx context.Context, kv ...string) context.Context {
|
||||||
|
if len(kv)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: AppendContext got an odd number of input pairs for metadata: %d", len(kv)))
|
||||||
|
}
|
||||||
|
md, _ := ctx.Value(metadataCurrentKey{}).(rawMetadata)
|
||||||
|
added := make([][]string, len(md.added)+1)
|
||||||
|
copy(added, md.added)
|
||||||
|
kvCopy := make([]string, 0, len(kv))
|
||||||
|
for i := 0; i < len(kv); i += 2 {
|
||||||
|
kvCopy = append(kvCopy, strings.ToLower(kv[i]), kv[i+1])
|
||||||
|
}
|
||||||
|
added[len(added)-1] = kvCopy
|
||||||
|
return context.WithValue(ctx, metadataCurrentKey{}, rawMetadata{md: md.md, added: added})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendOutgoingContext returns a new context with the provided key-value pairs (kv)
|
||||||
|
// merged with any existing metadata in the context. For a description of kv,
|
||||||
|
// please refer to the Pairs documentation.
|
||||||
|
func AppendOutgoingContext(ctx context.Context, kv ...string) context.Context {
|
||||||
|
if len(kv)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: AppendOutgoingContext got an odd number of input pairs for metadata: %d", len(kv)))
|
||||||
|
}
|
||||||
|
md, _ := ctx.Value(metadataOutgoingKey{}).(rawMetadata)
|
||||||
|
added := make([][]string, len(md.added)+1)
|
||||||
|
copy(added, md.added)
|
||||||
|
kvCopy := make([]string, 0, len(kv))
|
||||||
|
for i := 0; i < len(kv); i += 2 {
|
||||||
|
kvCopy = append(kvCopy, strings.ToLower(kv[i]), kv[i+1])
|
||||||
|
}
|
||||||
|
added[len(added)-1] = kvCopy
|
||||||
|
return context.WithValue(ctx, metadataOutgoingKey{}, rawMetadata{md: md.md, added: added})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromContext retrieves a deep copy of the metadata from the context and returns it
|
||||||
|
// with a boolean indicating if it was found.
|
||||||
|
func FromContext(ctx context.Context) (Metadata, bool) {
|
||||||
|
raw, ok := ctx.Value(metadataCurrentKey{}).(rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
metadataSize := len(raw.md)
|
||||||
|
for i := range raw.added {
|
||||||
|
metadataSize += len(raw.added[i]) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(Metadata, metadataSize)
|
||||||
|
for k, v := range raw.md {
|
||||||
|
out[k] = copyOf(v)
|
||||||
|
}
|
||||||
|
for _, added := range raw.added {
|
||||||
|
if len(added)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: FromContext got an odd number of input pairs for metadata: %d", len(added)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(added); i += 2 {
|
||||||
|
out[added[i]] = append(out[added[i]], added[i+1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustContext retrieves a deep copy of the metadata from the context and panics
|
||||||
|
// if the metadata is not found.
|
||||||
|
func MustContext(ctx context.Context) Metadata {
|
||||||
|
md, ok := FromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
panic("missing metadata")
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromIncomingContext retrieves a deep copy of the metadata from the context and returns it
|
||||||
|
// with a boolean indicating if it was found.
|
||||||
|
func FromIncomingContext(ctx context.Context) (Metadata, bool) {
|
||||||
|
raw, ok := ctx.Value(metadataIncomingKey{}).(rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
metadataSize := len(raw.md)
|
||||||
|
for i := range raw.added {
|
||||||
|
metadataSize += len(raw.added[i]) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(Metadata, metadataSize)
|
||||||
|
for k, v := range raw.md {
|
||||||
|
out[k] = copyOf(v)
|
||||||
|
}
|
||||||
|
for _, added := range raw.added {
|
||||||
|
if len(added)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: FromIncomingContext got an odd number of input pairs for metadata: %d", len(added)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(added); i += 2 {
|
||||||
|
out[added[i]] = append(out[added[i]], added[i+1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustIncomingContext retrieves a deep copy of the metadata from the context and panics
|
||||||
|
// if the metadata is not found.
|
||||||
|
func MustIncomingContext(ctx context.Context) Metadata {
|
||||||
|
md, ok := FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
panic("missing metadata")
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromOutgoingContext retrieves a deep copy of the metadata from the context and returns it
|
||||||
|
// with a boolean indicating if it was found.
|
||||||
|
func FromOutgoingContext(ctx context.Context) (Metadata, bool) {
|
||||||
|
raw, ok := ctx.Value(metadataOutgoingKey{}).(rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataSize := len(raw.md)
|
||||||
|
for i := range raw.added {
|
||||||
|
metadataSize += len(raw.added[i]) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(Metadata, metadataSize)
|
||||||
|
for k, v := range raw.md {
|
||||||
|
out[k] = copyOf(v)
|
||||||
|
}
|
||||||
|
for _, added := range raw.added {
|
||||||
|
if len(added)%2 == 1 {
|
||||||
|
panic(fmt.Sprintf("metadata: FromOutgoingContext got an odd number of input pairs for metadata: %d", len(added)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(added); i += 2 {
|
||||||
|
out[added[i]] = append(out[added[i]], added[i+1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustOutgoingContext retrieves a deep copy of the metadata from the context and panics
|
||||||
|
// if the metadata is not found.
|
||||||
|
func MustOutgoingContext(ctx context.Context) Metadata {
|
||||||
|
md, ok := FromOutgoingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
panic("missing metadata")
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueFromCurrentContext retrieves a deep copy of the metadata for the given key
|
||||||
|
// from the context, performing a case-insensitive search if needed. Returns nil if not found.
|
||||||
|
func ValueFromCurrentContext(ctx context.Context, key string) []string {
|
||||||
|
md, ok := ctx.Value(metadataCurrentKey{}).(rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := md.md[key]; ok {
|
||||||
|
return copyOf(v)
|
||||||
|
}
|
||||||
|
for k, v := range md.md {
|
||||||
|
// Case-insensitive comparison: Metadata is a map, and there's no guarantee
|
||||||
|
// that the Metadata attached to the context is created using our helper
|
||||||
|
// functions.
|
||||||
|
if strings.EqualFold(k, key) {
|
||||||
|
return copyOf(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueFromIncomingContext retrieves a deep copy of the metadata for the given key
|
||||||
|
// from the context, performing a case-insensitive search if needed. Returns nil if not found.
|
||||||
|
func ValueFromIncomingContext(ctx context.Context, key string) []string {
|
||||||
|
raw, ok := ctx.Value(metadataIncomingKey{}).(rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := raw.md[key]; ok {
|
||||||
|
return copyOf(v)
|
||||||
|
}
|
||||||
|
for k, v := range raw.md {
|
||||||
|
// Case-insensitive comparison: Metadata is a map, and there's no guarantee
|
||||||
|
// that the Metadata attached to the context is created using our helper
|
||||||
|
// functions.
|
||||||
|
if strings.EqualFold(k, key) {
|
||||||
|
return copyOf(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueFromOutgoingContext retrieves a deep copy of the metadata for the given key
|
||||||
|
// from the context, performing a case-insensitive search if needed. Returns nil if not found.
|
||||||
|
func ValueFromOutgoingContext(ctx context.Context, key string) []string {
|
||||||
|
md, ok := ctx.Value(metadataOutgoingKey{}).(rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := md.md[key]; ok {
|
||||||
|
return copyOf(v)
|
||||||
|
}
|
||||||
|
for k, v := range md.md {
|
||||||
|
// Case-insensitive comparison: Metadata is a map, and there's no guarantee
|
||||||
|
// that the Metadata attached to the context is created using our helper
|
||||||
|
// functions.
|
||||||
|
if strings.EqualFold(k, key) {
|
||||||
|
return copyOf(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@@ -2,18 +2,18 @@
|
|||||||
package metadata
|
package metadata
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// HeaderTopic is the header name that contains topic name
|
// HeaderTopic is the header name that contains topic name.
|
||||||
HeaderTopic = "Micro-Topic"
|
HeaderTopic = "Micro-Topic"
|
||||||
// HeaderContentType specifies content type of message
|
// HeaderContentType specifies content type of message.
|
||||||
HeaderContentType = "Content-Type"
|
HeaderContentType = "Content-Type"
|
||||||
// HeaderEndpoint specifies endpoint in service
|
// HeaderEndpoint specifies endpoint in service.
|
||||||
HeaderEndpoint = "Micro-Endpoint"
|
HeaderEndpoint = "Micro-Endpoint"
|
||||||
// HeaderService specifies service
|
// HeaderService specifies service.
|
||||||
HeaderService = "Micro-Service"
|
HeaderService = "Micro-Service"
|
||||||
// HeaderTimeout specifies timeout of operation
|
// HeaderTimeout specifies timeout of operation.
|
||||||
HeaderTimeout = "Micro-Timeout"
|
HeaderTimeout = "Micro-Timeout"
|
||||||
// HeaderAuthorization specifies Authorization header
|
// HeaderAuthorization specifies Authorization header.
|
||||||
HeaderAuthorization = "Authorization"
|
HeaderAuthorization = "Authorization"
|
||||||
// HeaderXRequestID specifies request id
|
// HeaderXRequestID specifies request id.
|
||||||
HeaderXRequestID = "X-Request-Id"
|
HeaderXRequestID = "X-Request-Id"
|
||||||
)
|
)
|
||||||
|
7
metadata/helpers.go
Normal file
7
metadata/helpers.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package metadata
|
||||||
|
|
||||||
|
func copyOf(v []string) []string {
|
||||||
|
vals := make([]string, len(v))
|
||||||
|
copy(vals, v)
|
||||||
|
return vals
|
||||||
|
}
|
37
metadata/iterator.go
Normal file
37
metadata/iterator.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package metadata
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
|
type Iterator struct {
|
||||||
|
md Metadata
|
||||||
|
keys []string
|
||||||
|
cur int
|
||||||
|
cnt int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next advances the iterator to the next element.
|
||||||
|
func (iter *Iterator) Next(k *string, v *[]string) bool {
|
||||||
|
if iter.cur+1 > iter.cnt {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if k != nil && v != nil {
|
||||||
|
*k = iter.keys[iter.cur]
|
||||||
|
vv := iter.md[*k]
|
||||||
|
*v = make([]string, len(vv))
|
||||||
|
copy(*v, vv)
|
||||||
|
iter.cur++
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterator returns an iterator for iterating over metadata in sorted order.
|
||||||
|
func (md Metadata) Iterator() *Iterator {
|
||||||
|
iter := &Iterator{md: md, cnt: len(md)}
|
||||||
|
iter.keys = make([]string, 0, iter.cnt)
|
||||||
|
for k := range md {
|
||||||
|
iter.keys = append(iter.keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(iter.keys)
|
||||||
|
return iter
|
||||||
|
}
|
@@ -1,21 +1,18 @@
|
|||||||
package metadata
|
package metadata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// defaultMetadataSize used when need to init new Metadata
|
// defaultMetadataSize is used when initializing new Metadata.
|
||||||
var defaultMetadataSize = 2
|
var defaultMetadataSize = 2
|
||||||
|
|
||||||
// Metadata is a mapping from metadata keys to values. Users should use the following
|
// Metadata maps keys to values. Use the New, NewWithMetadata and Pairs functions to create it.
|
||||||
// two convenience functions New and Pairs to generate Metadata.
|
|
||||||
type Metadata map[string][]string
|
type Metadata map[string][]string
|
||||||
|
|
||||||
// New creates an zero Metadata.
|
// New creates a zero-value Metadata with the specified size.
|
||||||
func New(l int) Metadata {
|
func New(l int) Metadata {
|
||||||
if l == 0 {
|
if l == 0 {
|
||||||
l = defaultMetadataSize
|
l = defaultMetadataSize
|
||||||
@@ -24,7 +21,7 @@ func New(l int) Metadata {
|
|||||||
return md
|
return md
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWithMetadata creates an Metadata from a given key-value map.
|
// NewWithMetadata creates a Metadata from the provided key-value map.
|
||||||
func NewWithMetadata(m map[string]string) Metadata {
|
func NewWithMetadata(m map[string]string) Metadata {
|
||||||
md := make(Metadata, len(m))
|
md := make(Metadata, len(m))
|
||||||
for key, val := range m {
|
for key, val := range m {
|
||||||
@@ -33,8 +30,7 @@ func NewWithMetadata(m map[string]string) Metadata {
|
|||||||
return md
|
return md
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pairs returns an Metadata formed by the mapping of key, value ...
|
// Pairs returns a Metadata formed from the key-value mapping. It panics if the length of kv is odd.
|
||||||
// Pairs panics if len(kv) is odd.
|
|
||||||
func Pairs(kv ...string) Metadata {
|
func Pairs(kv ...string) Metadata {
|
||||||
if len(kv)%2 == 1 {
|
if len(kv)%2 == 1 {
|
||||||
panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
|
panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
|
||||||
@@ -46,12 +42,19 @@ func Pairs(kv ...string) Metadata {
|
|||||||
return md
|
return md
|
||||||
}
|
}
|
||||||
|
|
||||||
// Len returns the number of items in Metadata.
|
// Join combines multiple Metadatas into a single Metadata.
|
||||||
func (md Metadata) Len() int {
|
// The order of values for each key is determined by the order in which the Metadatas are provided to Join.
|
||||||
return len(md)
|
func Join(mds ...Metadata) Metadata {
|
||||||
|
out := Metadata{}
|
||||||
|
for _, md := range mds {
|
||||||
|
for k, v := range md {
|
||||||
|
out[k] = append(out[k], v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns a copy of Metadata.
|
// Copy returns a deep copy of Metadata.
|
||||||
func Copy(src Metadata) Metadata {
|
func Copy(src Metadata) Metadata {
|
||||||
out := make(Metadata, len(src))
|
out := make(Metadata, len(src))
|
||||||
for k, v := range src {
|
for k, v := range src {
|
||||||
@@ -60,7 +63,7 @@ func Copy(src Metadata) Metadata {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns a copy of Metadata.
|
// Copy returns a deep copy of Metadata.
|
||||||
func (md Metadata) Copy() Metadata {
|
func (md Metadata) Copy() Metadata {
|
||||||
out := make(Metadata, len(md))
|
out := make(Metadata, len(md))
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
@@ -69,7 +72,19 @@ func (md Metadata) Copy() Metadata {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsMap returns a copy of Metadata with map[string]string.
|
// CopyTo performs a deep copy of Metadata to the out.
|
||||||
|
func (md Metadata) CopyTo(out Metadata) {
|
||||||
|
for k, v := range md {
|
||||||
|
out[k] = copyOf(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of items in Metadata.
|
||||||
|
func (md Metadata) Len() int {
|
||||||
|
return len(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsMap returns a deep copy of Metadata as a map[string]string
|
||||||
func (md Metadata) AsMap() map[string]string {
|
func (md Metadata) AsMap() map[string]string {
|
||||||
out := make(map[string]string, len(md))
|
out := make(map[string]string, len(md))
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
@@ -78,8 +93,7 @@ func (md Metadata) AsMap() map[string]string {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsHTTP1 returns a copy of Metadata
|
// AsHTTP1 returns a deep copy of Metadata with keys converted to canonical MIME header key format.
|
||||||
// with CanonicalMIMEHeaderKey.
|
|
||||||
func (md Metadata) AsHTTP1() map[string][]string {
|
func (md Metadata) AsHTTP1() map[string][]string {
|
||||||
out := make(map[string][]string, len(md))
|
out := make(map[string][]string, len(md))
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
@@ -88,8 +102,7 @@ func (md Metadata) AsHTTP1() map[string][]string {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsHTTP1 returns a copy of Metadata
|
// AsHTTP2 returns a deep copy of Metadata with keys converted to lowercase.
|
||||||
// with strings.ToLower.
|
|
||||||
func (md Metadata) AsHTTP2() map[string][]string {
|
func (md Metadata) AsHTTP2() map[string][]string {
|
||||||
out := make(map[string][]string, len(md))
|
out := make(map[string][]string, len(md))
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
@@ -98,14 +111,10 @@ func (md Metadata) AsHTTP2() map[string][]string {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyTo copies Metadata to out.
|
// Get retrieves the values for a given key, checking the key in three formats:
|
||||||
func (md Metadata) CopyTo(out Metadata) {
|
// - exact case,
|
||||||
for k, v := range md {
|
// - lower case,
|
||||||
out[k] = copyOf(v)
|
// - canonical MIME header key format.
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get obtains the values for a given key.
|
|
||||||
func (md Metadata) Get(k string) []string {
|
func (md Metadata) Get(k string) []string {
|
||||||
v, ok := md[k]
|
v, ok := md[k]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -117,13 +126,12 @@ func (md Metadata) Get(k string) []string {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetJoined obtains the values for a given key
|
// GetJoined retrieves the values for a given key and joins them into a single string, separated by commas.
|
||||||
// with joined values with "," symbol
|
|
||||||
func (md Metadata) GetJoined(k string) string {
|
func (md Metadata) GetJoined(k string) string {
|
||||||
return strings.Join(md.Get(k), ",")
|
return strings.Join(md.Get(k), ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets the value of a given key with a slice of values.
|
// Set assigns the values to the given key.
|
||||||
func (md Metadata) Set(key string, vals ...string) {
|
func (md Metadata) Set(key string, vals ...string) {
|
||||||
if len(vals) == 0 {
|
if len(vals) == 0 {
|
||||||
return
|
return
|
||||||
@@ -131,8 +139,7 @@ func (md Metadata) Set(key string, vals ...string) {
|
|||||||
md[key] = vals
|
md[key] = vals
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append adds the values to key k, not overwriting what was already stored at
|
// Append adds values to the existing values for the given key.
|
||||||
// that key.
|
|
||||||
func (md Metadata) Append(key string, vals ...string) {
|
func (md Metadata) Append(key string, vals ...string) {
|
||||||
if len(vals) == 0 {
|
if len(vals) == 0 {
|
||||||
return
|
return
|
||||||
@@ -140,7 +147,10 @@ func (md Metadata) Append(key string, vals ...string) {
|
|||||||
md[key] = append(md[key], vals...)
|
md[key] = append(md[key], vals...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Del removes the values for a given keys k.
|
// Del removes the values for the given keys k. It checks and removes the keys in the following formats:
|
||||||
|
// - exact case,
|
||||||
|
// - lower case,
|
||||||
|
// - canonical MIME header key format.
|
||||||
func (md Metadata) Del(k ...string) {
|
func (md Metadata) Del(k ...string) {
|
||||||
for i := range k {
|
for i := range k {
|
||||||
delete(md, k[i])
|
delete(md, k[i])
|
||||||
@@ -148,303 +158,3 @@ func (md Metadata) Del(k ...string) {
|
|||||||
delete(md, textproto.CanonicalMIMEHeaderKey(k[i]))
|
delete(md, textproto.CanonicalMIMEHeaderKey(k[i]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join joins any number of Metadatas into a single Metadata.
|
|
||||||
//
|
|
||||||
// The order of values for each key is determined by the order in which the Metadatas
|
|
||||||
// containing those values are presented to Join.
|
|
||||||
func Join(mds ...Metadata) Metadata {
|
|
||||||
out := Metadata{}
|
|
||||||
for _, Metadata := range mds {
|
|
||||||
for k, v := range Metadata {
|
|
||||||
out[k] = append(out[k], v...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
type (
|
|
||||||
metadataIncomingKey struct{}
|
|
||||||
metadataOutgoingKey struct{}
|
|
||||||
metadataCurrentKey struct{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewContext creates a new context with Metadata attached. Metadata must
|
|
||||||
// not be modified after calling this function.
|
|
||||||
func NewContext(ctx context.Context, md Metadata) context.Context {
|
|
||||||
return context.WithValue(ctx, metadataCurrentKey{}, rawMetadata{md: md})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIncomingContext creates a new context with incoming Metadata attached. Metadata must
|
|
||||||
// not be modified after calling this function.
|
|
||||||
func NewIncomingContext(ctx context.Context, md Metadata) context.Context {
|
|
||||||
return context.WithValue(ctx, metadataIncomingKey{}, rawMetadata{md: md})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewOutgoingContext creates a new context with outgoing Metadata attached. If used
|
|
||||||
// in conjunction with AppendOutgoingContext, NewOutgoingContext will
|
|
||||||
// overwrite any previously-appended metadata. Metadata must not be modified after
|
|
||||||
// calling this function.
|
|
||||||
func NewOutgoingContext(ctx context.Context, md Metadata) context.Context {
|
|
||||||
return context.WithValue(ctx, metadataOutgoingKey{}, rawMetadata{md: md})
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendContext returns a new context with the provided kv merged
|
|
||||||
// with any existing metadata in the context. Please refer to the documentation
|
|
||||||
// of Pairs for a description of kv.
|
|
||||||
func AppendContext(ctx context.Context, kv ...string) context.Context {
|
|
||||||
if len(kv)%2 == 1 {
|
|
||||||
panic(fmt.Sprintf("metadata: AppendContext got an odd number of input pairs for metadata: %d", len(kv)))
|
|
||||||
}
|
|
||||||
md, _ := ctx.Value(metadataCurrentKey{}).(rawMetadata)
|
|
||||||
added := make([][]string, len(md.added)+1)
|
|
||||||
copy(added, md.added)
|
|
||||||
kvCopy := make([]string, 0, len(kv))
|
|
||||||
for i := 0; i < len(kv); i += 2 {
|
|
||||||
kvCopy = append(kvCopy, strings.ToLower(kv[i]), kv[i+1])
|
|
||||||
}
|
|
||||||
added[len(added)-1] = kvCopy
|
|
||||||
return context.WithValue(ctx, metadataCurrentKey{}, rawMetadata{md: md.md, added: added})
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendOutgoingContext returns a new context with the provided kv merged
|
|
||||||
// with any existing metadata in the context. Please refer to the documentation
|
|
||||||
// of Pairs for a description of kv.
|
|
||||||
func AppendOutgoingContext(ctx context.Context, kv ...string) context.Context {
|
|
||||||
if len(kv)%2 == 1 {
|
|
||||||
panic(fmt.Sprintf("metadata: AppendOutgoingContext got an odd number of input pairs for metadata: %d", len(kv)))
|
|
||||||
}
|
|
||||||
md, _ := ctx.Value(metadataOutgoingKey{}).(rawMetadata)
|
|
||||||
added := make([][]string, len(md.added)+1)
|
|
||||||
copy(added, md.added)
|
|
||||||
kvCopy := make([]string, 0, len(kv))
|
|
||||||
for i := 0; i < len(kv); i += 2 {
|
|
||||||
kvCopy = append(kvCopy, strings.ToLower(kv[i]), kv[i+1])
|
|
||||||
}
|
|
||||||
added[len(added)-1] = kvCopy
|
|
||||||
return context.WithValue(ctx, metadataOutgoingKey{}, rawMetadata{md: md.md, added: added})
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromContext returns the metadata in ctx if it exists.
|
|
||||||
func FromContext(ctx context.Context) (Metadata, bool) {
|
|
||||||
raw, ok := ctx.Value(metadataCurrentKey{}).(rawMetadata)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
metadataSize := len(raw.md)
|
|
||||||
for i := range raw.added {
|
|
||||||
metadataSize += len(raw.added[i]) / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
out := make(Metadata, metadataSize)
|
|
||||||
for k, v := range raw.md {
|
|
||||||
out[k] = copyOf(v)
|
|
||||||
}
|
|
||||||
for _, added := range raw.added {
|
|
||||||
if len(added)%2 == 1 {
|
|
||||||
panic(fmt.Sprintf("metadata: FromContext got an odd number of input pairs for metadata: %d", len(added)))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(added); i += 2 {
|
|
||||||
out[added[i]] = append(out[added[i]], added[i+1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustContext returns the metadata in ctx.
|
|
||||||
func MustContext(ctx context.Context) Metadata {
|
|
||||||
md, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing metadata")
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromIncomingContext returns the incoming metadata in ctx if it exists.
|
|
||||||
func FromIncomingContext(ctx context.Context) (Metadata, bool) {
|
|
||||||
raw, ok := ctx.Value(metadataIncomingKey{}).(rawMetadata)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
metadataSize := len(raw.md)
|
|
||||||
for i := range raw.added {
|
|
||||||
metadataSize += len(raw.added[i]) / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
out := make(Metadata, metadataSize)
|
|
||||||
for k, v := range raw.md {
|
|
||||||
out[k] = copyOf(v)
|
|
||||||
}
|
|
||||||
for _, added := range raw.added {
|
|
||||||
if len(added)%2 == 1 {
|
|
||||||
panic(fmt.Sprintf("metadata: FromIncomingContext got an odd number of input pairs for metadata: %d", len(added)))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(added); i += 2 {
|
|
||||||
out[added[i]] = append(out[added[i]], added[i+1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustIncomingContext returns the incoming metadata in ctx.
|
|
||||||
func MustIncomingContext(ctx context.Context) Metadata {
|
|
||||||
md, ok := FromIncomingContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing metadata")
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValueFromIncomingContext returns the metadata value corresponding to the metadata
|
|
||||||
// key from the incoming metadata if it exists. Keys are matched in a case insensitive
|
|
||||||
// manner.
|
|
||||||
func ValueFromIncomingContext(ctx context.Context, key string) []string {
|
|
||||||
raw, ok := ctx.Value(metadataIncomingKey{}).(rawMetadata)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := raw.md[key]; ok {
|
|
||||||
return copyOf(v)
|
|
||||||
}
|
|
||||||
for k, v := range raw.md {
|
|
||||||
// Case insensitive comparison: Metadata is a map, and there's no guarantee
|
|
||||||
// that the Metadata attached to the context is created using our helper
|
|
||||||
// functions.
|
|
||||||
if strings.EqualFold(k, key) {
|
|
||||||
return copyOf(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValueFromCurrentContext returns the metadata value corresponding to the metadata
|
|
||||||
// key from the incoming metadata if it exists. Keys are matched in a case insensitive
|
|
||||||
// manner.
|
|
||||||
func ValueFromCurrentContext(ctx context.Context, key string) []string {
|
|
||||||
md, ok := ctx.Value(metadataCurrentKey{}).(rawMetadata)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := md.md[key]; ok {
|
|
||||||
return copyOf(v)
|
|
||||||
}
|
|
||||||
for k, v := range md.md {
|
|
||||||
// Case insensitive comparison: Metadata is a map, and there's no guarantee
|
|
||||||
// that the Metadata attached to the context is created using our helper
|
|
||||||
// functions.
|
|
||||||
if strings.EqualFold(k, key) {
|
|
||||||
return copyOf(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustOutgoingContext returns the outgoing metadata in ctx.
|
|
||||||
func MustOutgoingContext(ctx context.Context) Metadata {
|
|
||||||
md, ok := FromOutgoingContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing metadata")
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValueFromOutgoingContext returns the metadata value corresponding to the metadata
|
|
||||||
// key from the incoming metadata if it exists. Keys are matched in a case insensitive
|
|
||||||
// manner.
|
|
||||||
func ValueFromOutgoingContext(ctx context.Context, key string) []string {
|
|
||||||
md, ok := ctx.Value(metadataOutgoingKey{}).(rawMetadata)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := md.md[key]; ok {
|
|
||||||
return copyOf(v)
|
|
||||||
}
|
|
||||||
for k, v := range md.md {
|
|
||||||
// Case insensitive comparison: Metadata is a map, and there's no guarantee
|
|
||||||
// that the Metadata attached to the context is created using our helper
|
|
||||||
// functions.
|
|
||||||
if strings.EqualFold(k, key) {
|
|
||||||
return copyOf(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyOf(v []string) []string {
|
|
||||||
vals := make([]string, len(v))
|
|
||||||
copy(vals, v)
|
|
||||||
return vals
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromOutgoingContext returns the outgoing metadata in ctx if it exists.
|
|
||||||
func FromOutgoingContext(ctx context.Context) (Metadata, bool) {
|
|
||||||
raw, ok := ctx.Value(metadataOutgoingKey{}).(rawMetadata)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
metadataSize := len(raw.md)
|
|
||||||
for i := range raw.added {
|
|
||||||
metadataSize += len(raw.added[i]) / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
out := make(Metadata, metadataSize)
|
|
||||||
for k, v := range raw.md {
|
|
||||||
out[k] = copyOf(v)
|
|
||||||
}
|
|
||||||
for _, added := range raw.added {
|
|
||||||
if len(added)%2 == 1 {
|
|
||||||
panic(fmt.Sprintf("metadata: FromOutgoingContext got an odd number of input pairs for metadata: %d", len(added)))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(added); i += 2 {
|
|
||||||
out[added[i]] = append(out[added[i]], added[i+1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
type rawMetadata struct {
|
|
||||||
md Metadata
|
|
||||||
added [][]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterator used to iterate over metadata with order
|
|
||||||
type Iterator struct {
|
|
||||||
md Metadata
|
|
||||||
keys []string
|
|
||||||
cur int
|
|
||||||
cnt int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next advance iterator to next element
|
|
||||||
func (iter *Iterator) Next(k *string, v *[]string) bool {
|
|
||||||
if iter.cur+1 > iter.cnt {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if k != nil && v != nil {
|
|
||||||
*k = iter.keys[iter.cur]
|
|
||||||
vv := iter.md[*k]
|
|
||||||
*v = make([]string, len(vv))
|
|
||||||
copy(*v, vv)
|
|
||||||
iter.cur++
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterator returns the itarator for metadata in sorted order
|
|
||||||
func (md Metadata) Iterator() *Iterator {
|
|
||||||
iter := &Iterator{md: md, cnt: len(md)}
|
|
||||||
iter.keys = make([]string, 0, iter.cnt)
|
|
||||||
for k := range md {
|
|
||||||
iter.keys = append(iter.keys, k)
|
|
||||||
}
|
|
||||||
sort.Strings(iter.keys)
|
|
||||||
return iter
|
|
||||||
}
|
|
||||||
|
@@ -69,7 +69,8 @@ type Service struct {
|
|||||||
type Node struct {
|
type Node struct {
|
||||||
Metadata metadata.Metadata `json:"metadata,omitempty"`
|
Metadata metadata.Metadata `json:"metadata,omitempty"`
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
Address string `json:"address,omitempty"`
|
// Address also prefixed with scheme like grpc://xx.xx.xx.xx:1234
|
||||||
|
Address string `json:"address,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option func signature
|
// Option func signature
|
||||||
|
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v4/util/id"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Tracer = (*noopTracer)(nil)
|
var _ Tracer = (*noopTracer)(nil)
|
||||||
@@ -18,6 +18,8 @@ func (t *noopTracer) Spans() []Span {
|
|||||||
return t.spans
|
return t.spans
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var uuidNil = uuid.Nil.String()
|
||||||
|
|
||||||
func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption) (context.Context, Span) {
|
func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption) (context.Context, Span) {
|
||||||
options := NewSpanOptions(opts...)
|
options := NewSpanOptions(opts...)
|
||||||
span := &noopSpan{
|
span := &noopSpan{
|
||||||
@@ -28,8 +30,8 @@ func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption)
|
|||||||
labels: options.Labels,
|
labels: options.Labels,
|
||||||
kind: options.Kind,
|
kind: options.Kind,
|
||||||
}
|
}
|
||||||
span.spanID.s, _ = id.New()
|
span.spanID.s = uuidNil
|
||||||
span.traceID.s, _ = id.New()
|
span.traceID.s = uuidNil
|
||||||
if span.ctx == nil {
|
if span.ctx == nil {
|
||||||
span.ctx = context.Background()
|
span.ctx = context.Background()
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user