Go
-
print
nlines of source code around the bug:go vet -c n
Golang package structure:
cmd- commandspkg- package codetestdata
Tips:
-
Put test code in different package.
This way you will experience how users use the code.
package proj_test func Test(t *testing.T) { g := proj.FormatGreeter("Hi %s") g.Greet("Piotr") } -
use godoc early:
godoc -http=:8080
- types can express state & behaviour.
- interfaces are behavioural only.
- not using
io.Reader,io.Writer - Methods vs Functions
go build -ldflags="-s -w" main.go
Resource: How to Shrink a Go Executable by 70% in 30 Seconds
-
Run specific tests:
go test -run=TestAddor
go test -run=TestAdd/sub-test-1 -
use environment variables to enable integration tests:
func TestIntegration(t *testing.T) { fooAddr := os.Getenv("FOO_ADDR") if fooAddr == "" { t.Skip("set FOO_ADDR to run this test") } f, err := foo.Connect(fooAddr) // ... }Read more here.
Read line by line:
gzReader, err := gzip.NewReader(out.Body)
if err != nil {
log.Fatalf("error while creating gzip reader: %s", err)
}
count := 0
scanner := bufio.NewScanner(gzReader)
for scanner.Scan() {
fmt.Println(len(scanner.Bytes()))
count = count + 1
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
log.Fatalf("read lines: %d", count)
Pipe all to stdout:
gzReader, err := gzip.NewReader(out.Body)
if err != nil {
log.Fatalf("error while creating gzip reader: %s", err)
}
if m, err := io.Copy(os.Stdout, gzReader); err != nil {
log.Printf("size: %d", m)
log.Fatalf("error while reading gzip reader: %s", err)
}
Read chunks:
gzReader, err := gzip.NewReader(out.Body)
if err != nil {
log.Fatalf("error while creating gzip reader: %s", err)
}
gzb := make([]byte, 1024)
for {
m, err := gzReader.Read(gzb)
if err != nil && err != io.EOF {
log.Fatalf("error while reading gzip: %s", err)
}
if m == 0 {
break
}
log.Printf("content: %s", string(gzb))
log.Printf("size: %d", m)
}
wget https://storage.googleapis.com/golang/go1.9.1.linux-amd64.tar.gz
sha256sum go1.9.1.linux-amd64.tar.gz | grep 07d81c6b6b4c2dcf1b5ef7c27aaebd3691cdb40548500941f92b221147c5d9c7
tar -xzvf go1.9.1.linux-amd64.tar.gz
export GOROOT=$PWD/go
export PATH=$PATH:$GOROOT/bin
or install globally:
mv ./go /usr/local/go
and add to ~/.zshrc:
# Include Go into PATH
export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin/
Show all environment variables used by go:
go env
Show specific environment variable used by go:
go env GOOS
- arguments with the same type:
func add(a int, b int) int {
return a + b
}
or
func add(a, b int) int {
return a + b
}
- return multiple values
func switch(a, b string) (string, string) {
return b, a
}
a, b := switch("hi", "there")
- “naked” return
- define multiple variables
var a, b, c int
- variable with initializer
var a int = 1
var a = 1
var a, b int = 1, 2
- defining blocks
var (
ToBe bool = false
MaxInt uint64 = 1<<64 -
)
- short variable declaration - available only in functions:
k := 3
-
basic types
boolstringint,int8,int16,int32,int64uint uint8,uint16,uint32,uint64,uintptrbyte- alias for uint8rune- alias forint32, represents a Unicode code pointfloat32,float64complex64,complex128
-
alias
type Age int
var a Age = 5
var b int = a
- type conversions
var i int = 42
var f float64 = float64(i)
- constant
const Pi = 3.14
- view variable type
fmt.Printf("v is of type %T\n", v)
- check interface type
things := []interface{}{"hi", 5, 3.8, "there", nil, "!"}
for _, t := range things {
tt := reflect.TypeOf(t)
if tt != nil {
fmt.Println("type: ", tt.Kind())
} else {
fmt.Println("type: nil")
}
}
- array vs slice
Slice is an dynamic array. Create slice with:
var s2 []int
or with (initialized with the zero values):
s2 := make([]int, 5)
Slices are arrays. If you append an item to an slice it changes it’s pointer to the resulting array.
create slice with length and capacity:
b := make([]int, 0, 5)
- append to slice
s = append(s, 1)
s = append(s, 2, 3, 4)
- copy slice
// Create slice with the same length
c := make([]string, len(s))
copy(c, s)
- create multidimensional slice
twoD := make([][]int, 3)
for i := 0; i < 3; i++ {
innerLen := i + 1
twoD[i] = make([]int, innerLen)
for j := 0; j < innerLen; j++ {
twoD[i][j] = i + j
}
}
- iterate slice
for i := 0; i < len(s1); i++ {
fmt.Println(i, s1[i])
}
Using range:
for i, x := range(s1) {
fmt.Println(i, x)
}
- for/while
Forever:
for {
...
}
While:
i := 0
for i < 10 {
i = i + 1
}
- Maps are key-value pairs
m := map[int]string{1: "one", 2: "two", 3:"three"}
Create empty map:
m = make(map[string]Vertex)
Check if map key exists
v, ok := m[4]
Remove map key:
delete(m, key)
- iterate map
for k, v := range m {
fmt.Println(k, ":", v)
}
- if with a short statement
if v := 1; v < 10 {
fmt.Println("if is truthy")
}
- switch
Syntax:
switch i {
case 0:
...
case f():
...
default:
...
}
Example
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("MacOS")
default:
fmt.Println("other os:", os)
}
switch with no condition:
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
- interface type switch
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("is int")
case string:
fmt.Printf("is string")
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
do(123)
- create multidimensional slice
var (
image [][]uint8
x int
y int
)
image = make([][]uint8, dy)
y = dy - 1
for y >= 0 {
x = dx - 1
image[y] = make([]uint8, dx)
for x >= 0 {
image[y][x] = uint8(x * y)
x = x - 1
}
y = y - 1
}
- function values
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
- defer
Defer is working as a stack: FILO (First-in Last-out). So the first defer instruction will be executed the last.
- rescue
func rescue() {
if recover() != nil {
fmt.Println("Panic has been recover")
}
}
func main() {
// At the end of main, call rescue function
defer rescue()
fmt.Println("printed")
panic(1)
fmt.Println("never reached")
}
- readers
read everything from a Reader:
ioutil.ReadAll
transform string to a reader:
r := strings.NewReader(stringVariable)
copy all bytes from an io.Reader, and write them to an io.Writer:
n, err := io.Copy(writer, reader)
- type assertion
var i interface{} = "hello"
s, ok := i.(string)
f, ok := i.(float64) // ok == false
- Variadic Functions
func sum(nums ...int) {
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}
nums := []int{1, 2, 3, 4}
sum(nums...)
- Channel Directions
func ping(pings chan<- string, msg string) {
pings <- msg
}
func pong(pings <-chan string, pongs chan<- string) {
msg := <-pings
pongs <- msg
}
func main() {
pings := make(chan string, 1)
pongs := make(chan string, 1)
ping(pings, "passed message")
pong(pings, pongs)
fmt.Println(<-pongs)
}
- select timeout pattern
c1 := make(chan string, 1)
go func() {
time.Sleep(time.Second * 2)
c1 <- "result 1"
}()
select {
case res := <-c1:
fmt.Println(res)
case <-time.After(time.Second * 1):
fmt.Println("timeout 1")
}
- non-blocking select
select {
case msg := <-messages:
fmt.Println("received message", msg)
default:
fmt.Println("no message received")
}
-
b := new(bytes.Buffer)? -
Channels
Unbuffered
| Open | Closed | Nil | |
|---|---|---|---|
| Send | Blocks until the receiver is ready | Panics | Blocks |
| Receivce | Blocks until there’s data available | Returns zero value | Blocks |
| Close | Closes channel | Panics | Panics |
Buffered
| Open | Closed | Nil | |
|---|---|---|---|
| Send | Blocks if the channel is full | Panics | Blocks |
| Receivce | Blocks until there’s data available | Returns zero value | Blocks |
| Close | Closes channel | Panics | Panics |
Senders have the ability to close the channel to notify receivers that no more data will be sent on the channel.
Receiving on a closed channel will never block - a closed channel will never block
- example
ports := makev(chan int, 100)
You created a channel by using make(). A second parameter, an int value of 100, is provided to make() here. This allows the channel to be buffered, which means you can send it an item without waiting for a receiver to read the item. Buffered channels are ideal for maintaining and tracking work for multiple producers and consumers. You’ve capped the channel at 100, meaning it can hold 100 items before the sender will block.
- detect a closed channel
v, ok := <-ch
if ok == false {
fmt.Println("channel is closed")
}
- You should always close the channel on the sender side, not the receiver side. (Except delegating quit)
Only the sender knows when the channel was closed
-
Passing pointers to channels can create race conditions
-
make
make() can only be used to initialize slices, maps, and channels.
Unlike the new() function, make() does not return a pointer.
- new
The new(T) function allocates “zeroed” storage for a new item of type T and returns its address, a value of type *T. In Go terminology, it returns a pointer to a newly allocated zero value of type T.
All 3 lines do the same:
var buf bytes.Buffer; p := &buf
p := &bytes.Buffer{}
p := new(bytes.Buffer)
// Define regexp
rthumb, err := regexp.Compile(`(?i)^.*\.(thm|lrv)$`)
if err != nil {
log.Printf("invalid regular expression %+v", err)
os.Exit(1)
}
if rthumb.MatchString(file.Name()) {
fmt.Println()
}
Run tests in all sub-packages
go test ./...
Detect racing conditions:
go test -race ./...
func TestSometing(t *testing.T) {
t.Parallel()
...
}
Detect racing conditions and masure the code coverage:
go test -cover -race ./...
Table-driven tests.
Fail test:
- soft fail without a message:
t.Fail()
- soft fail with message:
// Informs that the test breaks here
t.Errorf("got bar = %v, want %v", got, want)
- fail the test and stop running (hard fail):
// Breaks the whole test run from here
t.Fatalf("Frobnicate(%v) returned error: %v", arg, err)
Log stuff while running go test -v:
t.Logf("iteration %v", i)
Enable parallel tests:
t.Parallel()
Skip a test:
if runtime.GOARCH == "arm" {
t.Skip("this doesn't work on ARM")
}
Run a specific test/method (TestCoala):
go test -run Coala
Run subtests:
func TestInts(t *testing.T) {
tt := []struct {
name string
values []int
sum int
}{
{"simple sum", []int{1, 2, 3, 4}, 10},
{"no elements", []int{}, 0},
{"negative elements", []int{1, -1}, 0},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
if sum := Ints(tc.values...); sum != tc.sum {
t.Fatalf("Expected sum: %v, got: %v", tc.sum, sum)
}
})
}
}
go test -v -run Ints/no_elem
Run tests for all files (wildcard):
go test github.com/user/...
Coverage details:
- generate coverage profile file:
go test -coverprofile=cover.out
Test HTTP server:
func TestHandler(t *testing.T) {
req, err := http.NewRequest(
http.MethodGet,
"http://localhost:8080/test",
nil, // body
)
if err != nil {
t.Fatalf("could not create request %v", err)
}
rec := httptest.NewRecorder()
handler(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("Expected status 200; got %d", rec.Code)
}
}
- show coverage statistics in terminal:
go tool cover -func=cover.out
- open browser and show coverage statistics:
go tool cover -html=cover.out
- generate coverage statistics as HTML file:
go tool cover -html=cover.out -o coverage.html
// To print the annotated error without the stack trace:
fmt.Printf("%v", err)
getCount failed: invalid key
// To print the stack trace as well, use %+v formatting flag
fmt.Printf("%+v", err)
Panic Reports
func reportPanics() {
// Capture the panic if there is one, and report it.
if panic := recover(); panic != nil {
postToSlack(panic)
}
}
go func() {
// Defer reportPanics before calling myFunc --
// if myFunc panics, reportPanic will capture and report the panic right before
// the goroutine exits.
defer reportPanics()
myFunc()
}()
NOTE The method can’t report panics for goroutines created by third-party libraries that your application uses
As HTTP middleware:
func PanicReporterMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
defer reportPanics()
next(w, r)
}
NOTE Add
PanicReporterMiddlewareas the first middleware
Limit the amount of POST data we read:
body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576))
Add middleware:
func wrapHandler(handler http.HandlerFunc) http.HandlerFunc {
h := func(w http.ResponseWriter, r *http.Request) {
if !userIsAuthorized(r) {
w.WriteHeader(http.StatusUnauthorized)
return
}
handler(w, r)
}
return h
}
...
r := mux.NewRouter()
r.HandleFunc("/user/me", wrapHandler(userHandler)).Methods("GET")
Get request header:
func handler(w http.ResponseWriter, r *http.Request) {
userID := r.Header.Get("X-HashText-User-ID")
}
Set response code:
func handler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.WriteHeader(http.StatusNotFound)
w.WriteHeader(http.StatusInternalServerError)
w.WriteHeader(http.StatusUnauthorized)
w.WriteHeader(http.StatusBadRequest)
// ...
}
Set response header:
func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
}
Common log levels:
- debug
- trace
- info
- warning
- error
- fatal
- panic
Create a custom logger:
var Info *log.Logger
Info = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.LUTC|log.Llongfile)
// Or without predefining the variable
Warning := log.New(ioutil.Discard, "WARNING: ", log.Ldate|log.Ltime|log.LUTC|log.Llongfile)
Error := log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.LUTC|log.Llongfile)
Log to a file:
// Create file with desired read/write permissions
file, err := os.OpenFile("logs/production.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Fatalln("Failed to open log file", file, ":", err)
}
defer file.Close()
// Create a new logger
Info := log.New(file, "INFO: ", log.Ldate|log.Ltime|log.LUTC|log.Llongfile)
// or use the standard one
log.SetOutput(file)
Remove standard logger prefix:
log.SetFlags(0)
Disable standard logger:
log.SetOutput(ioutil.Discard)
Set standard logger prefix:
log.SetPrefix("CUSTOM PREFIX")
Output logs in JSON format:
import (
log "github.com/Sirupsen/logrus"
)
func init() {
// Log as JSON instead of the default ASCII formatter.
log.SetFormatter(&log.JSONFormatter{})
// Output to stdout instead of the default stderr
// Can be any io.Writer, see below for File example
log.SetOutput(os.Stdout)
// Only log the warning severity or above.
log.SetLevel(log.WarnLevel)
}
func main() {
logger := log.WithFields(log.Fields{
"hostname": "staging-1",
"appname": "application-name",
"session": session_id,
"request_id": request_id,
"user_ip": user_ip,
})
logger.Info("Log this")
}
Outputs:
{"level":"info","msg":"Redirecting user","server":"www.google.com","time":"2017-03-25T17:00:00-08:00","userId":1, ...}
Tips:
- don’t create loggers per Gorountine
- logging can negatively influence performance
Handle Ctrl+C:
signals := make(chan os.Signal)
signal.Notify(signals, syscall.SIGINT)
go func() {
for range signals {
log.Println("Stopping...")
// ,,,
return
}
}()
or
signals := make(chan os.Signal)
signal.Notify(signals, syscall.SIGINT)
go func() {
<-signals
log.Println("Stopping...")
}()
Set defaults:
viper.SetDefault("port", 8080)
Read configuration file (with environment variables support):
import (
"fmt"
"os"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func init() {
// Setup viper when any command is executed
cobra.OnInitialize(onInitialize)
// Set some flags
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is $HOME/.cobra.yaml)")
rootCmd.PersistentFlags().StringVarP(&projectBase, "projectbase", "b", "", "base project directory eg. github.com/spf13/")
rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "Name of license for the project (can provide `licensetext` in config)")
// ...
viper.BindPFlag("projectbase", rootCmd.PersistentFlags().Lookup("projectbase"))
viper.BindPFlag("license", rootCmd.PersistentFlags().Lookup("license"))
viper.SetDefault("license", "apache")
// Add environment variables support
viper.SetEnvPrefix("czerasz")
viper.AutomaticEnv()
}
func onInitialize() {
// Read config either from cfgFile or from home directory
if cfgFile != "" {
// Use configuration file from the flag
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Search config in home directory with name ".cobra" (without extension).
viper.AddConfigPath(home)
viper.SetConfigName(".czerasz")
}
if err := viper.ReadInConfig(); err != nil {
fmt.Println("Can't read config:", err)
os.Exit(1)
}
}
Read configuration file with multiple environments:
prod := viper.Sub("production")
// Unmarshal into struct.
// Struct fields should match keys from config (case in-sensitive)
type config struct {
Host string
Port int
enabled bool
}
var C config
if err := prod.Unmarshal(&C); err != nil {
log.Fatalf("unable to decode into struct, %v", err)
}
fmt.Printf("Host: %s\n", C.Host)
Check if key is set:
if !viper.IsSet("production.port") {
log.Fatal("missing port number")
}
Add environment variables support for nested structures:
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
func main() {
pprof.StartCPUProfile(os.Stdout)
defer pprof.StopCPUProfile()
...
}
- build the binary:
go build -o programm-name
- generate a statistics file:
./programm-name > cpu.pprof
Or to measure the execution time:
time ./programm-name > cpu.pprof
- open the profile file:
go tool pprof cpu.pprof
- add
traceto your program:
func main() {
trace.Start(os.Stdout)
defer trace.Stop()
...
}
- build the binary:
go build -o programm-name
- generate a statistics file:
./programm-name > analisis.trace
Or to measure the execution time:
time ./programm-name > analisis.trace
- open the
analisis.tracefile:
go tool trace analisis.trace
Install Delve debugger (works with Atoms go-debug plugin):
go get -u github.com/derekparker/delve/cmd/dlv
Build and debug:
dlv debug
Build test binary and debug:
dlv test
Connect to headless debug server:
dlv connect
Set a breakpoint on the main function:
(dlv) break main.main
Breakpoint 1 set at 0x58e4c8 for main.main() ./main.go:10
(dlv) continue
or
(dlv) b main.go:10
Breakpoint 1 set at 0x58e4c8 for main.main() ./main.go:10
(dlv) c
Install package into $GOPATH/bin/$PACKAGE_NAME:
go install # inside package directory
View package information:
go list -f '{{ .Name }}: {{ .Doc }}' # inside package directory
go list -f '{{ join .Imports "\n" }}' # inside package directory
or
go list -f '{{ .Doc }}' fmt
Documentation
go doc fmt Println
Start documentation server:
godoc -http :6060
Check for errors when go run fails silently:
errcheck
GOOS=windows go build
go version <binary>
<binary>: go1.13.5
or
$ go version -m dlv
dlv: go1.13.5
path github.com/go-delve/delve/cmd/dlv
mod github.com/go-delve/delve v1.3.2 h1:K8VjV+Q2YnBYlPq0ctjrvc9h7h03wXszlszzfGW5Tog=
dep github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5 h1:rIXlvz2IWiupMFlC45cZCXZFvKX/ExBcSLrDy2G0Lp8=
...
Resource:
Run for 5 seconds:
go-wrk -d 5 'http://localhost:8080/test'
Get current directory:
// Get current directory (executable folder) as absolute path
// Resource: https://stackoverflow.com/a/18537419
func currentDir() string {
ex, err := os.Executable()
if err != nil {
log.Fatalf("Could not get executable directory. %+v", err)
}
return filepath.Dir(ex)
}
Basic context:
ctx := context.Background()
With possibility to cancel it:
ctx, cancel := context.WithCancel(ctx)
With timeout:
ctx, cancel := context.WithTimeout(ctx, time.Second*3)
// Uses time.AfterFunc
// Will not garbage collect before timer expires
defer cancel()
Add value to context:
ctx := context.WithValue(ctx, "variable_name", "variable value")
Scope context keyspace
type privateCtxType string
var (
reqIDKey = privateCtxType("reqID")
)
func RequestIDValue(ctx context.Context) (id int, exists bool) {
id, exists := ctx.Value(reqIDKey).(int)
return
}
func WithRequestID(ctx context.Context, reqID int) context.Context {
return context.WithValue(ctx, reqIDKey, reqID)
}
Errgroup Example
const query = "golang"
g, ctx := errgroup.WithContext(ctx)
searches := []Search{Web, Image, Video}
results := make([]Result, len(searches))
for i, search := range searches {
i, search := i, search // https://golang.org/doc/faq#closures_and_goroutines
g.Go(func() error {
result, err := search(ctx, query)
if err == nil {
results[i] = result
}
return err
})
}
if err := g.Wait(); err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
for _, result := range results {
fmt.Println(result)
}
HTTP request example
package main
import (
"context"
"fmt"
"log"
"math"
"net/http"
"time"
)
func main() {
// Context setup
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
go func () {
dur := time.Second*10
time.Sleep(dur)
log.Printf("Canceled request after: %.2f seconds\n", dur.Seconds())
cancel()
}()
// Prepare a request which takes 5 seconds
slowURL := "http://www.mocky.io/v2/5185415ba171ea3a00704eed?mocky-delay=5s"
req, err := http.NewRequestWithContext(ctx, http.MethodGet, slowURL, http.NoBody)
if err != nil {
log.Printf("Problem with request. %s\n", err)
return
}
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Printf("Problem with response. %s\n", err)
return
}
if end, ok := ctx.Deadline(); ok {
fmt.Printf("Remain: %.2f seconds\n", math.Abs(time.Now().Sub(end).Seconds()))
}
fmt.Printf("Response status: %d\n", res.StatusCode)
}
$ go run http-with-context.go
2020/04/28 15:28:56 Problem with response. Get http://www.mocky.io/v2/5185415ba171ea3a00704eed?mocky-delay=5s: context deadline exceeded
Resources
package main
import (
"fmt"
"reflect"
)
type T struct {
f1 string `one:"1" two:"2" blank:""`
f2 string "f one"
f3 string "invalid:``" // Only double quotes are allowed
}
func main() {
t := reflect.TypeOf(T{})
f, _ := t.FieldByName("f1")
fmt.Println(f.Tag) // one:"1" two:"2" blank: 4
v, ok := f.Tag.Lookup("one")
fmt.Printf("%s, %t\n", v, ok) // 1, true
// Alternatively use get
v = f.Tag.Get("one")
fmt.Printf("%s\n", v) // 1
v, ok = f.Tag.Lookup("blank")
fmt.Printf("%s, %t\n", v, ok) // , true
v, ok = f.Tag.Lookup("five")
fmt.Printf("%s, %t\n", v, ok) // , false
}
Example:
type Member struct {
Age int `json:"age,string" xml:"the_age,string"`
}
Resources:
FROM golang
COPY . /go/src/github.com/czerasz/example
WORKDIR /go/src/github.com/czerasz/example
RUN go get && CGO_ENABLED=0 GOOS=linux go build -o server .
FROM scratch
LABEL maintainer="Michal Czeraszkiewicz <contact@czerasz.com>"
COPY --from=0 /go/src/github.com/czerasz/example/server /opt/czerasz/vision/server
COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
ADD html/ /opt/czerasz/vision/html
EXPOSE 8080
WORKDIR /opt/czerasz/vision
ENTRYPOINT [ "./server" ]
Resources:
package main
import (
"fmt"
"reflect"
)
func main() {
var v float64 = 65.5
getTypeInfo(v)
}
func getTypeInfo(v interface{}) {
fmt.Printf("Type via Printf: %T\n", v)
vo := reflect.ValueOf(v)
fmt.Printf("ValueOf: %v\n", vo)
fmt.Printf("ValueOf().Type(): %v\n", vo.Type())
to := reflect.TypeOf(v)
fmt.Printf("TypeOf: %v\n", to)
}
$ go run reflect-example.go
Type via Printf: float64
ValueOf: 65.5
ValueOf().Type(): float64
TypeOf: float64
Resources:
Initialize module:
go mod init github.com/czerasz/example
The above command creates go.mod file
- install specific module
# specific branch
go get github.com/joho/godotenv@master
# specific tag
go get github.com/joho/godotenv@v1.2.0
# specific commit
go get github.com/joho/godotenv@d6ee687
The above command creates or updates go.sum file
- clean up
go mod tidy
Update the go.mod file - remove unused dependencies and add any missing ones
This command should be used after each commit.
- update minor or patch version
go get -u gopkg.in/gookit/color.v1
go get -u=patch gopkg.in/gookit/color.v1
- update major version
semantic import versions - this feature allows go to use two versions of the package at the same time
import (
"github.com/urfave/cli/v2"
)
- vendoring
$ go mod tidy
$ go mod vendor
Creates a vendor directory and vendor/modules.txt file
- replace module:
go mod edit -replace=github.com/golangci/golangci-lint=github.com/khan/golangci-lint@${LATEST_GOLANGCI_COMMIT}
# This will create/update the replace directive to github.com/Khan/golangci-lint v1.27.2-0.20200610182323-d6b2676ecc9b
# One could also have used the branch name instead of commit SHA1.
Or use local version:
go mod edit -replace github.com/czerasz/bar=/home/czerasz/Projects/bar
Resources:
- Evaluating Go’s Package Management and Module Systems
- Handling Go modules replacement directive version lag and fork maintenance
- Using “replace” in go.mod to point to your local module
UPX usage:
curl -sSLO https://github.com/upx/upx/releases/download/v3.96/upx-3.96-amd64_linux.tar.xz
tar -xf upx-3.96-amd64_linux.tar.xz
./upx-3.96-amd64_linux/upx --brute out
Resources:
Useful Atom packages:
autocomplete-golanguage-protobufplatformio-ide-terminal- Atom terminalgo-debuggo-plus- gomodifytags - find package name
Improve Go development in Atom:
-
Packages → Go → Update Tools
-
Generating Ctags for your Go code - works with Atoms autocomplete:
Install:
go get -u github.com/jstemmer/gotagsGenerate tags:gotags -tag-relative=true -R=true -sort=true -f="tags" -fields=+l .
Ctrl+C
Resources:
$GOROOT/src/pkg/net/http/internalcan be imported only by the standardnet/httpandnet/http/*packages.$GOPATH/src/mypkg/internal/foocan be imported only by code in$GOPATH/src/mypkg.
- YouTube: Ardan Labs - Go Syntax [LiveBytes]
- Golang basics - writing unit tests
- Unit Testing http client in Go
- cgo is not Go
- Build Web Application with Golang
- How I write HTTP services after eight years.
- YouTube: Mat Ryer - How I Write HTTP Web Services after Eight Years
- YouTube: Ardan Labs - Go Syntax
- GitHub: fgprof - The Full Go Profiler
- Why You Should Be Using errgroup.WithContext() in Your Golang Server Handlers
- DigitalOcean How to code in go PDF
- How to handle signals with Go to graceful shutdown HTTP server
- Package debug
- Go for Cloud - super cool go tips
- Tools: golangci-lint
- YouTube: Profiling a go service in production
- YouTube: Gopherpalooza 2019
- GitHub: ardanlabs/gotraining
- Debugging with Delve
- Apex log - structured & centralized logging for Go
- Clean Architecture with GO
- How to Manage Database Timeouts and Cancellations in Go
- Practical Persistence in Go: Organising Database Access
- Bitmasks, bitsets and flags
- Hash tables explained [step-by-step example]
- Numbers that start with zero
- The Ultimate Go Study Guide - based on Ardan Labs training
- Spaceship Go: online book
- YouTube: Golang / Go Gin Framework Crash Course 08 | Setting up a CI/CD pipeline with GitLab CI and Heroku
- YouTube: Golang course - a lot of cool videos
- Containerize Your Go Developer Environment – Part 2
Blogs worth reading:
-
Example RESTful API:
structuring applications in GO plurasight.com - greating wed applications in GO golang-book.com
-
Go Concurrency Patterns - READ
-
Parsing JSON in Golang - READ
-
IO:
-
Logger:
-
Testing:
-
Channels:
-
HTTP:
-
Viper:
-
Advanced Google’s Go (golang) Programming Course - taki sobie
-
Web Development w/ Google’s Go (golang) Programming Language
-
Editor:
-
Debugging:
-
Proto Buffers: