Now i decided to use leveled logger in my Go programm. At this moment it's logrus.
But after some investigation (through pain) found that there are much loggers: glog, log15, logrus and so on. So, now i want try to use logrus and have
possibility to change it on another without fixing all my sources where log is used.
I tried to create my own package logger which only imports logrus:
package logger
import "github.com/Sirupsen/logrus"
func init() {
logrus.SetFormater(new(logrus.TextFormater))
}
In all other my sources a want to use i in such way:
// main.go
package main
import log "logger"
func main() {
log.Print(...)
log.Debug(...)
and so on
}
But compiler says me that there are no such methods in logger package.
Is there a way just import some package A to my own package B and use in my code all methods of A through B?
The reason what i need this is to have a chance quickly change logrus to ahother logger in future which support same methods just replacing several lines of code in my own logger file without replacing
import log "github.com/Sirupsen/logrus" on import log "github.com/some_bestlogger" over all sources
You can import your logger package simply for its side effects, and still use the logrus package directly:
package main
import (
log "github.com/Sirupsen/logrus"
_ "logger"
)
func main() {
log.Print()
log.Debug()
}
If you do want to be able to call those log function directly through your "logger" package, you will need to define the functions and delegate them to the "logrus" package internally.
to have a chance quickly change logrus to ahother logger in future which support same methods
You will never switch to anything else, as every logger has distinct method set. For example log15 doesn't have Print method, but Info. And completely different approach to logging fields (key-value pairs). You're worried about changing a single import line, but in fact you will need to change every single line which uses logger.
If you want to be able to easily switch between logging packages, you need to come up with own logging interface (or use one that fits you best) and use logging package of your choice behind the scenes. Only this approach gives you ability to minimise changes all around your code.
You could also provide an interface in your library and then provide a default logger which would use a particular implementation. Note that all logger packages I know provide some kind of Logger structure (e.g. logrus.Logger) which you could than attach to your interface. Then, instead of changing package name on import, you could use log = logger.New() across your application.
If you don't take these arguments and want to follow your path, from my perspective all you need is an alias. You can either fork one of the logging libraries and use own fork across your code (this would allow you to completely change the behaviour without affecting importers) or simply symlink your desired import path to, say, logrus (although this will not work with go get).
I think what you need is an interface. Here is a very simple example.
package main
import (
"os"
"github.com/Sirupsen/logrus"
)
func init() {
// Output to stdout instead of the default stderr
// Can be any io.Writer, see below for File example
logrus.SetOutput(os.Stdout)
}
type logInterface interface {
Print()
}
type MyLogger struct{}
func (ml MyLogger) Print() {
logrus.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
}
func main() {
mylogger := MyLogger{}
mylogger.Print()
}
In this way, you can replace logrus with any other logging package, and you just need to update the Print function and don't have to change the code where the Print function is called.
Related
I was looking into this logger. What this import exactly means?
The simplest way to use Logrus is simply the package-level exported logger:
package main
import (
log "github.com/sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
}
From the example it looks like that "built-in" log was replaces by the the above implementation. I couldn't locate any official/formal specs, probably I don't know how this feature is called.
This is simply a reference to the fact that there are functions exported directly at the package level. That is, they do not require any type or variable reference to access those exported symbols.
The example in the.. um.. example, is the WithFields() function.
This is exported at that "package level", so all you need in order to reference that function is the package name (or alias).
In this case, the logrus package has been aliased as log, so the WithFields() function it exports can be called simply using:
log.WithFields(/* etc */)
By contrast, the Info() function in the example illustrates a symbol (in this case a function) exported by another type in the logrus package; the Info() function here is called on a logrus.Entry reference (returned by the WithFields() function).
What's the Big Deal?
Package-Level exports are very common in go; a package that didn't export anything wouldn't be very useful to anybody! :)
I'm not sure that they are explicitly referenced as "package-level" exports in general; usually they would just be "exports" or "exported symbols" etc.
They are called out specifically in the case of logrus because the package authors have used this to create a package that is 'compatible' with the standard log package in GoLang (which also uses "package-level" exports).
i.e. if you alias logrus as log then logrus provides a superset of the standard log package; this is deliberately so that existing code that uses the log package can be converted to logrus very quickly by simply changing the import statement in files that make log calls.
I want to make global logger.
Can I make global logger? (To access every pacakges)
Packages can have global variables:
package somepackage
import "log"
var (
Log *log.Logger = log.New(os.Stderr, "", log.LstdFlags)
)
Notice that I capitalized Log. This means it is exported (think public in other languages).
In general though, using globals is discouraged. I would recommend thinking of a different pattern.
The project structure requires to define interface and implementations in separate files.
It's best to download a small isolated test case to get a picture, but here's the code too. It's a library project that's why mylib is declared as package for each file. I wanted to have subpackages but golang doesn't allow that, so instead I put everything under one package while files are in separate directories.
Download Test Project
src/interfaces/my_interface.go
package mylib
type MyInterface interface {
foo()
bar()
}
src/interfaces/my_implementation.go how to import interface here?
package mylib
type MyImplementation struct {}
// possible to declare *MyInteface here?
func (imp *MyImplementation) foo() {}
func (imp *MyImplementation) bar() {}
test/implementations/implementation_test.go
package implementations
import (
"testing"
"fmt"
"implementations"
mylib2 "interfaces" // why mylib2 here?
)
func TestImplementation(t *testing.T) {
// how to declare variable of type `MyInterface` and assign an object `MyImplementation` to it
interface_type_var := mylib2.MyInterface() // error
interface_type_var := mylib.MyImplementation{} // error
fmt.Println("Test successful")
}
Question
How can I declare a type of MyInterface and assign object of MyImplementation to it.
how to import and interface in the implementation file
Autocomplete of IDE put this under imports automatically as I was importing interface type. not sure why. mylib2 "interfaces". i learned it's an alias but why do we need alias here?
Help fix the code in the implementation_test please
You have already declared a type of MyInterface inside src/interfaces/my_interface.go. You don't have to redeclare it and you don't have to explicitly assign MyImplementation to the interface. All you have to make sure is that your MyImplementation implements all the methods from the interface. As long as both files are in the same package and same folder level, your implementation automatically becomes your interface.
As long they are both in the same package and folder you do not have to import interface inside implementation
At time of this writing Gogland is in Early Access Program which is just another name for BETA :). I don't think SO allows commenting on anything in beta. Sorry.
EDIT
Based on the structure of your code, you are taking bit wrong approach when trying to use subfolders. If you are using subfolders, those subfolders should be independent packages. So let's say your GOPATH is ~/go. You need a structure like this: ~/go/src/github.com/user/mylib. So if you want a subfolder/subpackage you would have something like this ~/go/src/github.com/user/mylib/util and util would be its own package. It's wrong to try to have Implementations in one folder and interfaces in another. Rather group them logically and create subpackages. Then in your test you can use them like this:
import(
"github.com/user/mylib"
"github.com/user/mylib/util"
)
github.com or any other repo service is important specially if you are writing library that is suppose to be reusable in any project.
Besides that, I'd suggest to use more stable IDE or editor.
Hope this helps
Can we have generic importing of modules with go. To be more clear, here is use case:
package main
import (
"fmt"
"net/http"
)
json handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "you requested", r.URL.Path)
}
func main() {
var moduleName String = "/path/to/module"
import moduleName
http.HandleFunc("/", handler)
http.ListenAndServe(":8000", nil)
}
So in main you can see that I am trying to import moduleName, but this gives me an error.
Is there some workaround for this?
Go is a statically compiled language, not an interpreted language like Python. Your imports happen in compile time, not in run time. So in short, no, you can only import stuff on the package level.
The official definition makes this clear:
An import declaration states that the source file containing the declaration depends on functionality of the imported package and enables access to exported identifiers of that package.
One more interesting note on imports is that if a package is imported and has an init() function, this function will be called to initialize the package, on program startup; from the docs:
If a package has imports, the imported packages are initialized before initializing the package itself
This leaves some room for dynamic intiailization, but it's far from dynamic imports.
Google Go doesn't natively support dynamic imports. More on this subject can be read here: How to import package by path from string in Go?
In a forum discussion a solution that is suggested is calling the compiler with your specific module. This is however not general practice. The discussion can be found here: https://groups.google.com/forum/#!topic/golang-nuts/Rm0gcBPbxLk
In a general sense I would advise against any such schemes. There are probably different ways to implement the program with the same functionality. If you can't find another scheme for Google Go, try searching for the same kind of schemes in C++, they are usually quite similar.
In this code from go-sqlite3:
import (
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
"log"
"os"
)
what does the underscore in the import statement mean?
It's for importing a package solely for its side-effects.
From the Go Specification:
To import a package solely for its side-effects (initialization), use the blank identifier as explicit package name:
import _ "lib/math"
In sqlite3
In the case of go-sqlite3, the underscore import is used for the side-effect of registering the sqlite3 driver as a database driver in the init() function, without importing any other functions:
sql.Register("sqlite3", &SQLiteDriver{})
Once it's registered in this way, sqlite3 can be used with the standard library's sql interface in your code like in the example:
db, err := sql.Open("sqlite3", "./foo.db")
While other answers described it completely, for "Show me The Code" people, this basically means: create package-level variables and execute the init function of that package.
And (if any) the hierarchy of package-level variables & init functions of packages that, this package has imported.
The only side effect that a package can make, without being actually called, is by creating package-level variables (public or private) and inside it's init function.
Note: There is a trick to run a function before even init function. We can use package-level variables for this by initializing them using that function.
func theVeryFirstFunction() int {
log.Println("theVeryFirstFunction")
return 6
}
var (
Num = theVeryFirstFunction()
)
func init() { log.Println("init", Num) }
https://golang.org/doc/effective_go.html#blank
It's either a work in progress, or imported for side effects. In this case, I believe it's for the side effects, as described in the doc.
Let's say you have an Animal package. And your main file wants to use that Animal package to call a method called Speak but there are many different types of animals and each animal implemented their own common Talk method. So let's say you want to call a method Speak implemented in the Animal's package which internally calls Talk method implemented in each of the animal's package. So in this case you just want to do an import _ "dog" which will actually call the init method defined inside the dog package which actually registers a Talk method with the Animal package which it too imports.
As I'm new in Go, this definition made it more clear:
Underscore is a special character in Go which acts as null container. Since we are importing a package but not using it, Go compiler will complain about it. To avoid that, we are storing reference of that package into _ and Go compiler will simply ignore it.
Aliasing a package with an underscore which seems to do nothing is quite useful sometimes when you want to initialize a package but not use it.
Link