github.com/mongodb/grip@v0.0.0-20240213223901-f906268d82b9/message/fields.go (about) 1 package message 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 8 "github.com/mongodb/grip/level" 9 ) 10 11 // FieldsMsgName is the name of the default "message" field in the 12 // fields structure. 13 const FieldsMsgName = "message" 14 15 type fieldMessage struct { 16 message string 17 fields Fields 18 cachedOutput string 19 includeMetadata bool 20 includeExtendedMetadata bool 21 Base 22 } 23 24 // Fields is a convince type that wraps map[string]interface{} and is 25 // used for attaching structured metadata to a build request. For 26 // example: 27 // 28 // message.Fields{"key0", <value>, "key1", <value>} 29 type Fields map[string]interface{} 30 31 // NewFieldsMessage creates a fully configured Composer instance that will 32 // attach basic metadata. This constructor allows you to include a string 33 // message as well as Fields object. 34 func NewFieldsMessage(p level.Priority, message string, f Fields) Composer { 35 m := MakeFieldsMessage(message, f) 36 37 _ = m.SetPriority(p) 38 39 return m 40 } 41 42 // NewFields constructs a full configured fields Composer with basic metadata. 43 func NewFields(p level.Priority, f Fields) Composer { 44 m := MakeFields(f) 45 _ = m.SetPriority(p) 46 47 return m 48 } 49 50 // MakeFieldsMessage constructs a fields Composer from a message string and 51 // Fields object, without specifying the priority of the message. This includes 52 // basic metadata. 53 func MakeFieldsMessage(message string, f Fields) Composer { 54 m := &fieldMessage{ 55 message: message, 56 fields: f, 57 includeMetadata: true, 58 } 59 m.setup() 60 return m 61 } 62 63 // NewExtendedFieldsMessage is the same as NewFieldsMessage, but also collects 64 // extended logging metadata. 65 func NewExtendedFieldsMessage(p level.Priority, message string, f Fields) Composer { 66 m := MakeExtendedFieldsMessage(message, f) 67 _ = m.SetPriority(p) 68 return m 69 } 70 71 // MakeExtendedFields is the same as MakeFields but also collects extended 72 // logging metadata. 73 func MakeExtendedFields(f Fields) Composer { 74 m := &fieldMessage{ 75 fields: f, 76 includeMetadata: true, 77 includeExtendedMetadata: true, 78 } 79 m.setup() 80 return m 81 } 82 83 // NewExtendedFields is the same as NewFields but also collects extended logging 84 // metadata. 85 func NewExtendedFields(p level.Priority, f Fields) Composer { 86 m := MakeExtendedFields(f) 87 _ = m.SetPriority(p) 88 return m 89 } 90 91 // MakeExtendedFieldsMessage is the same as MakeFieldsMessage but also collects 92 // extended logging metadata. 93 func MakeExtendedFieldsMessage(msg string, f Fields) Composer { 94 m := &fieldMessage{ 95 message: msg, 96 fields: f, 97 includeMetadata: true, 98 includeExtendedMetadata: true, 99 } 100 101 m.setup() 102 return m 103 } 104 105 // MakeSimpleFields returns a structured Composer that does 106 // not attach any logging metadata. 107 func MakeSimpleFields(f Fields) Composer { 108 m := &fieldMessage{fields: f} 109 m.setup() 110 return m 111 } 112 113 // NewSimpleFields returns a structured Composer that does not 114 // attach any logging metadata and allows callers to configure the 115 // messages' log level. 116 func NewSimpleFields(p level.Priority, f Fields) Composer { 117 m := MakeSimpleFields(f) 118 _ = m.SetPriority(p) 119 return m 120 } 121 122 // MakeSimpleFieldsMessage returns a structured Composer that does not attach 123 // any logging metadata, but allows callers to specify the message 124 // (the "message" field) as a string. 125 func MakeSimpleFieldsMessage(msg string, f Fields) Composer { 126 m := &fieldMessage{ 127 message: msg, 128 fields: f, 129 } 130 131 m.setup() 132 return m 133 } 134 135 // NewSimpleFieldsMessage returns a structured Composer that does not attach 136 // any logging metadata, but allows callers to specify the message 137 // (the "message" field) as well as the message's log-level. 138 func NewSimpleFieldsMessage(p level.Priority, msg string, f Fields) Composer { 139 m := MakeSimpleFieldsMessage(msg, f) 140 _ = m.SetPriority(p) 141 return m 142 } 143 144 //////////////////////////////////////////////////////////////////////// 145 // 146 // Implementation 147 // 148 //////////////////////////////////////////////////////////////////////// 149 150 func (m *fieldMessage) setup() { 151 if _, ok := m.fields[FieldsMsgName]; !ok && m.message != "" { 152 m.fields[FieldsMsgName] = m.message 153 } 154 155 if !m.includeMetadata { 156 return 157 } 158 _ = m.Collect(m.includeExtendedMetadata) 159 160 if b, ok := m.fields["metadata"]; !ok { 161 m.fields["metadata"] = &m.Base 162 } else if _, ok = b.(*Base); ok { 163 m.fields["metadata"] = &m.Base 164 } 165 } 166 167 // MakeFields creates a composer interface from *just* a Fields instance. 168 func MakeFields(f Fields) Composer { 169 m := &fieldMessage{fields: f, includeMetadata: true} 170 m.setup() 171 return m 172 } 173 174 func (m *fieldMessage) Loggable() bool { 175 if m.message == "" && len(m.fields) == 0 { 176 return false 177 } 178 179 if len(m.fields) == 1 { 180 if _, ok := m.fields["metadata"]; ok { 181 return false 182 } 183 } 184 185 return true 186 } 187 188 func (m *fieldMessage) String() string { 189 if !m.Loggable() { 190 return "" 191 } 192 193 if m.cachedOutput == "" { 194 out := []string{} 195 if m.message != "" { 196 out = append(out, fmt.Sprintf("%s='%s'", FieldsMsgName, m.message)) 197 } 198 199 for k, v := range m.fields { 200 if k == FieldsMsgName && v == m.message { 201 continue 202 } 203 if k == "time" { 204 continue 205 } 206 if k == "metadata" { 207 continue 208 } 209 210 if str, ok := v.(fmt.Stringer); ok { 211 out = append(out, fmt.Sprintf("%s='%s'", k, str.String())) 212 } else { 213 out = append(out, fmt.Sprintf("%s='%v'", k, v)) 214 } 215 } 216 217 sort.Strings(out) 218 219 m.cachedOutput = fmt.Sprintf("[%s]", strings.Join(out, " ")) 220 } 221 222 return m.cachedOutput 223 } 224 225 func (m *fieldMessage) Raw() interface{} { return m.fields } 226 227 func (m *fieldMessage) Annotate(key string, value interface{}) error { 228 if _, ok := m.fields[key]; ok { 229 return fmt.Errorf("key '%s' already exists", key) 230 } 231 232 m.fields[key] = value 233 234 return nil 235 }