Extending Packages with Go
-
Go makes extending packages easy. You can easily take a package and add your own functionality. Let's use logging as an example.
A popular logging package for Go is logrus. It's at "github.com/sirupsen/logrus". So let's start by creating a package of our own and import logrus as a dependency:
package mangologs import ( "github.com/sirupsen/logrus" )
Now that we've imported logrus, let's extend it by creating our own custom logging entry. Let's say that our company Mangos, Inc requires that we have the app owner and their phone number as part of their application logs (stupid example but you get the point) and that the logs be in JSON format for easier parsing. Let's create our own logging entry so that you don't need to define that every time you write an application:
package mangologs import ( "os" "github.com/sirupsen/logrus" ) type AppOwner struct { Name string Number string } type MangoLogger interface { Debug(args ...interface{}) Info(args ...interface{}) Print(args ...interface{}) Warn(args ...interface{}) Warning(args ...interface{}) Error(args ...interface{}) Fatal(args ...interface{}) Panic(args ...interface{}) } var hostName := os.Getenv("HOSTNAME") func New(name, number string) MangoLogger { logger := logrus.New() logger.SetFormatter(&logrus.JSONFormatter{}) logger.SetReportCaller(true) entry := logger.WithFields(logrus.Fields{ "owner_name": name, "owner_number": number, }) return entry }
We create our own interface that implements only the methods we want our logging package to have. This is both to simplify the example and to show that you can slim down the options available to people if you only want to expose certain things. Then we set the formatting for the logging in the New() function.
Then in our actual application we call our package:
package main import ( "github.com/mangos/mangologs" ) var owner mangologs.AppOwner func main() { owner.Name = "John" owner.Number = "555-555-5555" var log = mangologs.New(owner.Name, owner.Number) log.Error("something went wrong") }
So now our format looks like this
{"file":"/data/data/com.termux/files/usr/tmp/test.MhzSYLadxB/main.go:15","func":"main.main","level":"error","msg":"something went wrong","owner_name":"John","owner_number":"555-555-5555","time":"2019-10-01T23:58:38Z"}
You can see it includes our file, function name, our level, the owner_name, owner_number, and timestamp.
Whereas if we just import logrus and didlog.Error("something went wrong")
it would look like this:ERRO[0000] something went wrong
So now any time you want to use this format, you just need to import your package and call the
mangologs.New()
function with the name and number parameters and it sets up your logging format for you. -
In this example you wouldn't have to write an interface, but that's part of the power of Go. Interfaces are defined implicitly.