Uploaded image for project: 'Thrift'
  1. Thrift
  2. THRIFT-5745

go: Implement slog.LogValuer for exceptions and/or structs

    XMLWordPrintableJSON

Details

    • Task
    • Status: Closed
    • Major
    • Resolution: Fixed
    • None
    • 0.20.0
    • Go - Compiler
    • None

    Description

      To follow up on THRIFT-5744, implement slog.LogValuer on compiler generated exceptions, and maybe also structs.

      The current problem is that when logging an exception, the Stringer implementation will be used, but that usually gives you quite unhelpful string, especially when optional fields are used.

      For example we have this exception defined:

      exception Error {
          1: optional i32 code
          2: optional string message
          3: optional map<string, string> details
          4: optional bool retryable
      }
      

      When you create an instance of it:

      return &baseplate.Error{
        Code: thrift.Int32Ptr(404),
        Message: thrift.Pointer("no such id"),
      }
      

      And then log that error, instead of getting something useful with the code (404) and message ("no such id") in it, you get something like this instead because this is how we implemented fmt.Stringer from the compiler:

      Error({Code:0xc000426648 Message:0xc00041ca10 Details:map[] Retryable:<nil>})
      

      The compiler generates implementation for json.Marshaler for all exceptions, which will be much more helpful. The marshaled json for such error would be:

      {"code":400,"message":"no such id"}
      

      They are also much more expensive, so probably not suitable to replace the current Stringer implementation, but when logging them, it's probably worth it to implement slog.LogValuer so we can log something more useful, probably like this:

      func (p *Error) LogValue() slog.Value{
        var sb strings.Builder
        sb.WriteString("baseplate.Error")
        bytes, err := json.Marshal(p)
        if err != nil {
          // should not happen
          return slog.StringValue(fmt.Sprintf("baseplate.Error.LogValue: failed to marshal json: %v", err))
        }
        sb.Write(bytes)
        return slog.StringValue(sb.String())
      }
      

      Which will make the error logged show as:

      baseplate.Error{"code":400,"message":"no such id"}
      

      Some open questions:

      1. Is this also useful for structs?
      2. Do we want to make this optional (e.g. only generate LogValue function when a compiler flag is passed in)?

      Attachments

        Issue Links

          Activity

            People

              fishywang Yuxuan Wang
              fishywang Yuxuan Wang
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Time Tracking

                  Estimated:
                  Original Estimate - Not Specified
                  Not Specified
                  Remaining:
                  Remaining Estimate - 0h
                  0h
                  Logged:
                  Time Spent - 20m
                  20m