Skip to content

Commit e4b1fd9

Browse files
authored
tfprotov5/tf5server+tfprotov6/tf6server: Add downstream RPC request duration and response diagnostics logging (#203)
Reference: #183
1 parent f21c41e commit e4b1fd9

File tree

20 files changed

+1967
-49
lines changed

20 files changed

+1967
-49
lines changed

.changelog/203.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
```release-note:enhancement
2+
tfprotov5/tf5server: Added downstream RPC request duration and response diagnostics logging
3+
```
4+
5+
```release-note:enhancement
6+
tfprotov6/tf6server: Added downstream RPC request duration and response diagnostics logging
7+
```

internal/logging/context.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,19 @@ func InitContext(ctx context.Context, sdkOpts tfsdklog.Options, providerOpts tfl
2222
ctx = tfsdklog.NewRootSDKLogger(ctx, append(tfsdklog.Options{
2323
tfsdklog.WithLevelFromEnv(EnvTfLogSdk),
2424
}, sdkOpts...)...)
25+
ctx = ProtoSubsystemContext(ctx, sdkOpts)
26+
ctx = tfsdklog.NewRootProviderLogger(ctx, providerOpts...)
27+
28+
return ctx
29+
}
30+
31+
// ProtoSubsystemContext adds the proto subsystem to the SDK logger context.
32+
func ProtoSubsystemContext(ctx context.Context, sdkOpts tfsdklog.Options) context.Context {
2533
ctx = tfsdklog.NewSubsystem(ctx, SubsystemProto, append(tfsdklog.Options{
2634
// All calls are through the Protocol* helper functions
2735
tfsdklog.WithAdditionalLocationOffset(1),
2836
tfsdklog.WithLevelFromEnv(EnvTfLogSdkProto),
2937
}, sdkOpts...)...)
30-
ctx = tfsdklog.NewRootProviderLogger(ctx, providerOpts...)
3138

3239
return ctx
3340
}

internal/logging/keys.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,30 @@ package logging
55
// Practitioners or tooling reading logs may be depending on these keys, so be
66
// conscious of that when changing them.
77
const (
8+
// Attribute of the diagnostic being logged.
9+
KeyDiagnosticAttribute = "diagnostic_attribute"
10+
11+
// Number of the error diagnostics.
12+
KeyDiagnosticErrorCount = "diagnostic_error_count"
13+
14+
// Severity of the diagnostic being logged.
15+
KeyDiagnosticSeverity = "diagnostic_severity"
16+
17+
// Detail of the diagnostic being logged.
18+
KeyDiagnosticDetail = "diagnostic_detail"
19+
20+
// Summary of the diagnostic being logged.
21+
KeyDiagnosticSummary = "diagnostic_summary"
22+
23+
// Number of the warning diagnostics.
24+
KeyDiagnosticWarningCount = "diagnostic_warning_count"
25+
826
// Underlying error string
927
KeyError = "error"
1028

29+
// Duration in milliseconds for the RPC request
30+
KeyRequestDurationMs = "tf_req_duration_ms"
31+
1132
// A unique ID for the RPC request
1233
KeyRequestID = "tf_req_id"
1334

internal/logging/protocol.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ func ProtocolError(ctx context.Context, msg string, additionalFields ...map[stri
1616
tfsdklog.SubsystemError(ctx, SubsystemProto, msg, additionalFields...)
1717
}
1818

19+
// ProtocolWarn emits a protocol subsystem log at WARN level.
20+
func ProtocolWarn(ctx context.Context, msg string, additionalFields ...map[string]interface{}) {
21+
tfsdklog.SubsystemWarn(ctx, SubsystemProto, msg, additionalFields...)
22+
}
23+
1924
// ProtocolTrace emits a protocol subsystem log at TRACE level.
2025
func ProtocolTrace(ctx context.Context, msg string, additionalFields ...map[string]interface{}) {
2126
tfsdklog.SubsystemTrace(ctx, SubsystemProto, msg, additionalFields...)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package diag
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-go/internal/logging"
7+
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
8+
)
9+
10+
// Diagnostics is a collection of Diagnostic.
11+
type Diagnostics []*tfprotov5.Diagnostic
12+
13+
// ErrorCount returns the number of error severity diagnostics.
14+
func (d Diagnostics) ErrorCount() int {
15+
var result int
16+
17+
for _, diagnostic := range d {
18+
if diagnostic == nil {
19+
continue
20+
}
21+
22+
if diagnostic.Severity != tfprotov5.DiagnosticSeverityError {
23+
continue
24+
}
25+
26+
result++
27+
}
28+
29+
return result
30+
}
31+
32+
// Log will log every diagnostic:
33+
//
34+
// - Error severity at ERROR level
35+
// - Warning severity at WARN level
36+
// - Invalid/Unknown severity at WARN level
37+
//
38+
func (d Diagnostics) Log(ctx context.Context) {
39+
for _, diagnostic := range d {
40+
if diagnostic == nil {
41+
continue
42+
}
43+
44+
diagnosticFields := map[string]interface{}{
45+
logging.KeyDiagnosticDetail: diagnostic.Detail,
46+
logging.KeyDiagnosticSeverity: diagnostic.Severity.String(),
47+
logging.KeyDiagnosticSummary: diagnostic.Summary,
48+
}
49+
50+
if diagnostic.Attribute != nil {
51+
diagnosticFields[logging.KeyDiagnosticAttribute] = diagnostic.Attribute.String()
52+
}
53+
54+
switch diagnostic.Severity {
55+
case tfprotov5.DiagnosticSeverityError:
56+
logging.ProtocolError(ctx, "Response contains error diagnostic", diagnosticFields)
57+
case tfprotov5.DiagnosticSeverityWarning:
58+
logging.ProtocolWarn(ctx, "Response contains warning diagnostic", diagnosticFields)
59+
default:
60+
logging.ProtocolWarn(ctx, "Response contains unknown diagnostic", diagnosticFields)
61+
}
62+
}
63+
}
64+
65+
// WarningCount returns the number of warning severity diagnostics.
66+
func (d Diagnostics) WarningCount() int {
67+
var result int
68+
69+
for _, diagnostic := range d {
70+
if diagnostic == nil {
71+
continue
72+
}
73+
74+
if diagnostic.Severity != tfprotov5.DiagnosticSeverityWarning {
75+
continue
76+
}
77+
78+
result++
79+
}
80+
81+
return result
82+
}

0 commit comments

Comments
 (0)