Skip to content

Commit 29582d1

Browse files
committed
add helper methods
1 parent 18a8795 commit 29582d1

2 files changed

Lines changed: 140 additions & 9 deletions

File tree

lx/field.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package lx
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
// Pair represents a key-value pair where the key is a string and the value is of any type.
9+
type Pair struct {
10+
Key string
11+
Value interface{}
12+
}
13+
14+
// Fields represents a slice of key-value pairs.
15+
type Fields []Pair
16+
17+
// Map converts the Fields slice to a map[string]interface{}.
18+
// This is useful for backward compatibility or when map operations are needed.
19+
// Example:
20+
//
21+
// fields := lx.Fields{{"user", "alice"}, {"age", 30}}
22+
// m := fields.Map() // Returns map[string]interface{}{"user": "alice", "age": 30}
23+
func (f Fields) Map() map[string]interface{} {
24+
m := make(map[string]interface{}, len(f))
25+
for _, pair := range f {
26+
m[pair.Key] = pair.Value
27+
}
28+
return m
29+
}
30+
31+
// Get returns the value for a given key and a boolean indicating if the key was found.
32+
// This provides O(n) lookup, which is fine for small numbers of fields.
33+
// Example:
34+
//
35+
// fields := lx.Fields{{"user", "alice"}, {"age", 30}}
36+
// value, found := fields.Get("user") // Returns "alice", true
37+
func (f Fields) Get(key string) (interface{}, bool) {
38+
for _, pair := range f {
39+
if pair.Key == key {
40+
return pair.Value, true
41+
}
42+
}
43+
return nil, false
44+
}
45+
46+
// Filter returns a new Fields slice containing only pairs where the predicate returns true.
47+
// Example:
48+
//
49+
// fields := lx.Fields{{"user", "alice"}, {"password", "secret"}, {"age", 30}}
50+
// filtered := fields.Filter(func(key string, value interface{}) bool {
51+
// return key != "password" // Remove sensitive fields
52+
// })
53+
func (f Fields) Filter(predicate func(key string, value interface{}) bool) Fields {
54+
result := make(Fields, 0, len(f))
55+
for _, pair := range f {
56+
if predicate(pair.Key, pair.Value) {
57+
result = append(result, pair)
58+
}
59+
}
60+
return result
61+
}
62+
63+
// Translate returns a new Fields slice with keys translated according to the provided mapping.
64+
// Keys not in the mapping are passed through unchanged. This is useful for adapters like Victoria.
65+
// Example:
66+
//
67+
// fields := lx.Fields{{"user", "alice"}, {"timestamp", time.Now()}}
68+
// translated := fields.Translate(map[string]string{
69+
// "user": "username",
70+
// "timestamp": "ts",
71+
// })
72+
// // Returns: {{"username", "alice"}, {"ts", time.Now()}}
73+
func (f Fields) Translate(mapping map[string]string) Fields {
74+
result := make(Fields, len(f))
75+
for i, pair := range f {
76+
if newKey, ok := mapping[pair.Key]; ok {
77+
result[i] = Pair{Key: newKey, Value: pair.Value}
78+
} else {
79+
result[i] = pair
80+
}
81+
}
82+
return result
83+
}
84+
85+
// Merge merges another Fields slice into this one, with the other slice's fields taking precedence
86+
// for duplicate keys (overwrites existing keys).
87+
// Example:
88+
//
89+
// base := lx.Fields{{"user", "alice"}, {"age", 30}}
90+
// additional := lx.Fields{{"age", 31}, {"city", "NYC"}}
91+
// merged := base.Merge(additional)
92+
// // Returns: {{"user", "alice"}, {"age", 31}, {"city", "NYC"}}
93+
func (f Fields) Merge(other Fields) Fields {
94+
result := make(Fields, 0, len(f)+len(other))
95+
96+
// Create a map to track which keys from 'other' we've seen
97+
seen := make(map[string]bool, len(other))
98+
99+
// First add all fields from 'f'
100+
result = append(result, f...)
101+
102+
// Then add fields from 'other', overwriting duplicates
103+
for _, pair := range other {
104+
// Check if this key already exists in result
105+
found := false
106+
for i, existing := range result {
107+
if existing.Key == pair.Key {
108+
result[i] = pair // Overwrite
109+
found = true
110+
break
111+
}
112+
}
113+
if !found {
114+
result = append(result, pair)
115+
}
116+
seen[pair.Key] = true
117+
}
118+
119+
return result
120+
}
121+
122+
// String returns a human-readable string representation of the fields.
123+
// Example:
124+
//
125+
// fields := lx.Fields{{"user", "alice"}, {"age", 30}}
126+
// str := fields.String() // Returns: "[user=alice age=30]"
127+
func (f Fields) String() string {
128+
var builder strings.Builder
129+
builder.WriteString(LeftBracket)
130+
for i, pair := range f {
131+
if i > 0 {
132+
builder.WriteString(Space)
133+
}
134+
builder.WriteString(pair.Key)
135+
builder.WriteString("=")
136+
builder.WriteString(fmt.Sprint(pair.Value))
137+
}
138+
builder.WriteString(RightBracket)
139+
return builder.String()
140+
}

lx/lx.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,15 +135,6 @@ func LevelParse(s string) LevelType {
135135
// ([parent]→[child]) styles, affecting how handlers render namespace hierarchies.
136136
type StyleType int
137137

138-
// Pair represents a key-value pair where the key is a string and the value is of any type.
139-
type Pair struct {
140-
Key string
141-
Value interface{}
142-
}
143-
144-
// Fields represents a slice of key-value pairs.
145-
type Fields []Pair
146-
147138
// Entry represents a single log entry passed to handlers.
148139
// It encapsulates all information about a log message, including its timestamp, severity,
149140
// content, namespace, metadata, and formatting style. Handlers process Entry instances

0 commit comments

Comments
 (0)