github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/views/json_view.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package views 5 6 import ( 7 encJson "encoding/json" 8 "fmt" 9 10 "github.com/hashicorp/go-hclog" 11 12 "github.com/terramate-io/tf/command/views/json" 13 "github.com/terramate-io/tf/tfdiags" 14 tfversion "github.com/terramate-io/tf/version" 15 ) 16 17 // This version describes the schema of JSON UI messages. This version must be 18 // updated after making any changes to this view, the jsonHook, or any of the 19 // command/views/json package. 20 const JSON_UI_VERSION = "1.2" 21 22 func NewJSONView(view *View) *JSONView { 23 log := hclog.New(&hclog.LoggerOptions{ 24 Name: "terraform.ui", 25 Output: view.streams.Stdout.File, 26 JSONFormat: true, 27 }) 28 jv := &JSONView{ 29 log: log, 30 view: view, 31 } 32 jv.Version() 33 return jv 34 } 35 36 type JSONView struct { 37 // hclog is used for all output in JSON UI mode. The logger has an internal 38 // mutex to ensure that messages are not interleaved. 39 log hclog.Logger 40 41 // We hold a reference to the view entirely to allow us to access the 42 // ConfigSources function pointer, in order to render source snippets into 43 // diagnostics. This is even more unfortunate than the same reference in the 44 // view. 45 // 46 // Do not be tempted to dereference the configSource value upon logger init, 47 // as it will likely be updated later. 48 view *View 49 } 50 51 func (v *JSONView) Version() { 52 version := tfversion.String() 53 v.log.Info( 54 fmt.Sprintf("Terraform %s", version), 55 "type", json.MessageVersion, 56 "terraform", version, 57 "ui", JSON_UI_VERSION, 58 ) 59 } 60 61 func (v *JSONView) Log(message string) { 62 v.log.Info(message, "type", json.MessageLog) 63 } 64 65 func (v *JSONView) StateDump(state string) { 66 v.log.Info( 67 "Emergency state dump", 68 "type", json.MessageLog, 69 "state", encJson.RawMessage(state), 70 ) 71 } 72 73 func (v *JSONView) Diagnostics(diags tfdiags.Diagnostics, metadata ...interface{}) { 74 sources := v.view.configSources() 75 for _, diag := range diags { 76 diagnostic := json.NewDiagnostic(diag, sources) 77 78 args := []interface{}{"type", json.MessageDiagnostic, "diagnostic", diagnostic} 79 args = append(args, metadata...) 80 81 switch diag.Severity() { 82 case tfdiags.Warning: 83 v.log.Warn(fmt.Sprintf("Warning: %s", diag.Description().Summary), args...) 84 default: 85 v.log.Error(fmt.Sprintf("Error: %s", diag.Description().Summary), args...) 86 } 87 } 88 } 89 90 func (v *JSONView) PlannedChange(c *json.ResourceInstanceChange) { 91 v.log.Info( 92 c.String(), 93 "type", json.MessagePlannedChange, 94 "change", c, 95 ) 96 } 97 98 func (v *JSONView) ResourceDrift(c *json.ResourceInstanceChange) { 99 v.log.Info( 100 fmt.Sprintf("%s: Drift detected (%s)", c.Resource.Addr, c.Action), 101 "type", json.MessageResourceDrift, 102 "change", c, 103 ) 104 } 105 106 func (v *JSONView) ChangeSummary(cs *json.ChangeSummary) { 107 v.log.Info( 108 cs.String(), 109 "type", json.MessageChangeSummary, 110 "changes", cs, 111 ) 112 } 113 114 func (v *JSONView) Hook(h json.Hook) { 115 v.log.Info( 116 h.String(), 117 "type", h.HookType(), 118 "hook", h, 119 ) 120 } 121 122 func (v *JSONView) Outputs(outputs json.Outputs) { 123 v.log.Info( 124 outputs.String(), 125 "type", json.MessageOutputs, 126 "outputs", outputs, 127 ) 128 }