Skip to content

Commit 1d9ce94

Browse files
authored
tflog+tfsdklog: Added WithRootFields() function (#60)
Reference: #59
1 parent 0a383c3 commit 1d9ce94

File tree

8 files changed

+205
-2
lines changed

8 files changed

+205
-2
lines changed

.changelog/60.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
```release-note:enhancement
2+
tflog: Added `WithRootFields()` function, which can copy root logger fields to a new subsystem logger during `NewSubsystem()`
3+
```
4+
5+
```release-note:enhancement
6+
tfsdklog: Added `WithRootFields()` function, which can copy root logger fields to a new subsystem logger during `NewSubsystem()`
7+
```

internal/logging/options.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ type LoggerOpts struct {
3939
// tfsdklog; providers and SDKs should always include the time logs
4040
// were written as part of the log.
4141
IncludeTime bool
42+
43+
// IncludeRootFields indicates whether a new subsystem logger should
44+
// copy existing fields from the root logger. This is only performed
45+
// at the time of new subsystem creation.
46+
IncludeRootFields bool
4247
}
4348

4449
// ApplyLoggerOpts generates a LoggerOpts out of a list of Option
@@ -81,6 +86,15 @@ func WithOutput(output io.Writer) Option {
8186
}
8287
}
8388

89+
// WithRootFields enables the copying of root logger fields to a new subsystem
90+
// logger during creation.
91+
func WithRootFields() Option {
92+
return func(l LoggerOpts) LoggerOpts {
93+
l.IncludeRootFields = true
94+
return l
95+
}
96+
}
97+
8498
// WithoutLocation disables the location included with logging statements. It
8599
// should only ever be used to make log output deterministic when testing
86100
// terraform-plugin-log.

tflog/options.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ func WithLevel(level hclog.Level) logging.Option {
4343
}
4444
}
4545

46+
// WithRootFields enables the copying of root logger fields to a new subsystem
47+
// logger during creation.
48+
func WithRootFields() logging.Option {
49+
return logging.WithRootFields()
50+
}
51+
4652
// WithoutLocation returns an option that disables including the location of
4753
// the log line in the log output, which is on by default. This has no effect
4854
// when used with NewSubsystem.

tflog/options_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,82 @@ func TestWithAdditionalLocationOffset(t *testing.T) {
120120
})
121121
}
122122
}
123+
124+
func TestWithRootFields(t *testing.T) {
125+
t.Parallel()
126+
127+
testCases := map[string]struct {
128+
logMessage string
129+
rootFields map[string]interface{}
130+
subsystemFields map[string]interface{}
131+
expectedOutput []map[string]interface{}
132+
}{
133+
"no-root-log-fields": {
134+
subsystemFields: map[string]interface{}{
135+
"test-subsystem-key": "test-subsystem-value",
136+
},
137+
logMessage: "test message",
138+
expectedOutput: []map[string]interface{}{
139+
{
140+
"@level": hclog.Trace.String(),
141+
"@message": "test message",
142+
"@module": testSubsystemModule,
143+
"test-subsystem-key": "test-subsystem-value",
144+
},
145+
},
146+
},
147+
"with-root-log-fields": {
148+
subsystemFields: map[string]interface{}{
149+
"test-subsystem-key": "test-subsystem-value",
150+
},
151+
logMessage: "test message",
152+
rootFields: map[string]interface{}{
153+
"test-root-key": "test-root-value",
154+
},
155+
expectedOutput: []map[string]interface{}{
156+
{
157+
"@level": hclog.Trace.String(),
158+
"@message": "test message",
159+
"@module": testSubsystemModule,
160+
"test-root-key": "test-root-value",
161+
"test-subsystem-key": "test-subsystem-value",
162+
},
163+
},
164+
},
165+
}
166+
167+
for name, testCase := range testCases {
168+
name, testCase := name, testCase
169+
170+
t.Run(name, func(t *testing.T) {
171+
t.Parallel()
172+
173+
var outputBuffer bytes.Buffer
174+
175+
ctx := context.Background()
176+
ctx = loggertest.ProviderRoot(ctx, &outputBuffer)
177+
178+
for key, value := range testCase.rootFields {
179+
ctx = tflog.With(ctx, key, value)
180+
}
181+
182+
ctx = tflog.NewSubsystem(ctx, testSubsystem, tflog.WithRootFields())
183+
184+
for key, value := range testCase.subsystemFields {
185+
ctx = tflog.SubsystemWith(ctx, testSubsystem, key, value)
186+
}
187+
188+
tflog.SubsystemTrace(ctx, testSubsystem, testCase.logMessage)
189+
190+
got, err := loggertest.MultilineJSONDecode(&outputBuffer)
191+
192+
if err != nil {
193+
t.Fatalf("unable to read multiple line JSON: %s", err)
194+
}
195+
196+
if diff := cmp.Diff(testCase.expectedOutput, got); diff != "" {
197+
t.Errorf("unexpected output difference: %s", diff)
198+
}
199+
})
200+
}
201+
}

tflog/subsystem.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,13 @@ func NewSubsystem(ctx context.Context, subsystem string, options ...logging.Opti
6363
subLoggerOptions.Level = opts.Level
6464
}
6565

66-
return logging.SetProviderSubsystemLogger(ctx, subsystem, hclog.New(subLoggerOptions))
66+
subLogger := hclog.New(subLoggerOptions)
67+
68+
if opts.IncludeRootFields {
69+
subLogger = subLogger.With(logger.ImpliedArgs()...)
70+
}
71+
72+
return logging.SetProviderSubsystemLogger(ctx, subsystem, subLogger)
6773
}
6874

6975
// SubsystemWith returns a new context.Context that has a modified logger for

tfsdklog/options.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ func WithLevel(level hclog.Level) logging.Option {
5353
}
5454
}
5555

56+
// WithRootFields enables the copying of root logger fields to a new subsystem
57+
// logger during creation.
58+
func WithRootFields() logging.Option {
59+
return logging.WithRootFields()
60+
}
61+
5662
// WithoutLocation returns an option that disables including the location of
5763
// the log line in the log output, which is on by default. This has no effect
5864
// when used with NewSubsystem.

tfsdklog/options_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,82 @@ func TestWithAdditionalLocationOffset(t *testing.T) {
120120
})
121121
}
122122
}
123+
124+
func TestWithRootFields(t *testing.T) {
125+
t.Parallel()
126+
127+
testCases := map[string]struct {
128+
logMessage string
129+
rootFields map[string]interface{}
130+
subsystemFields map[string]interface{}
131+
expectedOutput []map[string]interface{}
132+
}{
133+
"no-root-log-fields": {
134+
subsystemFields: map[string]interface{}{
135+
"test-subsystem-key": "test-subsystem-value",
136+
},
137+
logMessage: "test message",
138+
expectedOutput: []map[string]interface{}{
139+
{
140+
"@level": hclog.Trace.String(),
141+
"@message": "test message",
142+
"@module": testSubsystemModule,
143+
"test-subsystem-key": "test-subsystem-value",
144+
},
145+
},
146+
},
147+
"with-root-log-fields": {
148+
subsystemFields: map[string]interface{}{
149+
"test-subsystem-key": "test-subsystem-value",
150+
},
151+
logMessage: "test message",
152+
rootFields: map[string]interface{}{
153+
"test-root-key": "test-root-value",
154+
},
155+
expectedOutput: []map[string]interface{}{
156+
{
157+
"@level": hclog.Trace.String(),
158+
"@message": "test message",
159+
"@module": testSubsystemModule,
160+
"test-root-key": "test-root-value",
161+
"test-subsystem-key": "test-subsystem-value",
162+
},
163+
},
164+
},
165+
}
166+
167+
for name, testCase := range testCases {
168+
name, testCase := name, testCase
169+
170+
t.Run(name, func(t *testing.T) {
171+
t.Parallel()
172+
173+
var outputBuffer bytes.Buffer
174+
175+
ctx := context.Background()
176+
ctx = loggertest.SDKRoot(ctx, &outputBuffer)
177+
178+
for key, value := range testCase.rootFields {
179+
ctx = tfsdklog.With(ctx, key, value)
180+
}
181+
182+
ctx = tfsdklog.NewSubsystem(ctx, testSubsystem, tfsdklog.WithRootFields())
183+
184+
for key, value := range testCase.subsystemFields {
185+
ctx = tfsdklog.SubsystemWith(ctx, testSubsystem, key, value)
186+
}
187+
188+
tfsdklog.SubsystemTrace(ctx, testSubsystem, testCase.logMessage)
189+
190+
got, err := loggertest.MultilineJSONDecode(&outputBuffer)
191+
192+
if err != nil {
193+
t.Fatalf("unable to read multiple line JSON: %s", err)
194+
}
195+
196+
if diff := cmp.Diff(testCase.expectedOutput, got); diff != "" {
197+
t.Errorf("unexpected output difference: %s", diff)
198+
}
199+
})
200+
}
201+
}

tfsdklog/subsystem.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,13 @@ func NewSubsystem(ctx context.Context, subsystem string, options ...logging.Opti
6363
subLoggerOptions.Level = opts.Level
6464
}
6565

66-
return logging.SetSDKSubsystemLogger(ctx, subsystem, hclog.New(subLoggerOptions))
66+
subLogger := hclog.New(subLoggerOptions)
67+
68+
if opts.IncludeRootFields {
69+
subLogger = subLogger.With(logger.ImpliedArgs()...)
70+
}
71+
72+
return logging.SetSDKSubsystemLogger(ctx, subsystem, subLogger)
6773
}
6874

6975
// SubsystemWith returns a new context.Context that has a modified logger for

0 commit comments

Comments
 (0)