Type error when importing vendored package - go

I am currently trying to export all shared components from multiple services to one new package and then reimporting this package to the services.
I quickly came accross the following problem:
In my utility package I create a function:
package utils
import "github.com/rs/zerolog"
func CreateLogger(logLevel string, w io.Writer) Logger {
...
return zerolog.New(w).With().Timestamp().Logger()
}
So now this package references "github.com/rs/zerolog"
Now in my services I want to use this method like this:
import "github.com/rs/zerolog"
var (
logger zerolog.Logger
)
func New() {
logger = utils.CreateLogger("info", os.Stdout)
}
Now this also references "github.com/rs/zerolog", but throws the error:
cannot use utils.CreateLogger("info", os.Stdout) (type "MY_REPO_PATH/vendor/VENDORED_REPO_PATH/vendor/github.com/rs/zerolog".Logger) as type "MY_REPO_PATH/vendor/github.com/rs/zerolog".Logger in assignment
So I understand why this is happening and I found a solution by wrapping the zerologger in my utils into a struct like this:
type Logger struct {
Logger zerolog.Logger
}
Now this works, but it is inconvenient because I have to call logger.Logger.Info()... instead of logger.Info() in my code.
It looks to me like this is not the perfect solution to this problem and I would like to know if there is a better way to solve it.
Best Regards,
Jonathan

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.

Doesn't see public functions of a struct Golang

I don't see the public method of the struct that I defined.
Can someone please let me understand why?
Here is the code:
// DataSaver.go:
package DataStorage
import (
"fmt"
"os"
)
type DataSaver struct {
// doesn't relevant to my question
fileName string
file *os.File
}
func PrintStr() {
fmt.Println("hello")
}
Then, I have a main method in other class. I initialized the struct and I wanted to call to PrintStr() function. However, I can't call to this method. Why?
Thank you!
That is not a method, it is just a function.
Import DataStorage (you should make it lower case) then you can call DataStorage.PrintStr()
I had a play around myself with this, and there are a few things to be careful of:
Make sure you import the package properly with respect to your GOPATH, e.g.
import "github.com/some-repo/datastorage"
And ensure that your packages/repos are in your GOPATH and in the correct directories (go is very fussy about how you do this)
Refer to your function using the package name like so:
func main(){
DataStorage.PrintStr()
}
The other poster is correct in that go conventions are to keep package names in lowercase.
Seems you dont have the concept of method receiver yet. Your method for the struct should be defined as below :
func (this DataSaver)PrintStr() {
fmt.Println("hello")
}
or
func (this *DataSaver)PrintStr() {
fmt.Println("hello")
}
which one you choose depends on you want to call the method on a pointer or not;

Implementing interface from different package golang

I'm having some issues trying to implement an interface, defined in a different package in golang. I have made a minimal recreation of the problem below
Interface:
package interfaces
type Interface interface {
do(param int) int
}
Implementation:
package implementations
type Implementation struct{}
func (implementation *Implementation) do(param int) int {
return param
}
Main.go:
package main
import (
"test/implementing-interface-in-different-package/implementations"
"test/implementing-interface-in-different-package/interfaces"
)
func main() {
var interfaceImpl interfaces.Interface
interfaceImpl = &implementations.Implementation{}
}
Error message:
test/implementing-interface-in-different-package
./main.go:10:16: cannot use implementations.Implementation literal (type
implementations.Implementation) as type interfaces.Interface in assignment:
implementations.Implementation does not implement interfaces.Interface (missing interfaces.do method)
have implementations.do(int) int
want interfaces.do(int) int
Is it possible to implement an interface from a different package?
Thanks!
The problem is that your do function is not exported from the implementations package because it starts with a lower-case letter. Thus from the point of view of the main package, the variable interfaceImpl does not implement the interface because it cannot see the do function.
Rename your interface function to upper-case Do to resolve the problem.

Share object from in local package

I'm new to Go and I've created an application which is working as expected.
My application structure is like following:
myproj
Gopkg.toml
Gopkg.lock
src
server
main.go
utils
file1.go
logger.go
handler
handler1.go
handler2.go
Now inside the main.go file I've created a logger like the following:
File server-> main.go
import (
"handler"
"utils"
"github.com/sirupsen/logrus"
)
var logger *logrus.Logger
fun init(){
logger = utils.InitLogs()
}
func main(){
logger.info("my message")
…
handler.run()
}
Everything is working as expected!
Now I want to use the logger inside the handler1&2 files (from diff package inside my local project)
To do this, I did the following steps.
Inside the handler init the logger (exactly as I did in the main file) and it’s working
File handler-> handler1.go
import (
"utils"
"github.com/sirupsen/logrus"
)
var logger *logrus.Logger
fun init(){
//Init the logger again
logger = utils.InitLogs()
}
func run(){
//here Im not using logger otherwise maybe I can move it as parameter…
}
func build(){
//Here Im using the logger
logger.info("Hi")
}
While this is working but I've created two instance of logger, first on main and second on handler1 which Im not sure is best
My questions are:
I want to reuse the logger inside the handler class/module, is a better way to do it in Go?
This is diff package (inside my local proj) and I'm not sure that I'm that if Im not re-use the same logger object from the main method, this is the right way to do it...
(lower prio question) Is my project structure okay for Go? I used this as reference which is similar...also this which is a bit different
If you want to share your logger instance across your project, one way is to just export it as a global variable from your utils package as Armin suggested in his answer:
package utils
...
var Logger *logrus.Logger
func init() {
Logger = InitLogs()
}
I'd bet you can then get away with not having to export InitLogs() by changing it to initLogs()
Then, elsewhere in your code, you can import utils and use that logger instance:
import "utils"
...
func something() {
utils.Logger.Info("Hi")
}
Alternatively, if it makes sense to keep all your configuration in one place, you can declare your logger pointer as a field of a config struct and initialize it along with the rest of your program configuration (if you have any).
For example, say you have the following in your utils package:
package utils
...
type MyAppConfig struct {
// whatever config parameters your app needs,
// like DB connections, etc.
Logger *logrus.Logger
}
// pass in whatever configuration parameters you need,
// like DB URL, etc.
func InitConfig() (*MyAppConfig, error) {
// set up other configuration
config := &MyAppConfig{
// other config
Logger: InitLogs(),
}
return config, nil
}
func (c *MyAppConfig) DoSomethingImportant() {
c.Logger.Info("Hello")
}
Meanwhile, you can use this anywhere else, like in your CLI interface:
package main
import "utils"
...
func main() {
// input, or CLI parameters...
// pass in other CLI parameters, if any
// (of course you'd have to change the function signature in
// the previous file above)
config, err := utils.NewConfig()
if err != nil {
// handle error
}
config.DoSomethingImportant()
// or since Logger is exported:
config.Logger.Info("hello")
}
If you find you end up with lots of global things like the Logger you need to configure, the config struct is the more scalable approach IMO. As an added bonus, using a config type makes it easier to do dependency injection in unit tests than having the global variables used directly.
On the other hand, if you only have to worry about the one global Logger, then the config struct might be overkill, especially if it's not something you'll need to instrument when designing tests. It's a subjective call depending on your circumstances.
To answer your second question...
Project Layout
If /server has a main package with a command, move it under cmd/server.
Also, the way you currently have it, it would be a bit confusing for others to depend on your project. Since your repository appears to begin at src/, here's what it will look like when someone else tries to import your utils package:
File structure:
<top of someone else's GOPATH>/
src/
myproject/src/utils/
...
To import:
import "myproject/src/utils"
... and actually, since you import "utils" in your code right now, I don't think your code will compile in someone else's GOPATH because they don't have a $GOPATH/src/utils.
Solution: Your repository should not include src/. The idea is that you setup your $GOPATH and you put different repositories/packages inside of it. For example (assuming you host your code on Github):
<top of gopath>/
src/
github.com/you/myproject/ <---- your repo starts here
cmd/server/
main.go
utils
file1.go
logger.go
handler
handler1.go
handler2.go
This would make your packages importable to you and to others like so:
import "github.com/you/myproject/utils"
Also, this project structure allow others to include your project inside their $GOPATH or in their vendor/ interchangeably.
It seems you want to use the exact logger from main.go. You should just export it and use it in other packages:
server/main.go
var Logger *logrus.Logger // Notice capital L
func main() {
Logger = utils.InitLogs() // Init logger once
...
}
handler/handler1.go
import "server"
func run(){
//here Im not using logger otherwise maybe I can move it as parameter…
}
func build(){
// No need to initialize logger as its done in main func.
//Here Im using the logger
server.Logger.info("in xyz")
}
And your project structure is not standard. See this.

Package imports, cannot use struct from imported package

This is probably a noob mistake but I cannot figure this out.
In main.go I am importing a package.
import(
"models/users"
)
// ...
func main() {
r.HandleFunc("/users/list", UsersModel.List())
The package is stored in src/models/users
The users package looks like this:
package users
import (
"gopkg.in/mgo.v2"
)
// ...
/**
* User Model
*/
type UsersModel struct {
}
// Add methods to the UsersModel type.
func (m *UsersModel) List() {
// ...
When I run the code I get the following error.
src\main.go:9: imported and not used: "models/users" src\main.go:20:
undefined: UsersModel
The go code appears to be valid as if I invalidate it throws an error. How do I export the UsersModel type from the users package?
You need to prefix your use of UsersModel with the package name users, like so:
um := users.UserModel{}
UsersModel.List() is also wrong: go doesn't have "static methods" or "class methods".

Resources