Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ Table below shows the names of the configuration options:
|----------------------|------------------------|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| OutputFormat | output-format | text | The output format that will be used for the formatted values (text or json). |
| PrintConfigOnInit | print-config-on-init | false | If true, the configuration will be printed when any of available constructors is used. |
| UseJSONTagName | use-json-tag-name | false | If true, the JSON tag name will be used instead of the Go struct field name. |
| UseJSONTagName | use-json-tag-name | false | If true, struct fields encoded as JSON reuse their `json` tag name. Fields tagged with `json:"-"` stay hidden, and tags without a name fall back to the Go identifier. |
| MaskValue | mask-value | [CENSORED] | The value that will be used to mask the sensitive information. |
| DisplayStructName | display-struct-name | false | If true, the struct name will be displayed in the output. |
| DisplayMapType | display-map-type | false | If true, the map type will be displayed in the output. |
Expand All @@ -189,6 +189,8 @@ Table below shows the names of the configuration options:
| ExcludePatterns | exclude-patterns | [] | A list of regular expressions that will be compared against all the string values. <br/>If a value matches any of the patterns, that section will be masked. Up to 50 patterns are allowed. |


Enabling `UseJSONTagName` mirrors Go's `encoding/json`: only the portion before the first comma is used for the field name, `json:"-"` keeps the field hidden, and tags without an explicit name fall back to the original identifier.

### Using the `censor.Config` struct

It's possible to define a configuration using `censor.Config` struct:
Expand Down
24 changes: 23 additions & 1 deletion internal/encoder/json_encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"reflect"
"strconv"
"strings"
"unicode/utf8"

"github.com/shopspring/decimal"
Expand All @@ -20,6 +21,7 @@ func NewJSONEncoder(c Config) *JSONEncoder {
CensorFieldTag: defaultCensorFieldTag,
ExcludePatterns: c.ExcludePatterns,
MaskValue: c.MaskValue,
UseJSONTagName: c.UseJSONTagName,
structFieldsCache: cache.NewTypeCache[[]Field](cache.DefaultMaxCacheSize),
escapedStringsCache: cache.New[string](cache.DefaultMaxCacheSize),
regexpCache: cache.New[string](cache.DefaultMaxCacheSize),
Expand Down Expand Up @@ -133,6 +135,7 @@ func (e *JSONEncoder) Struct(b *bytes.Buffer, v reflect.Value) {
b.WriteByte('}')
}

//nolint:gocognit
func (e *JSONEncoder) getStructFields(v reflect.Value) []Field {
numFields := v.NumField()
fields := make([]Field, numFields)
Expand All @@ -143,8 +146,27 @@ func (e *JSONEncoder) getStructFields(v reflect.Value) []Field {
continue
}

name := field.Name

if e.UseJSONTagName {
tagValue := field.Tag.Get("json")
if tagValue != "" {
if commaIdx := strings.Index(tagValue, ","); commaIdx != -1 {
tagValue = tagValue[:commaIdx]
}

if tagValue == "-" {
continue
}

if tagValue != "" {
name = tagValue
}
}
}

fields[i] = Field{
Name: field.Name,
Name: name,
IsMasked: field.Tag.Get(e.CensorFieldTag) != display,
}
}
Expand Down
36 changes: 36 additions & 0 deletions internal/encoder/json_encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -724,3 +724,39 @@ func TestJSONEncoder_StringEscaped(t *testing.T) {
require.Equal(t, `"say \"email: [CENSORED]\""`, b.String())
})
}

func TestJSONEncoder_Struct_UseJSONTagName(t *testing.T) {
type payload struct {
Tagged string `json:"alias,omitempty" censor:"display"`
Skipped string `json:"-" censor:"display"`
Empty string `json:",omitempty" censor:"display"`
Default string `censor:"display"`
}

value := payload{
Tagged: "tagged",
Skipped: "skipped",
Empty: "empty",
Default: "default",
}

t.Run("disabled uses Go field names", func(t *testing.T) {
e := NewJSONEncoder(Config{})
var b bytes.Buffer
defer b.Reset()

e.Struct(&b, reflect.ValueOf(value))

require.Equal(t, `{"Tagged": "tagged","Skipped": "skipped","Empty": "empty","Default": "default"}`, b.String())
})

t.Run("enabled uses json tags and skips dash tag", func(t *testing.T) {
e := NewJSONEncoder(Config{UseJSONTagName: true})
var b bytes.Buffer
defer b.Reset()

e.Struct(&b, reflect.ValueOf(value))

require.Equal(t, `{"alias": "tagged","Empty": "empty","Default": "default"}`, b.String())
})
}
Loading