Fundamentals

Package plugin implements loading and symbol resolution of Go plugins.

Pros:

  • Dynamic Loading: Plugins can be loaded and unloaded dynamically at runtime.
  • Encapsulation: Since plugins run under their own package, there are no conflicts etc.

Cons:

  • Version Dependency: While I was using it, I kept getting the error ‘plugin was built with a different version of package XXX’ because the plugin.so file requires all packages used in the build to be the same as the main application. It can be a bit annoying, but after a bit of thought I can see that it’s almost necessary (better to get an error now than in runtime :))

You can found the further warnings here


Introduction

In order to develop a plugin for main app you need to create a go file like below:

group.go:

package errorg

var symName = "Group"

// Group represents interface that will be implemented by plugins
type Group interface {
   GetErrorMap() map[string]interface{}
}

plugins/plugin.go:

package  main   

type httpError struct {
}

func (g httpError) GetErrorMap() map[string]interface{} {
   return map[string]interface{}{
   	"400": struct {
   		Message string `json:"msg"`
   	}{"The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing)."},
   }
}

var Group httpError

📝️ Useful Notes:

  • package must be main.
  • method name must be same with that will be implemented main app’s interface method name.
  • if return type is not strict such as interface, you can convert the value in main app.
  • golang version and libraries version must be same.

Now we can build to our plugin:

go build  -buildmode=plugin   -o  plugins/plugin.so plugins/plugin.go

This command creates a plugin.so file. (In Linux, a .so file is a Shared Object file, also known as a shared library)

🎉 Finally, we can add the plugin to main app.

group.go:

package errorg

import (
    "fmt"
    "plugin"
    "errors"
)

var symName = "Group"

// Group represents interface that will be implemented by plugins
type Group interface {
  GetErrorMap() map[string]interface{}
}

// loadThePlugin loads the plugin according to the given path
// pluginPath is "plugins/plugin.so"
func loadThePlugin(pluginPath string) error {

    // Open opens a Go plugin.
    plug, err := plugin.Open(pluginPath)
    if err != nil {
     return errors.New(fmt.Sprintf("an error occurred while opening plugin: %v", err))
    }
	
    //Lookup searches for a symbol named symName in plugin p.
    sym, err := plug.Lookup(symName)
    if err != nil {
     return errors.New(fmt.Sprintf("an error occurred while looking plugin: %v", err))
    }

    var group Group
    group, ok := sym.(Group)
    if !ok {
     return errors.New("unexpected type from module symbol")
    }

    errorMap := group.GetErrorMap()
    // do some stuff
    fmt.Println(errorMap)
	
    return nil
}

Thus, our code is loaded as plugin.


Real life usage in app

errorg helps to error handling for golang apps and thanks to golang/plugin it can be added the new plugin The main purpose of this library is to manage all error codes from a common library.

Clone the repo:

git clone https://github.com/cemayan/errorg.git

Example main.go:

package main

import (
    "fmt"
    "github.com/cemayan/errorg"
)

func test() error {
    return errorg.New("http", "401")
}

func init() {
    errorg.Init(errorg.JsonLog())
}

func main() {
    fmt.Println(test())
}

In order to see result on example app make commands can be used like below:

make init
make example

And example app returns like below:

{"msg":"Although the HTTP standard specifies \"unauthorized\", semantically this response means \"unauthenticated\". That is, the client must authenticate itself to get the requested response."}

Conclusion

It is not widely used, but I think it can be used in some cases because it adds a flexible structure* to the app.

  • Plugin can be loaded/unloaded easily at runtime
  • You can add new plugin whenever what you

📚 Further readings:

Real life project:

RPC based plugins:

Optimization:

Thank you for reading. I’m waiting for your questions and comments.