orbit

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 17, 2024 License: MIT Imports: 19 Imported by: 3

README

orbit

logo

A lightweight http web service wrapper framework.

Introduction

orbit is a lightweight http web service wrapper framework. It is designed to be simple and easy to use. It is based on gin, zap and prometheus. It provides a series of convenient features to help you quickly build a web service.

Why is it called orbit? Because it is a lightweight framework, it is like a satellite orbiting the earth, and the orbit is your business logic.

Why not use gin directly? Because gin is too simple, if you want to start a web service, you need to do a lot of work, such as logging, monitoring, etc. orbit is based on gin, and provides a series of convenient features to help you quickly build a web service.

Advantages

  • Lightweight, easy to use, easy to learn
  • Support zap logging, async and sync logging
  • Support prometheus monitoring
  • Support swagger api document
  • Support graceful shutdown
  • Support cors middleware
  • Support auto recover from panic
  • Support custom middleware
  • Support custom router group
  • Support custom define access log format and fields
  • Support custom define recovery log format and fields
  • Support repeat read request/response body, and cache request/response body bytes.

Installation

go get github.com/shengyanli1982/orbit

Quick Start

orbit is very easy to use, you can quickly build a web service in a few minutes. usually, you only need to do the following:

  1. Create orbit start configuration
  2. Create orbit feature options
  3. Create orbit instance

Default URL PATH

[!NOTE] Default url path is system default, you can not change it and it is not in your control.

  • /metrics - prometheus metrics
  • /swagger/*any - swagger api document
  • /debug/pprof/*any - pprof debug
  • /ping - health check

1. Config

The orbit has some config options, you can set it before start orbit instance.

  • WithSugaredLogger - use zap sugared logger, default is DefaultSugeredLogger
  • WithLogger - use zap logger, default is DefaultConsoleLogger
  • WithAddress - http server listen address, default is 127.0.0.1
  • WithPort - http server listen port, default is 8080
  • WithRelease - http server release mode, default is false
  • WithHttpReadTimeout - http server read timeout, default is 15s
  • WithHttpWriteTimeout - http server write timeout, default is 15s
  • WithHttpReadHeaderTimeout - http server read header timeout, default is 15s
  • WithAccessLogEventFunc - http server access log event func, default is DefaultAccessEventFunc
  • WithRecoveryLogEventFunc - http server recovery log event func, default is DefaultRecoveryEventFunc
  • WithPrometheusRegistry - http server prometheus registry, default is prometheus.DefaultRegister

You can use NewConfig to create a default config, and use WithXXX to set config options. DefaultConfig is alias of NewConfig().

2. Feature

Also orbit has some feature options, you can set it before start orbit instance.

  • EnablePProf - enable pprof debug, default is false
  • EnableSwagger - enable swagger api document, default is false
  • EnableMetric - enable prometheus metrics, default is false
  • EnableRedirectTrailingSlash - enable redirect trailing slash, default is false
  • EnableRedirectFixedPath - enable redirect fixed path, default is false
  • EnableForwardedByClientIp - enable forwarded by client ip, default is false
  • EnableRecordRequestBody - enable record request body, default is false

You can use NewOptions to create a null feature, and use EnableXXX to set feature options.

  • DebugOptions use for debug, it is alias of NewOptions().EnablePProf().EnableSwagger().EnableMetric().EnableRecordRequestBody().
  • ReleaseOptions use for release, it is alias of NewOptions().EnableMetric().

[!NOTE] Here is a best recommendation, you can use DebugOptions for debug, and use ReleaseOptions for release.

3. Instance

After you create orbit config and feature options, you can create orbit instance.

[!IMPORTANT] When you Run the orbit instance, it will not block the current goroutine which mean you can do other things after Run the orbit instance.

If you want to block the current goroutine, you can use project GS to provide a Waitting to block the current goroutine.

[!TIP] Here is a way to lazy. You can use NewHttpService to wrap func(*gin.RouterGroup) to Service interface implementation.

NewHttpService(func(g *gin.RouterGroup) {
  g.GET("/demo", func(c *gin.Context) {
      c.String(http.StatusOK, "demo")
  })
})

Example

package main

import (
	"time"

	"github.com/shengyanli1982/orbit"
)

func main() {
	// Create a new orbit configuration.
	config := orbit.NewConfig()

	// Create a new orbit feature options.
	opts := orbit.NewOptions()

	// Create a new orbit engine.
	engine := orbit.NewEngine(config, opts)

	// Start the engine.
	engine.Run()

	// Wait for 30 seconds.
	time.Sleep(30 * time.Second)

	// Stop the engine.
	engine.Stop()
}

Result

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> github.com/shengyanli1982/orbit.healthcheckService.func1 (1 handlers)
{"level":"INFO","time":"2024-01-10T17:00:13.139+0800","logger":"default","caller":"orbit/gin.go:160","message":"http server is ready","address":"127.0.0.1:8080"}

Testing

$ curl -i http://127.0.0.1:8080/ping
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Wed, 10 Jan 2024 09:07:26 GMT
Content-Length: 7

successs

4. Custom Middleware

Because orbit is based on gin, so you can use gin middleware directly. So you can use custom middleware to do some custom things. For example, you can use cors middleware to support cors request.

Here is a example to use demo middleware to print >>>>>>!!! demo in the console.

Example

package main

import (
	"fmt"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
)

func customMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println(">>>>>>!!! demo")
		c.Next()
	}
}

type service struct{}

func (s *service) RegisterGroup(g *gin.RouterGroup) {
	g.GET("/demo", func(c *gin.Context) {})
}

func main() {
	// Create a new orbit configuration.
	config := orbit.NewConfig()

	// Create a new orbit feature options.
	opts := orbit.NewOptions().EnableMetric()

	// Create a new orbit engine.
	engine := orbit.NewEngine(config, opts)

	// Register a custom middleware.
	engine.RegisterMiddleware(customMiddleware())

	// Register a custom router group.
	engine.RegisterService(&service{})

	// Start the engine.
	engine.Run()

	// Wait for 30 seconds.
	time.Sleep(30 * time.Second)

	// Stop the engine.
	engine.Stop()
}

Result

$ go run demo.go
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> github.com/shengyanli1982/orbit.healthcheckService.func1 (1 handlers)
[GIN-debug] GET    /metrics                  --> github.com/shengyanli1982/orbit/utils/wrapper.WrapHandlerToGin.func1 (2 handlers)
[GIN-debug] GET    /demo                     --> main.(*service).RegisterGroup.func1 (7 handlers)
{"level":"INFO","time":"2024-01-10T20:03:38.869+0800","logger":"default","caller":"orbit/gin.go:162","message":"http server is ready","address":"127.0.0.1:8080"}
>>>>>>!!! demo
{"level":"INFO","time":"2024-01-10T20:03:41.275+0800","logger":"default","caller":"log/default.go:10","message":"http server access log","id":"","ip":"127.0.0.1","endpoint":"127.0.0.1:59787","path":"/demo","method":"GET","code":200,"status":"OK","latency":"780ns","agent":"curl/8.1.2","query":"","reqContentType":"","reqBody":""}

5. Custom Router Group

Custom router group is a very useful feature, you can use it to register a custom router group. You can use it to register a custom router group for demo service.

eg: /demo and /demo/test

Example

package main

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
	ocom "github.com/shengyanli1982/orbit/common"
)

type service struct{}

func (s *service) RegisterGroup(g *gin.RouterGroup) {
	// Register a custom router group.
	g = g.Group("/demo")

	// /demo
	g.GET(ocom.EmptyURLPath, func(c *gin.Context) {
		c.String(http.StatusOK, "demo")
	})

	// /demo/test
	g.GET("/test", func(c *gin.Context) {
		c.String(http.StatusOK, "test")
	})
}

func main() {
	// Create a new orbit configuration.
	config := orbit.NewConfig()

	// Create a new orbit feature options.
	opts := orbit.NewOptions().EnableMetric()

	// Create a new orbit engine.
	engine := orbit.NewEngine(config, opts)

	// Register a custom router group.
	engine.RegisterService(&service{})

	// Start the engine.
	engine.Run()

	// Wait for 30 seconds.
	time.Sleep(30 * time.Second)

	// Stop the engine.
	engine.Stop()
}

Result

$ curl -i http://127.0.0.1:8080/demo
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, UPDATE
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type
Access-Control-Max-Age: 172800
Content-Type: text/plain; charset=utf-8
Date: Wed, 10 Jan 2024 12:09:37 GMT
Content-Length: 4

demo

$ curl -i http://127.0.0.1:8080/demo/test
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, UPDATE
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type
Access-Control-Max-Age: 172800
Content-Type: text/plain; charset=utf-8
Date: Wed, 10 Jan 2024 12:09:43 GMT
Content-Length: 4

test

6. Custom Access Log

Http server access log is very important, you can use orbit to custom access log format and fields. Here is a example to custom access log format and fields.

Default LogEvent Fields

// LogEvent represents a log event.
type LogEvent struct {
	Message        string `json:"message,omitempty" yaml:"message,omitempty"`               // Message contains the log message.
	ID             string `json:"id,omitempty" yaml:"id,omitempty"`                         // ID contains the unique identifier of the log event.
	IP             string `json:"ip,omitempty" yaml:"ip,omitempty"`                         // IP contains the IP address of the client.
	EndPoint       string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`             // EndPoint contains the endpoint of the request.
	Path           string `json:"path,omitempty" yaml:"path,omitempty"`                     // Path contains the path of the request.
	Method         string `json:"method,omitempty" yaml:"method,omitempty"`                 // Method contains the HTTP method of the request.
	Code           int    `json:"statusCode,omitempty" yaml:"statusCode,omitempty"`         // Code contains the HTTP status code of the response.
	Status         string `json:"status,omitempty" yaml:"status,omitempty"`                 // Status contains the status message of the response.
	Latency        string `json:"latency,omitempty" yaml:"latency,omitempty"`               // Latency contains the request latency.
	Agent          string `json:"agent,omitempty" yaml:"agent,omitempty"`                   // Agent contains the user agent of the client.
	ReqContentType string `json:"reqContentType,omitempty" yaml:"reqContentType,omitempty"` // ReqContentType contains the content type of the request.
	ReqQuery       string `json:"query,omitempty" yaml:"query,omitempty"`                   // ReqQuery contains the query parameters of the request.
	ReqBody        string `json:"reqBody,omitempty" yaml:"reqBody,omitempty"`               // ReqBody contains the request body.
	Error          any    `json:"error,omitempty" yaml:"error,omitempty"`                   // Error contains the error object.
	ErrorStack     string `json:"errorStack,omitempty" yaml:"errorStack,omitempty"`         // ErrorStack contains the stack trace of the error.
}

Example

package main

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
	"github.com/shengyanli1982/orbit/utils/log"
	"go.uber.org/zap"
)

type service struct{}

func (s *service) RegisterGroup(g *gin.RouterGroup) {
	// /demo
	g.GET("/demo", func(c *gin.Context) {
		c.String(http.StatusOK, "demo")
	})
}

func customAccessLogger(logger *zap.SugaredLogger, event *log.LogEvent) {
	logger.Infow("access log", "path", event.Path, "method", event.Method)
}

func main() {
	// Create a new orbit configuration.
	config := orbit.NewConfig().WithAccessLogEventFunc(customAccessLogger)

	// Create a new orbit feature options.
	opts := orbit.NewOptions()

	// Create a new orbit engine.
	engine := orbit.NewEngine(config, opts)

	// Register a custom router group.
	engine.RegisterService(&service{})

	// Start the engine.
	engine.Run()

	// Wait for 30 seconds.
	time.Sleep(30 * time.Second)

	// Stop the engine.
	engine.Stop()
}

Result

{"level":"INFO","time":"2024-01-10T20:22:01.244+0800","logger":"default","caller":"accesslog/demo.go:24","message":"access log","path":"/demo","method":"GET"}

7. Custom Recovery Log

Http server recovery log give you a chance to know what happened when your service panic. You can use orbit to custom recovery log format and fields. Here is a example to custom recovery log format and fields.

Example

package main

import (
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
	"github.com/shengyanli1982/orbit/utils/log"
	"go.uber.org/zap"
)

type service struct{}

func (s *service) RegisterGroup(g *gin.RouterGroup) {
	// /demo
	g.GET("/demo", func(c *gin.Context) {
		panic("demo")
	})
}

func customRecoveryLogger(logger *zap.SugaredLogger, event *log.LogEvent) {
	logger.Infow("recovery log", "path", event.Path, "method", event.Method, "error", event.Error, "errorStack", event.ErrorStack)
}

func main() {
	// Create a new orbit configuration.
	config := orbit.NewConfig().WithRecoveryLogEventFunc(customRecoveryLogger)

	// Create a new orbit feature options.
	opts := orbit.NewOptions()

	// Create a new orbit engine.
	engine := orbit.NewEngine(config, opts)

	// Register a custom router group.
	engine.RegisterService(&service{})

	// Start the engine.
	engine.Run()

	// Wait for 30 seconds.
	time.Sleep(30 * time.Second)

	// Stop the engine.
	engine.Stop()
}

Result

{"level":"INFO","time":"2024-01-10T20:27:10.041+0800","logger":"default","caller":"recoverylog/demo.go:22","message":"recovery log","path":"/demo","method":"GET","error":"demo","errorStack":"goroutine 6 [running]:\nruntime/debug.Stack()\n\t/usr/local/go/src/runtime/debug/stack.go:24 +0x65\ngithub.com/shengyanli1982/orbit/internal/middleware.Recovery.func1.1()\n\t/Volumes/DATA/programs/GolandProjects/orbit/internal/middleware/system.go:145 +0x559\npanic({0x170ec80, 0x191cb70})\n\t/usr/local/go/src/runtime/panic.go:884 +0x213\nmain.(*service).RegisterGroup.func1(0x0?)\n\t/Volumes/DATA/programs/GolandProjects/orbit/example/recoverylog/demo.go:17 +0x27\ngithub.com/gin-gonic/gin.(*Context).Next(...)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/context.go:173\ngithub.com/shengyanli1982/orbit/internal/middleware.AccessLogger.func1(0xc0001e6300)\n\t/Volumes/DATA/programs/GolandProjects/orbit/internal/middleware/system.go:59 +0x1a5\ngithub.com/gin-gonic/gin.(*Context).Next(...)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/context.go:173\ngithub.com/shengyanli1982/orbit/internal/middleware.Cors.func1(0xc0001e6300)\n\t/Volumes/DATA/programs/GolandProjects/orbit/internal/middleware/system.go:35 +0x139\ngithub.com/gin-gonic/gin.(*Context).Next(...)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/context.go:173\ngithub.com/shengyanli1982/orbit/internal/middleware.BodyBuffer.func1(0xc0001e6300)\n\t/Volumes/DATA/programs/GolandProjects/orbit/internal/middleware/buffer.go:18 +0x92\ngithub.com/gin-gonic/gin.(*Context).Next(...)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/context.go:173\ngithub.com/shengyanli1982/orbit/internal/middleware.Recovery.func1(0xc0001e6300)\n\t/Volumes/DATA/programs/GolandProjects/orbit/internal/middleware/system.go:166 +0x82\ngithub.com/gin-gonic/gin.(*Context).Next(...)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/context.go:173\ngithub.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0000076c0, 0xc0001e6300)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/gin.go:616 +0x66b\ngithub.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0000076c0, {0x1924a30?, 0xc0000c02a0}, 0xc0001e6200)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/gin.go:572 +0x1dd\nnet/http.serverHandler.ServeHTTP({0xc00008be30?}, {0x1924a30, 0xc0000c02a0}, 0xc0001e6200)\n\t/usr/local/go/src/net/http/server.go:2936 +0x316\nnet/http.(*conn).serve(0xc0000962d0, {0x19253e0, 0xc00008bd40})\n\t/usr/local/go/src/net/http/server.go:1995 +0x612\ncreated by net/http.(*Server).Serve\n\t/usr/local/go/src/net/http/server.go:3089 +0x5ed\n"}

8. Prometheus Metrics

orbit support prometheus metrics, you can use EnableMetric to enable it. Here is a example to use demo service to collect demo metrics.

[!TIP] Use curl http://127.0.0.1:8080/metrics to get metrics.

Example

package main

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
)

type service struct{}

func (s *service) RegisterGroup(g *gin.RouterGroup) {
	// /demo
	g.GET("/demo", func(c *gin.Context) {
		c.String(http.StatusOK, "demo")
	})

}

func main() {
	// Create a new orbit configuration.
	config := orbit.NewConfig()

	// Create a new orbit feature options.
	opts := orbit.NewOptions().EnableMetric()

	// Create a new orbit engine.
	engine := orbit.NewEngine(config, opts)

	// Register a custom router group.
	engine.RegisterService(&service{})

	// Start the engine.
	engine.Run()

	// Wait for 30 seconds.
	time.Sleep(30 * time.Second)

	// Stop the engine.
	engine.Stop()
}

Result

# HELP orbit_http_request_latency_seconds HTTP request latencies in seconds.
# TYPE orbit_http_request_latency_seconds gauge
orbit_http_request_latency_seconds{method="GET",path="/demo",status="200"} 0
# HELP orbit_http_request_latency_seconds_histogram HTTP request latencies in seconds(Histogram).
# TYPE orbit_http_request_latency_seconds_histogram histogram
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="0.1"} 3
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="0.5"} 3
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="1"} 3
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="2"} 3
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="5"} 3
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="10"} 3
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="+Inf"} 3
orbit_http_request_latency_seconds_histogram_sum{method="GET",path="/demo",status="200"} 0
orbit_http_request_latency_seconds_histogram_count{method="GET",path="/demo",status="200"} 3

9. Repeat Read Request/Response Body

orbit support repeat read request/response body, default behavior is enable, no need to do anything. Here is a example to use demo service to repeat read request/response body.

9.1 Repeat Read Request Body

httptool.GenerateRequestBody method can help you to get request body bytes and cache it. The next time you read it, you can get the cached bytes.

[!IMPORTANT] Request body is a io.ReadCloser, it is a stream, so you can only read it once. If you want to read it again, do not directly read it, you can use orbit to cache it.

Example

package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
	"github.com/shengyanli1982/orbit/utils/httptool"
)

type service struct{}

func (s *service) RegisterGroup(g *gin.RouterGroup) {
	// /demo
	g.POST("/demo", func(c *gin.Context) {
		// Repeat the read request body content 20 times.
		for i := 0; i < 20; i++ {
			if body, err := httptool.GenerateRequestBody(c); err != nil {
				c.String(http.StatusInternalServerError, err.Error())
			} else {
				c.String(http.StatusOK, fmt.Sprintf(">> %d, %s\n", i, string(body)))
			}
		}
	})
}

func main() {
	// Create a new orbit configuration.
	config := orbit.NewConfig()

	// Create a new orbit feature options.
	opts := orbit.NewOptions()

	// Create a new orbit engine.
	engine := orbit.NewEngine(config, opts)

	// Register a custom router group.
	engine.RegisterService(&service{})

	// Start the engine.
	engine.Run()

	// Simulate a request.
	resp, _ := http.Post("http://localhost:8080/demo", "text/plain", io.NopCloser(bytes.NewBuffer([]byte("demo"))))
	defer resp.Body.Close()

	// Print the response body.
	buf := new(bytes.Buffer)
	_, _ = buf.ReadFrom(resp.Body)
	fmt.Println(buf.String())

	// Wait for 30 seconds.
	time.Sleep(30 * time.Second)

	// Stop the engine.
	engine.Stop()
}

Result

$ go run demo.go
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> github.com/shengyanli1982/orbit.healthcheckService.func1 (1 handlers)
[GIN-debug] POST   /demo                     --> main.(*service).RegisterGroup.func1 (5 handlers)
{"level":"INFO","time":"2024-01-13T10:27:37.531+0800","logger":"default","caller":"orbit/gin.go:165","message":"http server is ready","address":"127.0.0.1:8080"}
{"level":"INFO","time":"2024-01-13T10:27:37.534+0800","logger":"default","caller":"log/default.go:10","message":"http server access log","id":"","ip":"127.0.0.1","endpoint":"127.0.0.1:58618","path":"/demo","method":"POST","code":200,"status":"OK","latency":"50.508µs","agent":"Go-http-client/1.1","query":"","reqContentType":"text/plain","reqBody":""}

>> 0, demo
>> 1, demo
>> 2, demo
>> 3, demo
>> 4, demo
>> 5, demo
>> 6, demo
>> 7, demo
>> 8, demo
>> 9, demo
>> 10, demo
>> 11, demo
>> 12, demo
>> 13, demo
>> 14, demo
>> 15, demo
>> 16, demo
>> 17, demo
>> 18, demo
>> 19, demo

{"level":"INFO","time":"2024-01-13T10:28:07.537+0800","logger":"default","caller":"orbit/gin.go:190","message":"http server is shutdown","address":"127.0.0.1:8080"}
9.2 Repeat Read Response Body

httptool.GenerateResponseBody method can help you to get response body bytes from cache. Remember, you must call httptool.GenerateResponseBody after you real write response body, like c.String(http.StatusOK, "demo").

[!NOTE] Response body aways write to io.Writer, so you can not read it directly. If you want to read it, you can use orbit to cache it.

Many times httptool.GenerateResponseBody is used to custom middleware, you can use it to get response body bytes and do something.

Example

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
	"github.com/shengyanli1982/orbit/utils/httptool"
)

func customMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Next()

		// Get the response body buffer from the context
		for i := 0; i < 20; i++ {
			body, _ := httptool.GenerateResponseBody(c)
			fmt.Printf("# %d, %s\n", i, string(body))
		}
	}
}

type service struct{}

func (s *service) RegisterGroup(g *gin.RouterGroup) {
	g.GET("/demo", func(c *gin.Context) {
		c.String(http.StatusOK, "demo")
	})
}

func main() {
	// Create a new orbit configuration.
	config := orbit.NewConfig()

	// Create a new orbit feature options.
	opts := orbit.NewOptions().EnableMetric()

	// Create a new orbit engine.
	engine := orbit.NewEngine(config, opts)

	// Register a custom middleware.
	engine.RegisterMiddleware(customMiddleware())

	// Register a custom router group.
	engine.RegisterService(&service{})

	// Start the engine.
	engine.Run()

	// Simulate a request.
	resp, _ := http.Get("http://localhost:8080/demo")
	defer resp.Body.Close()

	// Wait for 30 seconds.
	time.Sleep(30 * time.Second)

	// Stop the engine.
	engine.Stop()
}

Result

$ go run demo.go
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> github.com/shengyanli1982/orbit.healthcheckService.func1 (1 handlers)
[GIN-debug] GET    /metrics                  --> github.com/shengyanli1982/orbit/utils/wrapper.WrapHandlerToGin.func1 (2 handlers)
[GIN-debug] GET    /demo                     --> main.(*service).RegisterGroup.func1 (7 handlers)
{"level":"INFO","time":"2024-01-13T10:32:25.191+0800","logger":"default","caller":"orbit/gin.go:165","message":"http server is ready","address":"127.0.0.1:8080"}
{"level":"INFO","time":"2024-01-13T10:32:25.194+0800","logger":"default","caller":"log/default.go:10","message":"http server access log","id":"","ip":"127.0.0.1","endpoint":"127.0.0.1:59139","path":"/demo","method":"GET","code":200,"status":"OK","latency":"20.326µs","agent":"Go-http-client/1.1","query":"","reqContentType":"","reqBody":""}

# 0, demo
# 1, demo
# 2, demo
# 3, demo
# 4, demo
# 5, demo
# 6, demo
# 7, demo
# 8, demo
# 9, demo
# 10, demo
# 11, demo
# 12, demo
# 13, demo
# 14, demo
# 15, demo
# 16, demo
# 17, demo
# 18, demo
# 19, demo

{"level":"INFO","time":"2024-01-13T10:32:55.195+0800","logger":"default","caller":"orbit/gin.go:190","message":"http server is shutdown","address":"127.0.0.1:8080"}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Accounts

type Accounts = gin.Accounts

type Config

type Config struct {
	Address               string `json:"address,omitempty" yaml:"address,omitempty"`                             // Address to listen on
	Port                  uint16 `json:"port,omitempty" yaml:"port,omitempty"`                                   // Port to listen on
	ReleaseMode           bool   `json:"releaseMode,omitempty" yaml:"releaseMode,omitempty"`                     // Release mode flag
	HttpReadTimeout       uint32 `json:"httpReadTimeout,omitempty" yaml:"httpReadTimeout,omitempty"`             // HTTP read timeout
	HttpWriteTimeout      uint32 `json:"httpWriteTimeout,omitempty" yaml:"httpWriteTimeout,omitempty"`           // HTTP write timeout
	HttpReadHeaderTimeout uint32 `json:"httpReadHeaderTimeout,omitempty" yaml:"httpReadHeaderTimeout,omitempty"` // HTTP read header timeout
	// contains filtered or unexported fields
}

Configuration represents the configuration for the Orbit framework.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns a new Config instance with default values.

func NewConfig

func NewConfig() *Config

NewConfig creates a new Config instance with default values.

func (*Config) WithAccessLogEventFunc

func (c *Config) WithAccessLogEventFunc(fn com.LogEventFunc) *Config

WithAccessLogEventFunc sets a new access log event function for the Config instance.

func (*Config) WithAddress

func (c *Config) WithAddress(address string) *Config

WithAddress sets a new address for the Config instance.

func (*Config) WithHttpReadHeaderTimeout

func (c *Config) WithHttpReadHeaderTimeout(timeout uint32) *Config

WithHttpReadHeaderTimeout sets a new HTTP read header timeout for the Config instance.

func (*Config) WithHttpReadTimeout

func (c *Config) WithHttpReadTimeout(timeout uint32) *Config

WithHttpReadTimeout sets a new HTTP read timeout for the Config instance.

func (*Config) WithHttpWriteTimeout

func (c *Config) WithHttpWriteTimeout(timeout uint32) *Config

WithHttpWriteTimeout sets a new HTTP write timeout for the Config instance.

func (*Config) WithLogger

func (c *Config) WithLogger(logger *zap.Logger) *Config

WithLogger sets a new logger for the Config instance.

func (*Config) WithPort

func (c *Config) WithPort(port uint16) *Config

WithPort sets a new port for the Config instance.

func (*Config) WithPrometheusRegistry

func (c *Config) WithPrometheusRegistry(registry *prometheus.Registry) *Config

WithPrometheusRegistry sets a new Prometheus registry for the Config instance.

func (*Config) WithRecoveryLogEventFunc

func (c *Config) WithRecoveryLogEventFunc(fn com.LogEventFunc) *Config

WithRecoveryLogEventFunc sets a new recovery log event function for the Config instance.

func (*Config) WithRelease

func (c *Config) WithRelease() *Config

WithRelease sets the Config instance to release mode.

func (*Config) WithSugaredLogger

func (c *Config) WithSugaredLogger(logger *zap.SugaredLogger) *Config

WithSugaredLogger sets a new sugared logger for the Config instance.

type Context

type Context = gin.Context

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine is the main struct that represents the Orbit engine.

func NewEngine

func NewEngine(config *Config, options *Options) *Engine

NewEngine creates a new instance of the Engine.

func (*Engine) GetListenEndpoint

func (e *Engine) GetListenEndpoint() string

GetListenEndpoint returns the listen endpoint of the Orbit engine.

func (*Engine) GetLogger

func (e *Engine) GetLogger() *zap.SugaredLogger

GetLogger returns the logger of the Orbit engine.

func (*Engine) GetPrometheusRegistry

func (e *Engine) GetPrometheusRegistry() *prometheus.Registry

GetPrometheusRegistry returns the Prometheus registry of the Orbit engine.

func (*Engine) IsMetricEnabled

func (e *Engine) IsMetricEnabled() bool

GetConfig returns the metric status of the Orbit engine.

func (*Engine) IsReleaseMode

func (e *Engine) IsReleaseMode() bool

GetConfig returns the running mode of the Orbit engine.

func (*Engine) IsRunning

func (e *Engine) IsRunning() bool

IsRunning returns true if the Orbit engine is running.

func (*Engine) RegisterMiddleware

func (e *Engine) RegisterMiddleware(handler gin.HandlerFunc)

RegisterMiddleware registers a middleware to the Orbit engine.

func (*Engine) RegisterService

func (e *Engine) RegisterService(service Service)

RegisterService registers a service to the Orbit engine.

func (*Engine) Run

func (e *Engine) Run()

Run starts the Orbit engine.

func (*Engine) Stop

func (e *Engine) Stop()

Stop stops the Orbit engine.

type HandlerFunc

type HandlerFunc = gin.HandlerFunc

type HandlersChain

type HandlersChain = gin.HandlersChain

type Options

type Options struct {
	// contains filtered or unexported fields
}

Options represents the configuration options for the application.

func DebugOptions

func DebugOptions() *Options

DebugOptions returns the debug options.

func NewOptions

func NewOptions() *Options

NewOptions creates a new instance of Options.

func ReleaseOptions

func ReleaseOptions() *Options

ReleaseOptions returns the release options.

func (*Options) EnableForwardedByClientIp

func (o *Options) EnableForwardedByClientIp() *Options

EnableForwardedByClientIp enables the client IP forwarding.

func (*Options) EnableMetric

func (o *Options) EnableMetric() *Options

EnableMetric enables the metric collection.

func (*Options) EnablePProf

func (o *Options) EnablePProf() *Options

EnablePProf enables the pprof endpoint.

func (*Options) EnableRecordRequestBody

func (o *Options) EnableRecordRequestBody() *Options

EnableRecordRequestBody enables the recording request body.

func (*Options) EnableRedirectFixedPath

func (o *Options) EnableRedirectFixedPath() *Options

EnableRedirectFixedPath enables the fixed path redirection.

func (*Options) EnableRedirectTrailingSlash

func (o *Options) EnableRedirectTrailingSlash() *Options

EnableRedirectTrailingSlash enables the trailing slash redirection.

func (*Options) EnableSwagger

func (o *Options) EnableSwagger() *Options

EnableSwagger enables the swagger documentation.

type RouterGroup

type RouterGroup = gin.RouterGroup

type Service

type Service interface {
	RegisterGroup(routerGroup *gin.RouterGroup) // Register the service to the router group
}

Service is the interface that represents a service.

type WrapRegisterService

type WrapRegisterService struct {
	// contains filtered or unexported fields
}

WrapRegisterService is a wrapper for the service registration function.

func NewHttpService

func NewHttpService(registerFunc func(*gin.RouterGroup)) *WrapRegisterService

NewHttpService creates a new instance of the WrapRegisterService.

func (*WrapRegisterService) RegisterGroup

func (w *WrapRegisterService) RegisterGroup(group *gin.RouterGroup)

RegisterGroup registers the service to the given router group.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL