Golang fasthttp router custom logger - go

I'm playing with fasthttp and it's router, I have no issues with basic things, I have a working server and a router, that is the easy part.
The issue is with the logger, I would like to be able to customize that one, but it does not seem possible with the ctx.Logger() as it only takes a Printf argument and the format is not what I'm looking for.
Does anyone knows where in the documentation I can find a working example of what I want to do?
Example of code I currently have:
package server
import (
"github.com/fasthttp/router"
"github.com/valyala/fasthttp"
)
// Router will manage the routes of our API server
func Router() *router.Router {
r := router.New()
r.GET("/", index)
return r
}
func index(ctx *fasthttp.RequestCtx) {
ctx.Logger().Printf("/")
ctx.WriteString("Welcome!")
}
As I'm still trying my hand with the web servers and I still don't understand some things with it and Go also. So An example would be welcome.
For example I would like to be able to do something like that using a logger define in the main package:
package server
import (
"github.com/fasthttp/router"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
)
// Router will manage the routes of our API server
func Router(loger *zap.Logger) *router.Router {
r := router.New()
r.GET("/", index)
return r
}
func index(ctx *fasthttp.RequestCtx) {
ctx.Logger().Printf("/") // Here should print in the zap format of my choice.
ctx.WriteString("Welcome!")
}

If you look at the source code, it's apparent that all you have is the ability to write standard Go-formatted strings:
func (cl *ctxLogger) Printf(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
ctxLoggerLock.Lock()
cl.logger.Printf("%.3f %s - %s", time.Since(cl.ctx.ConnTime()).Seconds(), cl.ctx.String(), msg)
ctxLoggerLock.Unlock()
}
The logger simply adds some additional information from the context. So further cutomisation beyond the standard Go formatting does not seem possible. I'm not sure what "zap format of my choice" is, so I can't say if there's a workaround or even if standard Go formatting options will serve for you here.

Related

How to Mock inner methods in GoLang

e.g
type test struct { // few fields}
func (t *test) createresource(res1 string,res2 string)error {
//doing some task
t.createsubresource(res1)
}
func (t *test)createsubresource(res1 string)error{
//perform some task
}
I want to write test function for createresource , how can I mock t.createsubresource(res1) call. This is legacy code and I don't have permission to modify any above function.
Your mock can be done using interfaces, as for example:
main.go
package main
type TestInterface interface {
CreateResource(res1 string, res2 string) error
CreateSubresource (res1 string) error
}
func main() {
DoSomething(new(Test))
}
func DoSomething(t TestInterface) {
t.CreateResource()
}
main_test.go
package main
import "testing"
type TestMock struct {}
func (tm *TestMock) CreateResource(res1 string, res2 string) error {
return nil
}
func (tm *TestMock) CreateSubresource(res1 string) error {
return nil
}
func TestDoSomething(t *testing.T) {
err := DoSomething(new(TestMock))
//... do your assertions
}
Why does it works like that?
Calling a function that depends on a specific structure does not allow you to inject alternatives to it, that's why a solution using interface needs to be created. By having an interface, just implement a new structure that matches that interface and pass it as a dependency injection to the procedure that will be tested.
Also, check this out:
There is no easy way, by default, to just point your original structure and tell Go to make a mock from it. Maybe some 3rd party lib can do it (but I didn't saw that yet).
In go, public and private declarations are defined by the first letter as uppercase. By the lower cases declarations in your sample I've noticed that everything is private.
Usually it is not a good practice to test private methods. There are a lot of discussions about this topic, you can take a look in this one here
There are also some support libs to make assertions and mocks like for example stretchr/testify, please make a research first.
I hope that it helps you.

Create Routing Modules Go/Echo RestAPI

I just started learning Go and want to create my own REST API.
The problem is simple:
I want to have the routes of my api in a different file for example: routes/users.go that then I include in the "main" function and register those routes.
There are a high number of examples of restAPI's in Echo/Go but all of them have the routes in the main() function.
I checked a few examples/github starter kits but it seems that I cannot find a solution that I like.
func main() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
responseJSON := &JSResp{Msg: "Hello World!"}
return c.JSON(http.StatusOK, responseJSON)
})
//I want to get rid of this
e.GET("users", UserController.CreateUser)
e.POST("users", UserController.UpdateUser)
e.DELETE("users", UserController.DeleteUser)
//would like something like
// UserRoutes.initRoutes(e)
e.Logger.Fatal(e.Start(":1323"))
}
//UserController.go
//CreateUser
func CreateUser(c echo.Context) error {
responseJSON := &JSResp{Msg: "Create User!"}
return c.JSON(http.StatusOK, responseJSON)
}
//UserRoutes.go
func initRoutes(e) { //this is probably e* echo or something like that
//UserController is a package in this case that exports the CreateUser function
e.GET("users", UserController.CreateUser)
return e;
}
Is there an easy way to make this? Coming from node.js and still having some syntax errors of course, will solve them, but I am struggling with the architecture of my code at the moment.
I want to have the routes of my api in a different file for example:
routes/users.go that then I include in the "main" function and
register those routes.
This is possible, simply have your files in the routes package declare functions that take an instance of *echo.Echo and have them register the handlers.
// routes/users.go
func InitUserRoutes(e *echo.Echo) {
e.GET("users", UserController.CreateUser)
e.POST("users", UserController.UpdateUser)
e.DELETE("users", UserController.DeleteUser)
}
// routes/posts.go
func InitPostRoutes(e *echo.Echo) {
e.GET("posts", PostController.CreatePost)
e.POST("posts", PostController.UpdatePost)
e.DELETE("posts", PostController.DeletePost)
}
and then in main.go
import (
"github.com/whatever/echo"
"package/path/to/routes"
)
func main() {
e := echo.New()
routes.InitUserRoutes(e)
routes.InitPostRoutes(e)
// ...
}
Note that the InitXxx functions need to start with an upper case letter as opposed to your initRoutes example which has its first letter in lower case. This is because identifiers with lower case first letters are unexported, which makes them inaccessible from outside their own package. Put another way, for you to be able to reference an imported identifier you have to export it by having it start with an upper case letter.
More here: https://golang.org/ref/spec#Exported_identifiers

how to create a context object in google cloud run for firebase

I'm trying to create a context object, so that I can connect to firestore from cloud run, however, all the examples I find on the net basically say I need a context objects, examples I find online usually look like this:
ctx := context.Background()
client, err := firestore.NewClient(ctx, "projectID")
if err != nil {
fail(w, err.Error())
// TODO: Handle error.
}
You can find these examples in places like this one:
https://godoc.org/cloud.google.com/go/firestore#example-NewClient
Nowhere in this example is there an explanation of where to find the context object.
so I just get this error:
undefined: context
I think the documentation is just too confusing.
You should use r.Context() of http.Request object in the request handler instead of initializing a new detached context like context.Background().
The main purpose of contexts is to propagate them, and in Cloud Run you always process requests, so if you pass the request’s context, it’s the right thing to do.
I think in your case, “context” package is not imported. Make sure to use go 1.11+ in your Dockerfile and say:
import “context”
In Go, you need to import packages. For this statement ctx := context.Background() add to the top of your source file import "context" or merge with your existing import set.
Like most languages, the more experience you have the more the language makes sense and you just know what to do. Most languages are the same. In C/C++ you have the include statement, C# the using statement, in Python the import statement, etc.
Google has a large package of examples for using Go and Google Cloud Platform:
Google Cloud Examples in Go
I wrote an article that documents my 30-day journey to learn Go and Google Cloud Platform.
Google Cloud and Go – My Journey to Learn a new Language in 30 days
Given the plethora of 3 lines of code examples that are hard for beginners, and the lack of complete working examples online, like myself, here is a full working example, which is kinda what I needed when I started this task, I hope this helps anybody in the future.
package main
import (
"cloud.google.com/go/firestore" // https://godoc.org/cloud.google.com/go/firestore"
"context" // https://blog.golang.org/context
firebase "firebase.google.com/go"
"fmt"
"log"
"net/http"
"os"
)
func fail(w http.ResponseWriter, msg string) {
fmt.Fprintln(w, "fail:"+msg)
log.Println("fail:" + msg)
}
// State example code
type State struct {
Capital string `firestore:"capital"`
Population float64 `firestore:"pop"` // in millions
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
override := make(map[string]interface{})
ctx := context.Background()
client, err := firestore.NewClient(ctx, "YOURPID")// set GOOGLE_APPLICATION_CREDENTIALS env var
if err != nil {
fail(w, err.Error())
return
}
states := client.Collection("States")
ny := states.Doc("NewYork")
wr, err := ny.Create(ctx, State{
Capital: "Albany",
Population: 19.8,
})
fmt.Println(wr)
})
log.Fatal(http.ListenAndServe("0.0.0.0:8082", nil))
}

how to implement fasthttp framework

I want to start learning about the fasthttps server from this link https://github.com/valyala/fasthttp but I dont know that how will I implement a small piece of code in this framework. Can anybody tell me that how i will implement a small piece of code in this? example please.
Code I tried
package main
import "fmt"
type MyHandler struct {
foobar string
}
func main() {
// pass bound struct method to fasthttp
myHandler := &MyHandler{
foobar: "foobar",
}
fasthttp.ListenAndServe(":8080", myHandler.HandleFastHTTP)
// pass plain function to fasthttp
fasthttp.ListenAndServe(":8081", fastHTTPHandler)
}
// request handler in net/http style, i.e. method bound to MyHandler struct.
func (h *MyHandler) HandleFastHTTP(ctx *fasthttp.RequestCtx) {
// notice that we may access MyHandler properties here - see h.foobar.
fmt.Fprintf(ctx, "Hello, world! Requested path is %q. Foobar is %q",
ctx.Path(), h.foobar)
}
// request handler in fasthttp style, i.e. just plain function.
func fastHTTPHandler(ctx *fasthttp.RequestCtx) {
fmt.Fprintf(ctx, "Hi there! RequestURI is %q", ctx.RequestURI())
}
Can you please tell me that how I will implement this code.
This code seems to be working. I pasted it to a .go file, added:
import "github.com/valyala/fasthttp"
Then you have to install this package, either by using go get github.com/valyala/fasthttp or by writing a go.mod file if you want to use the new module support.
Then run this file and open localhost:8080 in a browser.
Maybe you have a more concrete question?
As #Volker said in a comment, for newbies it's highly recommended to stick to the standard library - net/http in this case; there are way more examples and code/tutorials you can find by googling, no need to install special packages, etc.

Golang logrus - how to do a centralized configuration?

I am using logrus in a Go app. I believe this question is applicable to any other logging package (which doesn't offer external file based configuration) as well.
logrus provides functions to setup various configuration, e.g. SetOutput, SetLevel etc.
Like any other application I need to do logging from multiple source files/packages, it seems you need to setup these options in each file with logrus.
Is there any way to setup these options once somewhere in a central place to be shared all over the application. That way if I have to make logging level change I can do it in one place and applies to all the components of the app.
You don't need to set these options in each file with Logrus.
You can import Logrus as log:
import log "github.com/Sirupsen/logrus"
Then functions like log.SetOutput() are just functions and modify the global logger and apply to any file that includes this import.
You can create a package global log variable:
var log = logrus.New()
Then functions like log.SetOutput() are methods and modify your package global. This is awkward IMO if you have multiple packages in your program, because each of them has a different logger with different settings (but maybe that's good for some use cases). I also don't like this approach because it confuses goimports (which will want to insert log into your imports list).
Or you can create your own wrapper (which is what I do). I have my own log package with its own logger var:
var logger = logrus.New()
Then I make top-level functions to wrap Logrus:
func Info(args ...interface{}) {
logger.Info(args...)
}
func Debug(args ...interface{}) {
logger.Debug(args...)
}
This is slightly tedious, but allows me to add functions specific to my program:
func WithConn(conn net.Conn) *logrus.Entry {
var addr string = "unknown"
if conn != nil {
addr = conn.RemoteAddr().String()
}
return logger.WithField("addr", addr)
}
func WithRequest(req *http.Request) *logrus.Entry {
return logger.WithFields(RequestFields(req))
}
So I can then do things like:
log.WithConn(c).Info("Connected")
(I plan in the future to wrap logrus.Entry into my own type so that I can chain these better; currently I can't call log.WithConn(c).WithRequest(r).Error(...) because I can't add WithRequest() to logrus.Entry.)
This is the solution that I arrived at for my application that allows adding of fields to the logging context. It does have a small performance impact due to the copying of context base fields.
package logging
import (
log "github.com/Sirupsen/logrus"
)
func NewContextLogger(c log.Fields) func(f log.Fields) *log.Entry {
return func(f log.Fields) *log.Entry {
for k, v := range c {
f[k] = v
}
return log.WithFields(f)
}
}
package main
import (
"logging"
)
func main {
app.Logger = logging.NewContextLogger(log.Fields{
"module": "app",
"id": event.Id,
})
app.Logger(log.Fields{
"startTime": event.StartTime,
"endTime": event.EndTime,
"title": event.Name,
}).Info("Starting process")
}

Resources