How golang's init() works. I am confused - go

I have a init() function defined in "config/config.go"
config.go
package config
import(
log "github.com/sirupsen/logrus"
)
func init() {
log.SetReportCaller(true)
}
I have another go file called auth.go in auth package
package auth
import(
log "github.com/sirupsen/logrus"
)
func auth(username string, pwd string) {
//some auth code
log.Info("Auth success")
}
When log.Info() is called in auth.go the log prints as below
2018-11-09T16:38:27+05:30 auth/auth.go:36 level=info msg="Auth success"
What I am confused here is that, how "log" in auth.go is aware of the settings done in config.go. log.SetReportCaller() is in config.go but even when auth.go is logged it takes settings of log.SetReportCaller() done in config.go
Since log.SetReportCaller() is not set in auth.go expected log should be as below without showing line number of caller method.
2018-11-09T16:38:27+05:30 level=info msg="Auth success"
main.go
package main
import (
"path/to/auth"
log "github.com/sirupsen/logrus"
"net/http"
)
func main() {
r := server.Route()
log.Info("Listening on 8080")
http.Handle("/", r)
log.Fatal(http.ListenAndServe(":8080", nil))
}
auth/router.go
package auth
import (
"github.com/gorilla/mux"
"github.com/rs/cors"
"net/http"
)
func Route() http.Handler {
r := mux.NewRouter()
// UI handlers
r.HandleFunc("/", IndexPageHandler)
r.HandleFunc("/login", LoginHandler).Methods("POST")
handler := cors.Default().Handler(r)
return
}
auth/login.go
package auth
import (
"fmt"
"path/to/config"
log "github.com/sirupsen/logrus"
"net/http"
)
func LoginHandler(w http.ResponseWriter, r *http.Request) {
username := r.FormValue("username")
password := r.FormValue("password")
status, userName, mail, _ := auth(username, password)
//some code goes here
}
Kindly explain how this is happening

Check this diagram to understand how init() work: diagram
Initialization order are as follows,
If a package imports other packages, the imported packages are initialised first.
Package level variables are initialised then.
init function of current package is called next. A package can have multiple init functions (either in a single file or distributed across multiple files) and they are called in the order in which they are presented to the compiler.
You will found an example explaining this here.
I am suspecting in dependency tree, there is a common ancestor file that import both config package and auth package.
Here is official doc on initialization: Package initialization
UPD: As you have added respective codes, let's visualize what is happening here. Look at the picture bellow,
What happening:
Your main package start initialing. But it is has imported auth package. So to complete initialization, auth package must be initialized.
auth package start initializing. But it has imported config package. So to complete initialization, config package must be initialized.
config package complete initialization(log.SetReportCaller(true) is called).
auth package complete initialization.
main package complete initialization.
main() function starts executing...

The behavior you're asking about actually has nothing to do with how init() functions work. SetReportCaller sets a global variable in github.com/sirupsen/logrus, not in your config or auth packages. So it doesn't matter where you call that function or where you call log.Info et al; the setting affects all calls to logrus regardless of call origin.

Related

Initialize internal package global from parent package global?

I am trying to use a global variable defined in parent package in the internal package. There are no errors but none of the logs from internal package are printed. My assumption on order of init is: parent.go global variables and then init() from internal package; which seems right when tested with fmt.Println(). But looks like the call to ReplaceGlobals is dropped during compilation. Is there a way to fix it so that I can use global variable from internal package?
Here is the zap package I am using: zap. The API in question is discussed here. Here is the code snippet.
~/parent/parent.go
package main
import (
"log"
"go.uber.org/zap"
"parent/internal"
)
var (
logger = GetMyLogger()
undo = zap.ReplaceGlobals(logger.Desugar())
)
func GetMyLogger() *zap.SugaredLogger {
xx, err := zap.NewProduction()
if err != nil {
log.Fatal("error")
}
sugar := xx.Sugar()
return sugar
}
func main() {
logger.Infof("logger in main")
internal.New()
}
Then in ~/parent/internal/internal.go
package internal
import (
"go.uber.org/zap"
)
var logger *zap.SugaredLogger
func init() {
logger = zap.L().Sugar()
logger.Infof("init() from internal: %v\n", logger)
logger = zap.S()
logger.Infof("init() from internal: %v\n", logger)
}
func New() {
logger.Infof("New() from internal")
}
Package initialization order is well defined, see Spec: Package initialization. Basically you can be sure that if you refer to a package, that package's initialization will be done first, recursively.
Since your internal package does not refer to your main, you have no guarantee if main is initialized before anything is executed from internal.
An easy solution in your case is to move the logger to your internal package, and have main refer / use that.
If you have more packages involved and you want to use the same logger from other packages, you may have a designated package holding the logger, and have everyone refer / use that.

golang : Custom package and 'undefined'

I've read the doc on creating custom packages, etc but I can't seem to pin down what the problem is.
GOPATH=/Users/lrsmith/GoWorkSpace
|->bin
|->pkg
|->src
|->github.com
|->lrsmith
|-> zaphod
|-> zaphod.go
I've done a 'go get github.com/lrsmith/go-icinga2-api/iapi' and it
dropped it into the same dir as 'zaphod' and created and .a file under pkg.
GOPATH=/Users/lrsmith/GoWorkSpace
|->bin/
|->pkg/
|->..../iapi.a
|->src/
|->github.com/
|->lrsmith/
|-> zaphod/
|-> zaphod.go
|-> go-icinga2-api/
zaphod.go is very simple right now
package main
import (
"github.com/lrsmith/go-icinga2-api/iapi"
)
func main () {
t := iapi.Config("zaphod","beeblebrox","http://localhost",true)
}
When I do a go build in the zaphod directory I get
./zaphod.go:11: undefined: iapi.Config
I've read through the docs, checked cases and tried different structures but I can't seem to get it to load the package and let me call iapi.Config. The iapi code works and if I build something in the go-icinga2-api directory it works fine and the test all pass.
I want to create a separate project/code base that imports the go-icinga2-api and uses it, but can't seem to get it work.
Thanks
Len
Added info. The structure for go-icinga2-api is
go-icinga2-api
|-> iapi
|-> client.go
|-> client_test.go
|-> host.go
.......
client.go is
// Package iapi provides a client for interacting with an Icinga2 Server
package iapi
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"net/http"
)
// Server ... Use to be ClientConfig
type Server struct {
Username string
Password string
BaseURL string
AllowUnverifiedSSL bool
httpClient *http.Client
}
// func Config ...
func (server *Server) Config(username, password, url string, allowUnverifiedSSL bool) (*Server, error) {
// TODO : Add code to verify parameters
return &Server{username, password, url, allowUnverifiedSSL, nil}, nil
}
I've tried with the .go files up one level, i.e. not nested underneath iapi/ to for the same results.
Updated Answer
In client.go, it looks like you're trying to use Config as a constructor for Server structs. Since Config is defined with a receiver (func (server *Server)), it's a method of Server and can't be called directly. You're code should work if you remove (server *Server).
It's idiomatic to name constructors New[type being returned], or New if the type is the same name as the package.
From the 3rd paragraph of the Package Names section of Idiomatic Go:
the function to make new instances of ring.Ring—which is the definition of a constructor in Go—would normally be called NewRing, but since Ring is the only type exported by the package, and since the package is called ring, it's called just New, which clients of the package see as ring.New
Original Answer
The import path should reference a directory. In your code, you then reference that package by whatever name is used in the package [name] in the .go files in that directory.
For example, if github.com/lrsmith/go-icinga2-api contains a file called api.go with the line package iapi, your importing package should look like this:
package main
import (
"github.com/lrsmith/go-icinga2-api"
)
func main () {
t := iapi.Config("zaphod","beeblebrox","http://localhost",true)
}
Note the declaration of the Config() function:
func (server *Server) Config(username, password, url string, allowUnverifiedSSL bool) (*Server, error)
It's a "method" that should be applied to a Server object:
server.Config(...)
Thus you need to first create a Server object (or you could try with nil):
var server iapi.Server
server, err := server.Config(...)
You're trying to run it as if it had the following declaration:
func Config(username, password, url string, allowUnverifiedSSL bool) (*Server, error)

Cycle import issue in gorilla mux routing with multiple sub packages

Here is my project structure
--main package
--|--child_package1
--|--child_package2
--|--child_package3
I have all the the routes and method call management for API calls listed in main_package
The router Handler from main_package.go looks like this:
func Handlers(db *sql.DB, customeruploadFile string) *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/api1", child_package1.method )
router.HandleFunc("/api2", child_package2.method)
router.HandleFunc("/api3", child_package3.mehtod)
fileHandler := http.FileServer(http.Dir("./client/compiled"))
router.PathPrefix("/").Handler(http.StripPrefix("/", fileHandler))
return router
}
The problem is when I write test cases for child packages, I need this Handlers method to create test server, for that I need to import main_package in child_packages, then as obvious there is cycle imports happening, as child_packages are imported in main_package.
Can any one please suggest me the best approach to tackle this?
I assume your main_package isn't the main package in Go. I think the child_packages shouldn't be under the main_package as our goal is to decouple each package from one another.
This is the pattern that I'm currently using in my project to avoid dependency conflicts:
project/
├── main_package
│ └── main_package.go
├── brokers
│ └── brokers.go
├── child_package1
│   └── child_package1.go
├── child_package2
│   └── child_package2.go
└── child_package3
   └── child_package3.go
Essentially, each package should never have to deal with anything outside of itself (or at least do so with as little as possible). The broker will be the sole party who "negotiates" between any two packages.
// main_package.go
package main_package
import (
"path/to/sql"
"path/to/mux"
"path/to/brokers"
)
// Never use selectors from packages directly
// but create a `Broker` object for each endpoint
var bk1 = brokers.New("/api1")
var bk2 = brokers.New("/api2")
var bk3 = brokers.New("/api3")
func Handlers(db *sql.DB, customeruploadFile string) *mux.Router {
router := mux.NewRouter()
// each broker has its own `MyHandler` function
router.HandleFunc("/api1", bk1.MyHandler)
router.HandleFunc("/api2", bk2.MyHandler)
router.HandleFunc("/api3", bk3.MyHandler)
fileHandler := http.FileServer(http.Dir("./client/compiled"))
router.PathPrefix("/").Handler(http.StripPrefix("/", fileHandler))
return router
}
The brokers package is the central interface for the communication
// brokers.go
package brokers
import (
"path/to/child_package1"
"path/to/child_package2"
"path/to/child_package3"
"net/http"
)
type Broker interface {
MyHandler(http.ResponseWriter, *http.Request)
}
// Factory function to create a `Broker` instance
func New(uri string) Broker {
if uri == "/api1" {
return Broker( new(child_package1.Delegate) )
} else if uri == "/api2" {
return Broker( new(child_package2.Delegate) )
} else if uri == "/api3" {
return Broker( new(child_package3.Delegate) )
}
return nil
}
Now child_packageX is no long decoupled to any internal dependency, provided
it expose a "representative" or Delegate object to talk to the broker.
// child_package1.go
package child_package1
import "net/http"
type Delegate struct {
// Optional parameters can be carried by the Delegate
// to be used in the created Broker anywhere
}
func (d *Delegate) MyHandler(w http.ResponseWriter, r *http.Request) {
// Maybe return a JSON here
}
Each child can have its own MyHandler that does different things for different api calls, without having to know what endpoints they are serving.
// child_package2
package child_package2
import "net/http"
type Delegate struct {}
func (d *Delegate) MyHandler(w http.ResponseWriter, r *http.Request) {
// Maybe return an XML here
}
The main_package doesn't import all the child_packageX, but just the broker package. You can write a test that imports the broker package instead of the actual packages, or you can even write another broker for testing.
package test
import (
"testing"
"path/to/main_package"
)
func TestMain(*testing.T) {
// test the routing in `main_package`
}
You're no longer testing a functionality of a handler function, but one of an endpoint exposed by a broker. This encourage you to write generic handler functions and focus on the higher level endpoints.
package test
import (
"testing"
"path/to/broker"
)
func TestGetJSONAlright(*testing.T) {
bk1 := brokers.New("/api1")
// test if I get JSON here
}
func TestGetXMLAlright(*testing.T) {
bk1 := brokers.New("/api2")
// test if I get XML here
}
This, in my opinion, is a powerful pattern since you can write more "generic" handlers and just plug them in to the routes you want.

How to access flags outside of main package?

We parse flags in main.go which is in main package, of course. Then we have another package where we want to read some flag's value.
flags.Args() work fine, it will return all non-flag values.
But I cannot figure out to how read already parsed value for a flag in a package other than main.
Is it possible?
Thanks
Amer
I had the same requirement recently and I wanted a solution that avoided calling flag.Parse repeatedly in init functions.
Perusing the flag package I found Lookup(name string) which returns a Flag which has a Value. Every built in Value implements the flag.Getter interface. The call chain looks like this:
flag.Lookup("httplog").Value.(flag.Getter).Get().(bool)
If you mistype the flag name or use the wrong type you get a runtime error. I wrapped the lookup in a function that I call directly where needed since the lookup and get methods are fast and the function is not called often. So the main package declares the flag.
// main.go
package main
import "flag"
var httplog = flag.Bool("httplog", false, "Log every HTTP request and response.")
func main() {
flag.Parse()
// ...
}
And the utility package, which is decoupled from main except for the flag name, reads the flag value.
// httpdiag.go
package utility
import "flag"
func logging() bool {
return flag.Lookup("httplog").Value.(flag.Getter).Get().(bool)
}
You can define the var storing the flag in the separate package, as an exported variable, then call the flag parsing in the main package to use that variable, like this:
mypackage/const.go
var (
MyExportedVar string
)
mainpackage/main.go
func init() {
flag.StringVar(&mypackage.MyExportedVar, "flagName", "defaultValue", "usage")
flag.Parse()
}
This way, everybody can access that flag, including that package itself.
Note:
this only works for exported variables.
You can define the flag in that package and call flag.Parse() in func init(), flag.Parse can be called multiple times.
However, if you want to access the flag's value from multiple packages you have to expose it or create an exposed function to check it.
for example:
// pkgA
var A = flag.Bool("a", false, "why a?")
func init() {
flag.Parse()
}
// main package
func main() {
flag.Parse()
if *pkgA.A {
// stuff
}
}
Also you can use FlagSet.Parse(os.Args) if you want to reparse the args.
When you parse your flags, parse them into global variables which start with an initial Capital so they are public, eg
package main
var Species = flag.String("species", "gopher", "the species we are studying")
func main() {
flag.Parse()
}
Then in your other package you can refer to them as
package other
import "/path/to/package/main"
func Whatever() {
fmt.Println(main.Species)
}

How to persist "package" states in Go?

My goal is to encapsulate in one module/package.
Main package:
package main
import (
"github.com/zenazn/goji"
"./routes"
)
func main(){
routes.Setup()
goji.Serve()
}
And another package:
package routes
import "github.com/zenazn/goji"
func Setup() {
goji.Get("/static", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "static!")
})
}
How can I do this?
goji, in your example, is a package. Not a variable.
You cannot pass packages around like this.
If you look at the example on the goji github page
You simply just call goji.Get from your Init function, and goji.Serve from your main
route.go
package route
import "route"
import "github.com/zenazn/goji"
func Init(){
goji.Get("/hello/:name", hello)
}
main.go
package main
import "github.com/zenazn/goji"
func main(){
route.Init()
goji.Serve()
}
Packages in go export constants, variables, types and functions that have uppercase letters as their name. The package itself is not something directly manipulatable by the program.
The package goji should be exporting a variable named something like goji.Goji if you want to directly access it from other packages. A better solution is to provide some functions in the package that allow you to register your functions/helpers.
You could also export a function from goji like:
func Set(s string, func(w http.ResponseWriter, r *http.Request)) { ... }
that could be used by other packages:
goji.Set("/static", myFunc)
The error you had "use of package goji without selector" is saying you can't use the name of the package without specifying which exported value you want from the package. It's expecting goji.something not goji by itself.
The function init() inside go files has special properties: see http://golang.org/ref/spec#Program_initialization_and_execution

Resources