Home > Software engineering >  Use struct in place of map in logrus logging
Use struct in place of map in logrus logging

Time:09-17

I'm using the logrus package for logging in a Go project. To display key value pairs in logs, the docs give the following format:

log.WithFields(log.Fields{
    "animal": "walrus",
    "size":   10,
  }).Info("A group of walrus emerges from the ocean")

Instead of using string keys manually in each log, I wish to use a common struct across all logs (to avoid chances of typos in keys).

Something like this:

type LogMessage struct {
    Status  bool        `json:"status"`
    Message string      `json:"message"`
}

log.WithFields(&LogMessage {Status: false, Message: "Error User Already Exists"}).Info("User Creation Failed.")

The log output should be as :

time="2015-03-26T01:27:38-04:00" level=info msg="User Creation Failed." status=false message="Error User Already Exists"

How can this be implemented ?

Thanks for any help !

CodePudding user response:

You cannot pass a struct to WithFields(). It takes Fields type (which is basically map[string]interface{}). To avoid making mistakes in common key names you can create constants - that way if you make typo in constant name code won't even compile (and in the end it will be less verbose to write than passing a struct):

const Status = "status"
const Message = "message"

//...
log.WithFields(log.Fields{Status: true, Message: "a message"}).Info("foo")

To implement exactly what you want you will need to convert struct to a map before passing to WithFields():

import (
    structs "github.com/fatih/structs" // ARCHIVED
    log "github.com/sirupsen/logrus"
)

//...
    
type LogMessage struct {
    Status  bool        `json:"status"`
    Message string      `json:"message"`
}

log.WithFields(structs.Map(&LogMessage{Status: true, Message: "a message"})).Info("foo")

// Will output:
// time="2009-11-10T23:00:00Z" level=info msg=foo Message="a message" Status=true

(Note: I used library "structs" that is archived just to demonstrate principle. Also reflection required to do conversion adds performance cost so I wouldn't use this in performance-critical parts of program).

CodePudding user response:

you can use a custom wrapping function inside which you can set your field keys.

https://play.golang.org/p/H22M63kn8Jb

package main

import (
    log "github.com/sirupsen/logrus"
)

func LogMyMessages(messageStruct *myMessageStruct) {
    log.WithFields(log.Fields{"status": messageStruct.Status, "message": messageStruct.Message}).Info("foo")
}

type myMessageStruct struct {
    Message string
    Status  bool
}

func main() {

    LogMyMessages(&myMessageStruct{Status: true, Message: "test message"})
}

gives a message like so

time="2009-11-10T23:00:00Z" level=info msg=foo message="test message" status=true

  • Related