2

I need to print all form requests to log, for later debugging. I need to have something like this, where formInput are different kind of structs. (All defined in my code.)

type SomeFormInput {
    Name string
    Age int
}

func handleForm(formInput interface{}) {
    ...
    logger.Logf("%+v", formInput);
}

However, some of the forms contain "secret" data that needs to be sanitized before logging - specifically, passwords.

What is the best and easiest way to do this, without losing the generality of handleForm?

I still need it to accept interface{}.

I can use tags if needed.

type SomeFormInput struct {
    Name string
    Password string `hide:"true"` // something like this
}
12
  • one way would consist of providing the password to the logger then erase it from each input received (once stringified, just run strings.Replace). Another way is to declare a marshaller, that can handle some secific tags as you demonstrated in your post Commented Dec 10, 2020 at 7:46
  • one way would consist of providing the password to the logger then erase it from each input received (once stringified, just run strings.Replace). - good idea, you can write it as a normal answer and I will accept Commented Dec 10, 2020 at 7:50
  • 3
    @KarelBílek It's not dumb, it often offers better output than "%+v" of the fmt package. And it's a ready solution. Commented Dec 10, 2020 at 7:55
  • 1
    yes, though, this could (potentially) conflict. Maybe later you will need to transmit that password, anyways, and you will be stuck Commented Dec 10, 2020 at 7:56
  • 1
    Why would you use reflect in a custom marshaler? Commented Dec 10, 2020 at 8:33

1 Answer 1

4

There is no completely general solution to this. There are only specific solutions with different trade-offs.

  1. Perhaps the most general solution would be to write a function that inspects each form type with reflection, and omits any sensitive fields in the output.

    logger.Logf("%s", stripPassword(formInput));
    

    This has the side effect of no longer using the %+v verb, which often won't matter, but if your type implements the fmt.Formatter interface, it will change behavior. But then again, your goal is to change behavior, so...

Which leads to a second option:

  1. You could implement a custom Format() method on your type, to cause %+v (or %v or %s) to output whatever you desire (omitting private fields).

  2. Along the same lines, but easier, if you're logging (or willing to log) JSON output, just implement your own json.Marshaler for each type.

    Example:

    func (i *SomeFormInput) MarshalJSON() ([]byte, error) {
        intermediate := struct{
            *SomeFormInput
            Password    struct{} `json:"-"` // Occlude Password field in the embedded struct
            MarshalJSON struct{} `json:"-"` // Occlude MarshalJSON method on embedded struct, to avoid infinite loop
        }{
            SomeFormInput: i,
        }
        return json.Marshal(intermediate)
    }
    
  3. A similar option is to use the json:"-" tag for fields that should not be output.

    Example:

    type SomeFormInput struct {
        Name     string
        Password string `json:"-"`
    }
    
  4. And as a final option, you could filter the log output, prior to sending it to the logger. If you're using JSON, you could parse the data using the tokenizing json.Decoder, to omit only the keys you want. For arbitrary text, perhaps a regular expression could be used.

For most applications I work on, where I practically always use JSON logging, I would probably prefer option #4, followed by #5.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.