🎨
golang-guidance
  • Go Guidance
  • Golang Fundamental
    • Basic Golang
    • Pseudo OOP
    • Konkurensi
  • Design Pattern
    • Singleton
    • Dependency Injection
  • Build Rest API Framework
    • Pengenalan Rest API
    • Start up
    • Shutdown
    • Json
    • Database
    • Clean architecture
    • Configuration
    • Fatal
    • Logging
    • Routing
    • CRUD
    • Request Response Helper
    • Error Handler
    • Unit Testing
    • API Testing
    • Context
    • Validation
    • Middleware
    • Token
    • Role Based Access Controller
  • GraphQL Framework
    • Pengenalan GraphQL
  • gRPC Framework
    • Pengenalan gRPC
    • Protocol Buffer
    • makefile
    • gRPC Server
    • Config
    • Database
    • Routing
    • Clean Architecture
    • gRPC Client
    • Tracing
    • Caching
    • gRPC Testing
Powered by GitBook
On this page

Was this helpful?

  1. Build Rest API Framework

Logging

  • Saat ini kita menggunakan log global. Ini sebuah variabel di level paket.

  • Jangan gunakan variabel global, seperti variabel di level paket.

  • Solusinya, kita akan melewatkan pointer *log.Logger ke paket yang membutuhkannya melalui pattern dependency injection.

  • Create variabel log (pointer) di awal func run() di file main.go

    // =========================================================================
    // Logging
    log := log.New(os.Stdout, "Essentials : ", log.LstdFlags|log.Lmicroseconds|log.Lshortfile)
  • Semua pemakaian log akan menggunakan pointer log yang telah dibuat.

  • Lewatkan pointer log ke service yang membutuhkan dengan pattern dependency injection

service := controllers.Users{Db: db, Log: log}
  • File main.go berubah menjadi

package main

import (
    "context"
    "essentials/controllers"
    "essentials/libraries/config"
    "essentials/libraries/database"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    _ "github.com/go-sql-driver/mysql"
)

func main() {
    if _, ok := os.LookupEnv("APP_ENV"); !ok {
        config.Setup(".env")
    }

    if err := run(); err != nil {
        log.Printf("error: shutting down: %s", err)
        os.Exit(1)
    }
}

func run() error {
    // =========================================================================
    // Logging
    log := log.New(os.Stdout, "Essentials : ", log.LstdFlags|log.Lmicroseconds|log.Lshortfile)

    // =========================================================================
    // App Starting

    log.Printf("main : Started")
    defer log.Println("main : Completed")

    // =========================================================================

    // Start Database

    db, err := database.Open()
    if err != nil {
        return fmt.Errorf("connecting to db: %v", err)
    }
    defer db.Close()

    // Create variable service with pattern dependency injection.
    // Inject koneksion db to type of Users
    service := controllers.Users{Db: db, Log: log}

    // parameter server
    server := http.Server{
        Addr:         os.Getenv("APP_PORT"),
        Handler:      http.HandlerFunc(service.List),
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 5 * time.Second,
    }

    serverErrors := make(chan error, 1)
    // mulai listening server
    go func() {
        log.Println("server listening on", server.Addr)
        serverErrors <- server.ListenAndServe()
    }()

    // Membuat channel untuk mendengarkan sinyal interupsi/terminate dari OS.
    // Menggunakan channel buffered karena paket signal membutuhkannya.
    shutdown := make(chan os.Signal, 1)
    signal.Notify(shutdown, os.Interrupt, syscall.SIGTERM)

    // Mengontrol penerimaan data dari channel,
    // jika ada error saat listenAndServe server maupun ada sinyal shutdown yang diterima
    select {
    case err := <-serverErrors:
        return fmt.Errorf("Starting server: %v", err)

    case <-shutdown:
        log.Println("caught signal, shutting down")

        // Jika ada shutdown, meminta tambahan waktu 5 detik untuk menyelesaikan proses yang sedang berjalan.
        const timeout = 5 * time.Second
        ctx, cancel := context.WithTimeout(context.Background(), timeout)
        defer cancel()

        if err := server.Shutdown(ctx); err != nil {
            log.Printf("main : Graceful shutdown did not complete in %v : %v", timeout, err)
            if err := server.Close(); err != nil {
                return fmt.Errorf("could not stop server gracefully: %v", err)
            }
        }
    }

    return nil
}
  • File controllers/users.go berubah menjadi

package controllers

import (
    "database/sql"
    "encoding/json"
    "essentials/models"
    "essentials/payloads/response"
    "log"
    "net/http"
)

// Users : struct for set Users Dependency Injection
type Users struct {
    Db  *sql.DB
    Log *log.Logger
}

// List : http handler for returning list of users
func (u *Users) List(w http.ResponseWriter, r *http.Request) {
    user := new(models.User)
    list, err := user.List(u.Db)
    if err != nil {
        u.Log.Println("error get list user", err)
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    var responseList []response.UserResponse
    for _, l := range list {
        var res response.UserResponse
        res.Transform(l)
        responseList = append(responseList, res)
    }

    data, err := json.Marshal(responseList)
    if err != nil {
        u.Log.Println("error marshalling result", err)
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    w.WriteHeader(http.StatusOK)
    if _, err := w.Write(data); err != nil {
        u.Log.Println("error writing result", err)
    }
}
PreviousFatalNextRouting

Last updated 3 years ago

Was this helpful?