[Code Page] Build your own golang framework step by step from scratch

Posted by AaZmaN on Mon, 20 Jan 2020 19:23:08 +0100

In the previous article, we defined the project basic catalog structure.Now let's review my thoughts:

  1. Create entry file;
  2. Specify configuration, log directory;
  3. Specify the database, queue, cache component directory;
  4. Create a controller directory for data processing;
  5. Specify miscellaneous items such as public functions.

Next, we'll fill in the code in this order.

Entry File

func main()  {
    config.InitConfig()
    logger.InitLogger()
}

Ignoring some implementation details for now, we need to initialize the configuration and logs first. Let's look at the logs first. I'll use Uber's zap log library.Let's see what the log module does?

Journal

package logger

import (
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "gopkg.in/natefinch/lumberjack.v2"
)

var l *zap.Logger

func InitLogger(logPath, logLevel string) error {
    hook := lumberjack.Logger{
        Filename:   logPath,
        MaxSize:    1024,
        MaxBackups: 3,
        MaxAge:     7,
        Compress:   true,
    }
    w := zapcore.AddSync(&hook)

    var level zapcore.Level
    switch logLevel {
    case "debug":
        level = zap.DebugLevel
    case "info":
        level = zap.InfoLevel
    case "error":
        level = zap.ErrorLevel
    default:
        level = zap.DebugLevel
    }

    encoderConfig := zap.NewProductionEncoderConfig()
    encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
    core := zapcore.NewCore(
        zapcore.NewConsoleEncoder(encoderConfig),
        w,
        level,
    )

    l = zap.New(core)

    return nil
}

func GetLogger() *zap.Logger {
    return l
}

We first define a package-level global variable, l, of type *zap.Logger, and create two functions, InitLogger and GetLogger.Because zap does not support log archiving, a hook for lumberjack is defined in InitLogger to archive logs.We can see that InitLogger has two inputs: logPath and logLevel.In general, these parameters should be in the configuration file, so let's write the configuration next.

To configure

We simply create a config.json file to store the configuration:
config.json:

{
  "log_config": {
    "log_path": "{your_path}",
    "log_level": "debug"
  }
}

Then create the corresponding structure in config.go, and define the InitConfig and GetConfig methods. In the InitConfig method, I read the contents of the configuration file, parse it into the structure, and handle the errors. If there is any error information, I wrap it up to facilitate future error locations.InitConfig has one entry, the path to the configuration file, which I get from the command line.
config.go:

package config

import (
    "encoding/json"
    "github.com/pkg/errors"
    "io/ioutil"
)

type LogConfig struct {
    LogPath string `json:"log_path"`
    LogLevel string `json:"log_level"`
}

type Config struct {
    LogConfig LogConfig `json:"log_config"`
} 

var conf Config

func InitConfig(configPath string) error {
    configFile, err := ioutil.ReadFile(configPath)
    if err != nil {
        err = errors.Wrap(err, "Read config file failed.")
        return err
    }
    err = json.Unmarshal(configFile, &conf)
    if err != nil {
        err = errors.Wrap(err, "Unmarshal config file failed.")
        return err
    }
    return nil
}

func GetConfig() Config {
    return conf
}

Of course, we have more than that configuration. We still have database, cache and other configurations not added, but don't worry. First we figure out a route, and then we can draw a ladle with a gourd.

Adjust entry file

Okay, the initialization of the log and configuration is roughly done. Let's go back and look at the adjustment of the entry file:

package main

import (
    "flag"
    "fmt"
    "github.com/TomatoMr/awesomeframework/config"
    "github.com/TomatoMr/awesomeframework/logger"
    "os"
)

func main()  {
    var configPath string
    flag.StringVar(&configPath, "config", "", "Profile Path")
    flag.Parse()

    if configPath == "" {
        fmt.Printf("Config Path must be assigned.")
        os.Exit(1)
    }

    var err error
    err = config.InitConfig(configPath)
    if err != nil {
        fmt.Printf("Init config failed. Error is %v", err)
        os.Exit(1)
    }

    logConfig := config.GetConfig().LogConfig

    err = logger.InitLogger(logConfig.LogPath, logConfig.LogLevel)
    if err != nil {
        fmt.Printf("Init logger failed. Error is %v", err)
        os.Exit(1)
    }

    logger.GetLogger().Info("Init success.")
}

We adjusted the entry file, got the configuration file path from the command line, initialized the configuration and log, and finally printed the initialization results.

Test it

First, compile it:

$ go build 

Next, modify the log_path of your config.json file, and specify your configuration file path on the command line:

$ awesomeframework --config={$your_path}/config.json

Finally, let's see that it's not working properly and that the log file is printed as follows:

2020-01-19T20:41:57.506+0800    info    Init success.

Summary

So far, we've initialized configuration and logging, and we'll start initializing components like databases.Can be used in https://github.com/TomatoMr/awesomeframework
Find today's code.Not finished yet...

Welcome to my public number: onepunchgo.

Topics: Go JSON github Database encoding