GoFrame framework: log configuration management

Posted by jazz_snob on Thu, 20 Jan 2022 22:35:11 +0100

introduce

Through a complete example, in gogf/gf Manage logs reasonably in the framework.

What are the usage scenarios?

  • Log auto scroll
  • Split into multiple log files
  • Log format modification
  • wait

We will use rk-boot To start gogf/gf Microservices of the framework.

Please visit the following address for a complete tutorial:

install

go get github.com/rookie-ninja/rk-boot/gf

Brief concept

Rk boot uses the following two libraries to manage logs.

Rk boot defines two log types, which will be described in detail later. Here is a brief introduction.

  • ZapLogger: standard log, used to record Error, Info, etc.
  • Event logger: JSON or Console format, used to record events, such as RPC requests.

Quick start

In this example, we will try to change the path and format of the zap log.

1. Create boot yaml

---
zapLogger:
  - name: zap-log                        # Required
    zap:
      encoding: json                     # Optional, options: console, json
      outputPaths: ["logs/zap.log"]      # Optional
gf:
  - name: greeter
    port: 8080
    enabled: true

2. Create main go

Write a log to the zap log instance.

// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-boot/gf"
)

func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Write zap log
	boot.GetZapLoggerEntry("zap-log").GetLogger().Info("This is zap-log")

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3. Verification

Folder structure

├── boot.yaml
├── go.mod
├── go.sum
├── logs
│   └── zap.log
└── main.go

Log output

{"level":"INFO","ts":"2021-10-21T02:10:09.279+0800","msg":"This is zap-log"}

Configure EventLogger

In the above example, we configured the zap log. This time, we modify the EventLogger.

1. Create boot yaml

---
eventLogger:
  - name: event-log                      # Required
    encoding: json                       # Optional, options: console, json
    outputPaths: ["logs/event.log"]      # Optional
gf:
  - name: greeter
    port: 8080
    enabled: true

2. Create main go

Write a log to the event log instance.

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	"github.com/rookie-ninja/rk-entry/entry"
)

func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Write event log
	helper := boot.GetEventLoggerEntry("event-log").GetEventHelper()
	event := helper.Start("demo-event")
	event.AddPair("key", "value")
	helper.Finish(event)

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3. Start main go

$ go run main.go

4. Verification

Folder structure

├── boot.yaml
├── go.mod
├── go.sum
├── logs
│   └── event.log
└── main.go

Log content

{"endTime": "2022-01-18T22:18:44.926+0800", "startTime": "2022-01-18T22:18:44.926+0800", "elapsedNano": 746, "timezone": "CST", "ids": {"eventId":"2aaea6f5-c7ac-4245-ac50-857726f3ede4"}, "app": {"appName":"rk","appVersion":"","entryName":"","entryType":""}, "env": {"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}, "payloads": {}, "error": {}, "counters": {}, "pairs": {"key":"value"}, "timing": {}, "remoteAddr": "localhost", "operation": "demo-event", "eventStatus": "Ended", "resCode": "OK"}

concept

In the above example, we tried ZapLogger and EventLogger. Next, let's see how rk boot is implemented and how to use it.

framework

ZapLoggerEntry

ZapLoggerEntry is an encapsulation of zap instances.

// ZapLoggerEntry contains bellow fields.
// 1: EntryName: Name of entry.
// 2: EntryType: Type of entry which is ZapLoggerEntryType.
// 3: EntryDescription: Description of ZapLoggerEntry.
// 4: Logger: zap.Logger which was initialized at the beginning.
// 5: LoggerConfig: zap.Logger config which was initialized at the beginning which is not accessible after initialization..
// 6: LumberjackConfig: lumberjack.Logger which was initialized at the beginning.
type ZapLoggerEntry struct {
	EntryName        string             `yaml:"entryName" json:"entryName"`
	EntryType        string             `yaml:"entryType" json:"entryType"`
	EntryDescription string             `yaml:"entryDescription" json:"entryDescription"`
	Logger           *zap.Logger        `yaml:"-" json:"-"`
	LoggerConfig     *zap.Config        `yaml:"zapConfig" json:"zapConfig"`
	LumberjackConfig *lumberjack.Logger `yaml:"lumberjackConfig" json:"lumberjackConfig"`
}

How to boot Configure ZapLoggerEntry in yaml?

ZapLoggerEntry is fully compatible zap and lumberjack YAML structure of. Users can configure multiple ZapLogger instances according to their needs and access them through name.

Full configuration:

---
zapLogger:
  - name: zap-logger                      # Required
    description: "Description of entry"   # Optional
    zap:
      level: info                         # Optional, default: info, options: [debug, DEBUG, info, INFO, warn, WARN, dpanic, DPANIC, panic, PANIC, fatal, FATAL]
      development: true                   # Optional, default: true
      disableCaller: false                # Optional, default: false
      disableStacktrace: true             # Optional, default: true
      sampling:                           # Optional, default: empty map
        initial: 0
        thereafter: 0
      encoding: console                   # Optional, default: "console", options: [console, json]
      encoderConfig:
        messageKey: "msg"                 # Optional, default: "msg"
        levelKey: "level"                 # Optional, default: "level"
        timeKey: "ts"                     # Optional, default: "ts"
        nameKey: "logger"                 # Optional, default: "logger"
        callerKey: "caller"               # Optional, default: "caller"
        functionKey: ""                   # Optional, default: ""
        stacktraceKey: "stacktrace"       # Optional, default: "stacktrace"
        lineEnding: "\n"                  # Optional, default: "\n"
        levelEncoder: "capitalColor"      # Optional, default: "capitalColor", options: [capital, capitalColor, color, lowercase]
        timeEncoder: "iso8601"            # Optional, default: "iso8601", options: [rfc3339nano, RFC3339Nano, rfc3339, RFC3339, iso8601, ISO8601, millis, nanos]
        durationEncoder: "string"         # Optional, default: "string", options: [string, nanos, ms]
        callerEncoder: ""                 # Optional, default: ""
        nameEncoder: ""                   # Optional, default: ""
        consoleSeparator: ""              # Optional, default: ""
      outputPaths: [ "stdout" ]           # Optional, default: ["stdout"], stdout would be replaced if specified
      errorOutputPaths: [ "stderr" ]      # Optional, default: ["stderr"], stderr would be replaced if specified
      initialFields:                      # Optional, default: empty map
        key: "value"
    lumberjack:                           # Optional
      filename: "rkapp-event.log"         # Optional, default: It uses <processname>-lumberjack.log in os.TempDir() if empty.
      maxsize: 1024                       # Optional, default: 1024 (MB)
      maxage: 7                           # Optional, default: 7 (days)
      maxbackups: 3                       # Optional, default: 3 (days)
      localtime: true                     # Optional, default: true
      compress: true                      # Optional, default: true

How to get ZapLogger in code?

Access via name.

boot := rkboot.NewBoot()

// Access entry
boot.GetZapLoggerEntry("zap-logger")

// Access zap logger
boot.GetZapLoggerEntry("zap-logger").GetLogger()

// Access zap logger config
boot.GetZapLoggerEntry("zap-logger").GetLoggerConfig()

// Access lumberjack config
boot.GetZapLoggerEntry("zap-logger").GetLumberjackConfig()

EventLoggerEntry

Rk boot treats each RPC request as an Event and uses rk-query Event type in to log.

// EventLoggerEntry contains bellow fields.
// 1: EntryName: Name of entry.
// 2: EntryType: Type of entry which is EventLoggerEntryType.
// 3: EntryDescription: Description of EventLoggerEntry.
// 4: EventFactory: rkquery.EventFactory was initialized at the beginning.
// 5: EventHelper: rkquery.EventHelper was initialized at the beginning.
// 6: LoggerConfig: zap.Config which was initialized at the beginning which is not accessible after initialization.
// 7: LumberjackConfig: lumberjack.Logger which was initialized at the beginning.
type EventLoggerEntry struct {
	EntryName        string                `yaml:"entryName" json:"entryName"`
	EntryType        string                `yaml:"entryType" json:"entryType"`
	EntryDescription string                `yaml:"entryDescription" json:"entryDescription"`
	EventFactory     *rkquery.EventFactory `yaml:"-" json:"-"`
	EventHelper      *rkquery.EventHelper  `yaml:"-" json:"-"`
	LoggerConfig     *zap.Config           `yaml:"zapConfig" json:"zapConfig"`
	LumberjackConfig *lumberjack.Logger    `yaml:"lumberjackConfig" json:"lumberjackConfig"`
}

EventLogger field

We can see that the log printed by EventLogger contains fields. Let's introduce these fields.

field details
endTime End time
startTime start time
elapsedNano Event time overhead (Nanoseconds)
timezone time zone
ids Contains eventId, requestId and traceId. If the original data interceptor is started, or event As like as two peas, SetRequest () is invoked by the user, the new RequestId will be used, and eventId will be exactly the same as requestId. If the call chain interceptor is started, the traceId will be recorded.
app contain appName, appVersion, entryName, entryType.
env Including arch, AZ, DOMAIN, hostname, localip, OS, REALM, REGION REALM, REGION, AZ, DOMAIN fields. These fields are from the system environment variables (real, REGION, AZ, DOMAIN). "*" means that the environment variable is empty.
payloads Contains RPC related information.
error Contains errors.
counters Via event Setcounter().
pairs Via event Addpair().
timing Via event Starttimer() and event Endtimer().
remoteAddr RPC remote address.
operation RPC name.
resCode RPC return code.
eventStatus Ended or InProgress

example

------------------------------------------------------------------------
endTime=2021-11-27T02:30:27.670807+08:00
startTime=2021-11-27T02:30:27.670745+08:00
elapsedNano=62536
timezone=CST
ids={"eventId":"4bd9e16b-2b29-4773-8908-66c860bf6754"}
app={"appName":"gf-demo","appVersion":"master-f948c90","entryName":"greeter","entryType":"GfEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.6","os":"darwin","realm":"*","region":"*"}
payloads={"apiMethod":"GET","apiPath":"/rk/v1/healthy","apiProtocol":"HTTP/1.1","apiQuery":"","userAgent":"curl/7.64.1"}
error={}
counters={}
pairs={}
timing={}
remoteAddr=localhost:61726
operation=/rk/v1/healthy
resCode=200
eventStatus=Ended
EOE

How to boot Configure EventLoggerEntry in yaml?

EventLoggerEntry will inject the Application name into the Event. The starter will start from go Extract the Application name from the mod file. If there is no go Mod file, the initiator will use the default name.

You can configure multiple EventLogger instances as required and access them through name.

Full configuration:

---
eventLogger:
  - name: event-logger                 # Required
    description: "This is description" # Optional
    encoding: console                  # Optional, default: console, options: console and json
    outputPaths: ["stdout"]            # Optional
    lumberjack:                        # Optional
      filename: "rkapp-event.log"      # Optional, default: It uses <processname>-lumberjack.log in os.TempDir() if empty.
      maxsize: 1024                    # Optional, default: 1024 (MB)
      maxage: 7                        # Optional, default: 7 (days)
      maxbackups: 3                    # Optional, default: 3 (days)
      localtime: true                  # Optional, default: true
      compress: true                   # Optional, default: true

How to get the EventLogger in the code?

Access via name.

boot := rkboot.NewBoot()

// Access entry
boot.GetEventLoggerEntry("event-logger")

// Access event factory
boot.GetEventLoggerEntry("event-logger").GetEventFactory()

// Access event helper
boot.GetEventLoggerEntry("event-logger").GetEventHelper()

// Access lumberjack config
boot.GetEventLoggerEntry("event-logger").GetLumberjackConfig()

How to use Event?

Event is an interface that contains several methods. Please refer to: Event

Common methods:

boot := rkboot.NewBoot()

// Get EventHelper to create Event instance
helper := boot.GetEventLoggerEntry("event-log").GetEventHelper()

// Start and finish event
event := helper.Start("demo-event")
helper.Finish(event)

// Add K/V
event.AddPair("key", "value")

// Start and end timer
event.StartTimer("my-timer")
event.EndTimer("my-timer")

// Set counter
event.SetCounter("my-counter", 1)

Topics: Go github yaml optional