Skip to content

Commit f659fdf

Browse files
committed
Allow forcing reflection in slog.Value
1 parent d8c5704 commit f659fdf

File tree

3 files changed

+28
-23
lines changed

3 files changed

+28
-23
lines changed

internal/humanfmt/humanfmt.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ func pinnedFields(ent slog.Entry) string {
6161
slog.F("span", ent.SpanContext.SpanID),
6262
)
6363

64-
return humanFields(slogval.Reflect(m))
64+
return humanFields(slogval.Encode(m).(slogval.Map))
6565
}
6666

6767
func stringFields(ent slog.Entry) string {
6868
pinned := pinnedFields(ent)
69-
fields := humanFields(slogval.Reflect(ent.Fields))
69+
fields := humanFields(slogval.Encode(ent.Fields).(slogval.Map))
7070

7171
if pinned == "" {
7272
return fields

internal/humanfmt/marshal_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ dsamkld`),
117117
t.Run(tc.name, func(t *testing.T) {
118118
t.Parallel()
119119

120-
v := slogval.Reflect(tc.in)
120+
v := slogval.Encode(tc.in).(slogval.Map)
121121
actOut := humanFields(v)
122122
t.Logf("yaml:\n%v", actOut)
123123
if diff := diff.Diff(tc.out, actOut); diff != "" {

slogval/reflect.go

+25-20
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,24 @@ import (
1212
"go.coder.com/slog"
1313
)
1414

15-
// Reflector is implemented by a value passed to Field
16-
// that would like to only use the reflection based
17-
// converter for itself and ignore interfaces like error
18-
// or fmt.Stringer.
19-
type Reflector interface {
20-
LogWithReflect()
15+
// Encode uses interface detection and reflection to Encode v into a
16+
// Value that uses only the primitive value types defined in this package.
17+
// Use Reflect if you'd like to force the value to be encoded with reflection and
18+
// ignore other interfaces.
19+
func Encode(v interface{}) Value {
20+
return encode(reflect.ValueOf(v))
2121
}
2222

23-
// Reflect uses reflection to convert the slice of fields into a ordered
24-
// map that uses only the primitive value types defined in this package.
25-
// It uses interfaces like error and fmt.Stringer where appropriate.
26-
// Have a type implement Reflector if you would like to force
27-
func Reflect(fs []slog.Field) Map {
28-
var m Map
29-
for _, f := range fs {
30-
m = m.appendVal(f.LogKey(), reflectValue(reflect.ValueOf(f.LogValue())))
31-
}
32-
return m
23+
// Reflect uses reflection to convert v to a Value.
24+
// Nested values inside v will be converted using the
25+
// Encode function which considers interfaces.
26+
// You should use this inside a slog.Value to force
27+
// reflection over fmt.Stringer and error.
28+
func Reflect(v interface{}) Value {
29+
return reflectValue(reflect.ValueOf(v))
3330
}
3431

35-
func reflectValue(rv reflect.Value) Value {
32+
func encode(rv reflect.Value) Value {
3633
if !rv.IsValid() {
3734
// reflect.ValueOf(nil).IsValid == false
3835
return nil
@@ -49,9 +46,6 @@ func reflectValue(rv reflect.Value) Value {
4946

5047
typ := rv.Type()
5148
switch {
52-
case implements(typ, (*Reflector)(nil)):
53-
// Skip checking for any other interfaces as the value wants
54-
//
5549
case implements(typ, (*Value)(nil)):
5650
v := rv.MethodByName("isSlogCoreValue").Call(nil)
5751
return v[0].Interface().(Value)
@@ -76,13 +70,24 @@ func reflectValue(rv reflect.Value) Value {
7670
return String(s[0].String())
7771
}
7872

73+
// Fallback to pure reflection.
74+
return reflectValue(rv)
75+
}
76+
77+
func reflectValue(rv reflect.Value) Value {
78+
if !rv.IsValid() {
79+
// reflect.ValueOf(nil).IsValid == false
80+
return nil
81+
}
82+
7983
switch rv.Kind() {
8084
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
8185
if rv.IsNil() {
8286
return nil
8387
}
8488
}
8589

90+
typ := rv.Type()
8691
switch rv.Kind() {
8792
case reflect.String:
8893
return String(rv.String())

0 commit comments

Comments
 (0)