github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/log/eventpb/eventpbgen/gen.go (about) 1 // Copyright 2020 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package main 12 13 import ( 14 "bytes" 15 "flag" 16 "fmt" 17 "os" 18 "regexp" 19 "sort" 20 "strconv" 21 "strings" 22 "text/template" 23 24 "github.com/cockroachdb/cockroachdb-parser/pkg/cli/exit" 25 "github.com/cockroachdb/errors" 26 "github.com/cockroachdb/gostdlib/go/format" 27 ) 28 29 func main() { 30 if err := run(); err != nil { 31 fmt.Fprintln(os.Stderr, "ERROR:", err) 32 exit.WithCode(exit.UnspecifiedError()) 33 } 34 } 35 36 type reInfos struct { 37 reCnt int 38 infos []reInfo 39 reToName map[string]string 40 } 41 42 type reInfo struct { 43 ReName string 44 ReDef string 45 } 46 47 type catInfo struct { 48 Title string 49 Comment string 50 LogChannel string 51 EventNames []string 52 Events []*eventInfo 53 } 54 55 type enumInfo struct { 56 Comment string 57 GoType string 58 Values []enumValInfo 59 } 60 61 type enumValInfo struct { 62 Comment string 63 Name string 64 Value int 65 } 66 67 type eventInfo struct { 68 Comment string 69 LogChannel string 70 GoType string 71 Type string 72 Fields []fieldInfo 73 InheritedFields []fieldInfo 74 AllFields []fieldInfo 75 } 76 77 type fieldInfo struct { 78 Comment string 79 FieldType string 80 FieldName string 81 AlwaysReportingSafe bool 82 ReportingSafeRe string 83 MixedRedactable bool 84 Inherited bool 85 IsEnum bool 86 AllowZeroValue bool 87 } 88 89 var ( 90 packageFlag = flag.String("package", "eventpb", "package to use in generated go") 91 excludeEventFlag = flag.String("excluded-events", "", "regexp of events to exclude") 92 ) 93 94 func run() error { 95 flag.Parse() 96 97 args := flag.CommandLine.Args() 98 if len(args) < 2 { 99 return errors.Newf("usage: %s <template> <protos...>\n", os.Args[0]) 100 } 101 var excludedEvents *regexp.Regexp 102 if *excludeEventFlag != "" { 103 var err error 104 excludedEvents, err = regexp.Compile(*excludeEventFlag) 105 if err != nil { 106 return errors.Wrap(err, "invalid --excluded-events flag") 107 } 108 } 109 110 // Which template are we running? 111 tmplName := args[0] 112 tmplSrc, ok := templates[tmplName] 113 if !ok { 114 return errors.Newf("unknown template: %q", tmplName) 115 } 116 tmplFuncs := template.FuncMap{ 117 // error produces an error. 118 "error": func(s string) string { 119 panic(errors.Newf("template error: %s", s)) 120 }, 121 // tableCell formats strings for use in a table cell. For example, it converts \n\n into <br>. 122 "tableCell": func(s string) string { 123 s = strings.TrimSpace(s) 124 if s == "" { 125 return "" 126 } 127 s = strings.ReplaceAll(s, "\r", "") 128 // Double newlines are paragraph breaks. 129 s = strings.ReplaceAll(s, "\n\n", "<br><br>") 130 // Other newlines are just width wrapping and should be converted to spaces. 131 s = strings.ReplaceAll(s, "\n", " ") 132 return s 133 }, 134 } 135 tmpl, err := template.New(tmplName).Funcs(tmplFuncs).Parse(tmplSrc) 136 if err != nil { 137 return errors.Wrapf(err, "failed to parse template %q", tmplName) 138 } 139 140 // Read the input .proto file. 141 info := map[string]*eventInfo{} 142 enums := map[string]*enumInfo{} 143 cats := map[string]*catInfo{} 144 regexps := reInfos{reToName: map[string]string{}} 145 for i := 1; i < len(args); i++ { 146 if err := readInput(®exps, enums, info, cats, args[i]); err != nil { 147 return err 148 } 149 } 150 151 var keys []string 152 for k := range cats { 153 keys = append(keys, k) 154 } 155 sort.Strings(keys) 156 var sortedInfos []*eventInfo 157 var sortedCats []*catInfo 158 for _, k := range keys { 159 cat := cats[k] 160 sort.Strings(cat.EventNames) 161 for _, evname := range cat.EventNames { 162 ev := info[evname] 163 cat.Events = append(cat.Events, ev) 164 sortedInfos = append(sortedInfos, ev) 165 } 166 sortedCats = append(sortedCats, cat) 167 } 168 169 keys = nil 170 for k := range info { 171 if excludedEvents != nil && excludedEvents.MatchString(k) { 172 continue 173 } 174 keys = append(keys, k) 175 } 176 sort.Strings(keys) 177 var allSortedInfos []*eventInfo 178 for _, k := range keys { 179 allSortedInfos = append(allSortedInfos, info[k]) 180 } 181 182 keys = nil 183 for k := range enums { 184 keys = append(keys, k) 185 } 186 sort.Strings(keys) 187 var allSortedEnums []*enumInfo 188 for _, k := range keys { 189 allSortedEnums = append(allSortedEnums, enums[k]) 190 } 191 192 // Render the template. 193 var src bytes.Buffer 194 if err := tmpl.Execute(&src, struct { 195 Package string 196 AllRegexps []reInfo 197 Categories []*catInfo 198 Events []*eventInfo 199 AllEvents []*eventInfo 200 Enums []*enumInfo 201 }{ 202 *packageFlag, 203 regexps.infos, 204 sortedCats, 205 sortedInfos, 206 allSortedInfos, 207 allSortedEnums, 208 }); err != nil { 209 return err 210 } 211 212 // If we are generating a .go file, do a pass of gofmt. 213 newBytes := src.Bytes() 214 if strings.HasSuffix(tmplName, "_go") { 215 newBytes, err = format.Source(newBytes) 216 if err != nil { 217 return errors.Wrap(err, "gofmt") 218 } 219 } 220 221 // Write the output file. 222 w := os.Stdout 223 if _, err := w.Write(newBytes); err != nil { 224 return err 225 } 226 227 return nil 228 } 229 230 func readInput( 231 regexps *reInfos, 232 enums map[string]*enumInfo, 233 infos map[string]*eventInfo, 234 cats map[string]*catInfo, 235 protoName string, 236 ) error { 237 protoData, err := os.ReadFile(protoName) 238 if err != nil { 239 return err 240 } 241 inMsg := false 242 inEnum := false 243 comment := "" 244 channel := "" 245 var curCat *catInfo 246 var curMsg *eventInfo 247 var curEnum *enumInfo 248 for _, line := range strings.Split(string(protoData), "\n") { 249 line = strings.TrimSpace(line) 250 251 if strings.HasPrefix(line, "//") { 252 comment += strings.TrimSpace(line[2:]) + "\n" 253 continue 254 } 255 256 if line == "" { 257 if strings.HasPrefix(comment, "Category:") { 258 lines := strings.SplitN(comment, "\n", 3) 259 if len(lines) < 3 || !strings.HasPrefix(lines[1], "Channel:") { 260 return errors.New("invalid category comment: missing Channel specification") 261 } 262 title := strings.TrimSpace(strings.SplitN(lines[0], ":", 2)[1]) 263 channel = strings.TrimSpace(strings.SplitN(lines[1], ":", 2)[1]) 264 if _, ok := channels[channel]; !ok { 265 return errors.Newf("unknown channel name: %q", channel) 266 } 267 curCat = &catInfo{ 268 Title: title, 269 Comment: strings.TrimSpace(strings.Join(lines[2:], "\n")), 270 LogChannel: channel, 271 } 272 cats[title] = curCat 273 } 274 275 comment = "" 276 continue 277 } 278 279 if !inEnum && !inMsg && strings.HasPrefix(line, "enum ") { 280 inEnum = true 281 282 typ := strings.Split(line, " ")[1] 283 if _, ok := enums[typ]; ok { 284 return errors.Newf("duplicate enum type: %q", typ) 285 } 286 287 curEnum = &enumInfo{ 288 Comment: comment, 289 GoType: typ, 290 } 291 comment = "" 292 enums[typ] = curEnum 293 continue 294 } 295 if inEnum { 296 if strings.HasPrefix(line, "}") { 297 inEnum = false 298 comment = "" 299 continue 300 } 301 302 // At this point, we don't support definitions that don't fit on a single line. 303 if !strings.Contains(line, ";") { 304 return errors.Newf("enum value definition must not span multiple lines: %q", line) 305 } 306 307 if !enumValDefRe.MatchString(line) { 308 return errors.Newf("invalid enum value definition: %q", line) 309 } 310 311 tag := enumValDefRe.ReplaceAllString(line, "$tag") 312 val := enumValDefRe.ReplaceAllString(line, "$val") 313 vali, err := strconv.Atoi(val) 314 if err != nil { 315 return errors.Wrapf(err, "parsing %q", line) 316 } 317 318 comment = strings.TrimSpace(strings.TrimPrefix(comment, tag)) 319 320 curEnum.Values = append(curEnum.Values, enumValInfo{ 321 Comment: comment, 322 Name: tag, 323 Value: vali, 324 }) 325 comment = "" 326 } 327 328 if !inMsg && !inEnum && strings.HasPrefix(line, "message ") { 329 inMsg = true 330 331 typ := strings.Split(line, " ")[1] 332 if _, ok := infos[typ]; ok { 333 return errors.Newf("duplicate message type: %q", typ) 334 } 335 snakeType := camelToSnake(typ) 336 337 if strings.HasPrefix(comment, typ) { 338 comment = "An event of type `" + snakeType + "`" + strings.TrimPrefix(comment, typ) 339 } 340 curMsg = &eventInfo{ 341 Comment: comment, 342 GoType: typ, 343 Type: snakeType, 344 LogChannel: channel, 345 } 346 comment = "" 347 infos[typ] = curMsg 348 if !strings.HasPrefix(typ, "Common") { 349 if curCat == nil { 350 return errors.New("missing category specification at top of file") 351 } 352 353 curCat.EventNames = append(curCat.EventNames, typ) 354 } 355 356 continue 357 } 358 if inMsg { 359 if strings.HasPrefix(line, "}") { 360 inMsg = false 361 comment = "" 362 continue 363 } 364 365 // At this point, we don't support definitions that don't fit on a single line. 366 if !strings.Contains(line, ";") { 367 return errors.Newf("field definition must not span multiple lines: %q", line) 368 } 369 370 // Skip reserved fields. 371 if reservedDefRe.MatchString(line) { 372 continue 373 } 374 375 // A field. 376 if strings.HasPrefix(line, "repeated") { 377 line = "array_of_" + strings.TrimSpace(strings.TrimPrefix(line, "repeated")) 378 } 379 if !fieldDefRe.MatchString(line) { 380 return errors.Newf("unknown field definition syntax: %q", line) 381 } 382 383 // Allow zero values if the field is annotated with 'includeempty'. 384 allowZeroValue := strings.Contains(line, "includeempty") 385 386 typ := fieldDefRe.ReplaceAllString(line, "$typ") 387 switch typ { 388 case "google.protobuf.Timestamp": 389 typ = "timestamp" 390 case "cockroach.sql.sqlbase.Descriptor": 391 typ = "protobuf" 392 } 393 394 if otherMsg, ok := infos[typ]; ok { 395 // Inline the fields from the other messages here. 396 curMsg.InheritedFields = append(curMsg.InheritedFields, otherMsg.Fields...) 397 curMsg.AllFields = append(curMsg.AllFields, fieldInfo{ 398 FieldType: typ, 399 FieldName: typ, 400 Inherited: true, 401 }) 402 } else { 403 _, isEnum := enums[typ] 404 405 name := snakeToCamel(fieldDefRe.ReplaceAllString(line, "$name")) 406 alwayssafe := false 407 mixed := false 408 if nameOverride := fieldDefRe.ReplaceAllString(line, "$noverride"); nameOverride != "" { 409 name = nameOverride 410 } 411 // redact:"nonsensitive" - always safe for reporting. 412 if reportingSafe := fieldDefRe.ReplaceAllString(line, "$reportingsafe"); reportingSafe != "" { 413 alwayssafe = true 414 if reportingSafe == "mixed" { 415 mixed = true 416 } 417 } 418 // Certain types are also always safe for reporting. 419 if !alwayssafe && isSafeType(typ) { 420 alwayssafe = true 421 } 422 // redact:"safeif:<regexp>" - safe for reporting if the string matches the regexp. 423 safeReName := "" 424 if re := fieldDefRe.ReplaceAllString(line, "$safeif"); re != "" { 425 var err error 426 // We're reading the regular expression from the .proto source, so we must 427 // take care of string un-escaping ourselves. If this code ever improves 428 // to apply as a protobuf plugin, this step can be removed. 429 re, err = strconv.Unquote(`"` + re + `"`) 430 if err != nil { 431 return errors.Wrapf(err, "error while unquoting regexp at %q", line) 432 } 433 safeRe := "^" + re + "$" 434 // Syntax check on regexp. 435 _, err = regexp.Compile(safeRe) 436 if err != nil { 437 return errors.Wrapf(err, "regexp %s is invalid (%q)", re, line) 438 } 439 // We want to reuse the regexp variables across fields if the regexps are the same. 440 if n, ok := regexps.reToName[safeRe]; ok { 441 safeReName = n 442 } else { 443 regexps.reCnt++ 444 safeReName = fmt.Sprintf("safeRe%d", regexps.reCnt) 445 regexps.reToName[safeRe] = safeReName 446 regexps.infos = append(regexps.infos, reInfo{ReName: safeReName, ReDef: safeRe}) 447 } 448 } 449 fi := fieldInfo{ 450 Comment: comment, 451 FieldType: typ, 452 FieldName: name, 453 AlwaysReportingSafe: alwayssafe, 454 ReportingSafeRe: safeReName, 455 MixedRedactable: mixed, 456 IsEnum: isEnum, 457 AllowZeroValue: allowZeroValue, 458 } 459 curMsg.Fields = append(curMsg.Fields, fi) 460 curMsg.AllFields = append(curMsg.AllFields, fi) 461 } 462 comment = "" 463 } 464 } 465 466 return nil 467 } 468 469 func isSafeType(typ string) bool { 470 switch typ { 471 case "timestamp", "int32", "int64", "int16", "uint32", "uint64", "uint16", "bool", "float", "double": 472 return true 473 } 474 return false 475 } 476 477 var enumValDefRe = regexp.MustCompile(`\s*(?P<tag>[_A-Z0-9]+)[^=]*=[^0-9]*(?P<val>[0-9]+).*;`) 478 479 var fieldDefRe = regexp.MustCompile(`\s*(?P<typ>[a-z._A-Z0-9]+)` + 480 `\s+(?P<name>[a-z_]+)` + 481 `(;|` + 482 `\s+(.*customname\) = "(?P<noverride>[A-Za-z]+)")?` + 483 `(.*"redact:\\"(?P<reportingsafe>nonsensitive|mixed)\\"")?` + 484 `(.*"redact:\\"safeif:(?P<safeif>([^\\]|\\[^"])+)\\"")?` + 485 `).*$`) 486 487 var reservedDefRe = regexp.MustCompile(`\s*(reserved ([1-9][0-9]*);)`) 488 489 func camelToSnake(typeName string) string { 490 var res strings.Builder 491 res.WriteByte(typeName[0] + 'a' - 'A') 492 for i := 1; i < len(typeName); i++ { 493 if typeName[i] >= 'A' && typeName[i] <= 'Z' { 494 res.WriteByte('_') 495 res.WriteByte(typeName[i] + 'a' - 'A') 496 } else { 497 res.WriteByte(typeName[i]) 498 } 499 } 500 return res.String() 501 } 502 503 func snakeToCamel(typeName string) string { 504 var res strings.Builder 505 res.WriteByte(typeName[0] + 'A' - 'a') 506 for i := 1; i < len(typeName); i++ { 507 if typeName[i] == '_' { 508 i++ 509 res.WriteByte(typeName[i] + 'A' - 'a') 510 } else { 511 res.WriteByte(typeName[i]) 512 } 513 } 514 return res.String() 515 } 516 517 var templates = map[string]string{ 518 "json_encode_go": `// Code generated by gen.go. DO NOT EDIT. 519 520 package {{ .Package }} 521 522 import ( 523 "strconv"{{ if .AllRegexps }} 524 "regexp"{{end}} 525 526 "github.com/cockroachdb/redact" 527 "github.com/cockroachdb/cockroachdb-parser/pkg/util/jsonbytes" 528 "github.com/gogo/protobuf/jsonpb" 529 ) 530 531 {{range .AllRegexps}} 532 var {{ .ReName }} = regexp.MustCompile(` + "`{{ .ReDef }}`" + `) 533 {{end}} 534 var _ = jsonpb.Marshaler{} 535 536 {{range .AllEvents}} 537 // AppendJSONFields implements the EventPayload interface. 538 func (m *{{.GoType}}) AppendJSONFields(printComma bool, b redact.RedactableBytes) (bool, redact.RedactableBytes) { 539 {{range .AllFields }} 540 {{if .Inherited -}} 541 printComma, b = m.{{.FieldName}}.AppendJSONFields(printComma, b) 542 {{- else if eq .FieldType "string" -}} 543 {{ if not .AllowZeroValue -}} 544 if m.{{.FieldName}} != "" { 545 {{- end }} 546 if printComma { b = append(b, ',')}; printComma = true 547 b = append(b, "\"{{.FieldName}}\":\""...) 548 {{ if .AlwaysReportingSafe -}} 549 b = redact.RedactableBytes(jsonbytes.EncodeString([]byte(b), string(m.{{.FieldName}}))) 550 {{- else if ne .ReportingSafeRe "" }} 551 if {{ .ReportingSafeRe }}.MatchString(m.{{.FieldName}}) { 552 b = redact.RedactableBytes(jsonbytes.EncodeString([]byte(b), string(redact.EscapeMarkers([]byte(m.{{.FieldName}}))))) 553 } else { 554 b = append(b, redact.StartMarker()...) 555 b = redact.RedactableBytes(jsonbytes.EncodeString([]byte(b), string(redact.EscapeMarkers([]byte(m.{{.FieldName}}))))) 556 b = append(b, redact.EndMarker()...) 557 } 558 {{- else -}} 559 b = append(b, redact.StartMarker()...) 560 b = redact.RedactableBytes(jsonbytes.EncodeString([]byte(b), string(redact.EscapeMarkers([]byte(m.{{.FieldName}}))))) 561 b = append(b, redact.EndMarker()...) 562 {{- end }} 563 b = append(b, '"') 564 {{ if not .AllowZeroValue -}} 565 } 566 {{- end }} 567 {{- else if eq .FieldType "array_of_string" -}} 568 if len(m.{{.FieldName}}) > 0 { 569 if printComma { b = append(b, ',')}; printComma = true 570 b = append(b, "\"{{.FieldName}}\":["...) 571 for i, v := range m.{{.FieldName}} { 572 if i > 0 { b = append(b, ',') } 573 b = append(b, '"') 574 {{ if .AlwaysReportingSafe -}} 575 b = redact.RedactableBytes(jsonbytes.EncodeString([]byte(b), v)) 576 {{- else if ne .ReportingSafeRe "" }} 577 if {{ .ReportingSafeRe }}.MatchString(v) { 578 b = redact.RedactableBytes(jsonbytes.EncodeString([]byte(b), string(redact.EscapeMarkers([]byte(v))))) 579 } else { 580 b = append(b, redact.StartMarker()...) 581 b = redact.RedactableBytes(jsonbytes.EncodeString([]byte(b), string(redact.EscapeMarkers([]byte(v))))) 582 b = append(b, redact.EndMarker()...) 583 } 584 {{- else -}} 585 b = append(b, redact.StartMarker()...) 586 b = redact.RedactableBytes(jsonbytes.EncodeString([]byte(b), string(redact.EscapeMarkers([]byte(v))))) 587 b = append(b, redact.EndMarker()...) 588 {{- end }} 589 b = append(b, '"') 590 } 591 b = append(b, ']') 592 } 593 {{- else if eq .FieldType "bool" -}} 594 {{ if not .AllowZeroValue -}} 595 if m.{{.FieldName}} { 596 {{- end }} 597 if printComma { b = append(b, ',')}; printComma = true 598 b = append(b, "\"{{.FieldName}}\":true"...) 599 {{ if not .AllowZeroValue -}} 600 } 601 {{- end }} 602 {{- else if eq .FieldType "int16" "int32" "int64"}} 603 {{ if not .AllowZeroValue -}} 604 if m.{{.FieldName}} != 0 { 605 {{- end }} 606 if printComma { b = append(b, ',')}; printComma = true 607 b = append(b, "\"{{.FieldName}}\":"...) 608 b = strconv.AppendInt(b, int64(m.{{.FieldName}}), 10) 609 {{ if not .AllowZeroValue -}} 610 } 611 {{- end }} 612 {{- else if eq .FieldType "float"}} 613 {{ if not .AllowZeroValue -}} 614 if m.{{.FieldName}} != 0 { 615 {{- end }} 616 if printComma { b = append(b, ',')}; printComma = true 617 b = append(b, "\"{{.FieldName}}\":"...) 618 b = strconv.AppendFloat(b, float64(m.{{.FieldName}}), 'f', -1, 32) 619 {{ if not .AllowZeroValue -}} 620 } 621 {{- end }} 622 {{- else if eq .FieldType "double"}} 623 {{ if not .AllowZeroValue -}} 624 if m.{{.FieldName}} != 0 { 625 {{- end }} 626 if printComma { b = append(b, ',')}; printComma = true 627 b = append(b, "\"{{.FieldName}}\":"...) 628 b = strconv.AppendFloat(b, float64(m.{{.FieldName}}), 'f', -1, 64) 629 {{ if not .AllowZeroValue -}} 630 } 631 {{- end }} 632 {{- else if eq .FieldType "uint16" "uint32" "uint64"}} 633 {{ if not .AllowZeroValue -}} 634 if m.{{.FieldName}} != 0 { 635 {{- end }} 636 if printComma { b = append(b, ',')}; printComma = true 637 b = append(b, "\"{{.FieldName}}\":"...) 638 b = strconv.AppendUint(b, uint64(m.{{.FieldName}}), 10) 639 {{ if not .AllowZeroValue -}} 640 } 641 {{- end }} 642 {{- else if eq .FieldType "array_of_uint32" -}} 643 if len(m.{{.FieldName}}) > 0 { 644 if printComma { b = append(b, ',')}; printComma = true 645 b = append(b, "\"{{.FieldName}}\":["...) 646 for i, v := range m.{{.FieldName}} { 647 if i > 0 { b = append(b, ',') } 648 b = strconv.AppendUint(b, uint64(v), 10) 649 } 650 b = append(b, ']') 651 } 652 {{- else if eq .FieldType "array_of_int32" -}} 653 if len(m.{{.FieldName}}) > 0 { 654 if printComma { b = append(b, ',')}; printComma = true 655 b = append(b, "\"{{.FieldName}}\":["...) 656 for i, v := range m.{{.FieldName}} { 657 if i > 0 { b = append(b, ',') } 658 b = strconv.AppendInt(b, int64(v), 10) 659 } 660 b = append(b, ']') 661 } 662 {{- else if .IsEnum }} 663 {{ if not .AllowZeroValue -}} 664 if m.{{.FieldName}} != 0 { 665 {{- end }} 666 if printComma { b = append(b, ',')}; printComma = true 667 b = append(b, "\"{{.FieldName}}\":"...) 668 // Enums are defined in our code, so are always safe to print without 669 // redaction. 670 b = append(b, '"') 671 b = append(b, m.{{.FieldName}}.String()...) 672 b = append(b, '"') 673 {{ if not .AllowZeroValue -}} 674 } 675 {{- end }} 676 {{- else if eq .FieldType "array_of_LevelStats"}} 677 if len(m.{{.FieldName}}) > 0 { 678 if printComma { b = append(b, ',')}; printComma = true 679 b = append(b, "\"{{.FieldName}}\":["...) 680 for i, l := range m.{{.FieldName}} { 681 if i > 0 { b = append(b, ',') } 682 b = append(b, '{') 683 printComma, b = l.AppendJSONFields(false, b) 684 b = append(b, '}') 685 } 686 b = append(b, ']') 687 } 688 {{- else if eq .FieldType "protobuf"}} 689 if m.{{.FieldName}} != nil { 690 if printComma { b = append(b, ',')}; printComma = true 691 jsonEncoder := jsonpb.Marshaler{} 692 if str, err := jsonEncoder.MarshalToString(m.{{.FieldName}}); err == nil { 693 b = append(b, "\"{{.FieldName}}\":"...) 694 b = append(b, []byte(str)...) 695 } 696 } 697 {{- else}} 698 {{ error .FieldType }} 699 {{- end}} 700 {{end}} 701 return printComma, b 702 } 703 {{end}} 704 705 706 `, 707 708 "eventlog_channels_go": `// Code generated by gen.go. DO NOT EDIT. 709 710 package {{ .Package }} 711 712 import "github.com/cockroachdb/cockroachdb-parser/pkg/util/log/logpb" 713 714 {{range .Events}} 715 // LoggingChannel implements the EventPayload interface. 716 func (m *{{.GoType}}) LoggingChannel() logpb.Channel { return logpb.Channel_{{.LogChannel}} } 717 {{end}} 718 `, 719 720 "eventlog.md": `Certain notable events are reported using a structured format. 721 Commonly, these notable events are also copied to the table 722 ` + "`system.eventlog`" + `, unless the cluster setting 723 ` + "`server.eventlog.enabled`" + ` is unset. 724 725 Additionally, notable events are copied to specific external logging 726 channels in log messages, where they can be collected for further processing. 727 728 The sections below document the possible notable event types 729 in this version of CockroachDB. For each event type, a table 730 documents the possible fields. A field may be omitted from 731 an event if its value is empty or zero. 732 733 A field is also considered "Sensitive" if it may contain 734 application-specific information or personally identifiable information (PII). In that case, 735 the copy of the event sent to the external logging channel 736 will contain redaction markers in a format that is compatible 737 with the redaction facilities in ` + "[`cockroach debug zip`](cockroach-debug-zip.html)" + ` 738 and ` + "[`cockroach debug merge-logs`](cockroach-debug-merge-logs.html)" + `, 739 provided the ` + "`redactable`" + ` functionality is enabled on the logging sink. 740 741 Events not documented on this page will have an unstructured format in log messages. 742 743 {{range .Categories -}} 744 ## {{.Title}} 745 746 {{.Comment}} 747 748 Events in this category are logged to the ` + "`" + `{{.LogChannel}}` + "`" + ` channel. 749 750 {{range .Events}} 751 ### ` + "`" + `{{.Type}}` + "`" + ` 752 753 {{.Comment}} 754 755 {{if .Fields -}} 756 | Field | Description | Sensitive | 757 |--|--|--| 758 {{range .Fields -}} 759 | ` + "`" + `{{- .FieldName -}}` + "`" + ` | {{ .Comment | tableCell }}{{- if .IsEnum }} See below for possible values for type ` + "`" + `{{- .FieldType -}}` + "`" + `.{{- end }} | {{ if .MixedRedactable }}partially{{ else if .AlwaysReportingSafe }}no{{else if ne .ReportingSafeRe "" }}depends{{else}}yes{{end}} | 760 {{end}} 761 {{- end}} 762 763 {{if .InheritedFields -}} 764 #### Common fields 765 766 | Field | Description | Sensitive | 767 |--|--|--| 768 {{range .InheritedFields -}} 769 | ` + "`" + `{{- .FieldName -}}` + "`" + ` | {{ .Comment | tableCell }} | {{ if .MixedRedactable }}partially{{ else if .AlwaysReportingSafe }}no{{else if ne .ReportingSafeRe "" }}depends{{else}}yes{{end}} | 770 {{end}} 771 {{- end}} 772 773 {{- end}} 774 {{end}} 775 776 {{if .Enums}} 777 ## Enumeration types 778 {{range .Enums}} 779 ### ` + "`" + `{{ .GoType }}` + "`" + ` 780 781 {{ .Comment }} 782 783 | Value | Textual alias in code or documentation | Description | 784 |--|--|--| 785 {{range .Values -}} 786 | {{ .Value }} | {{ .Name }} | {{ .Comment | tableCell }} | 787 {{end}} 788 {{end}} 789 {{end}} 790 `, 791 }