orange framework first commit

This commit is contained in:
zhucheer 2019-10-23 11:39:48 +08:00
commit 1782c67b47
18 changed files with 732 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/.idea

75
app/app.go Normal file
View File

@ -0,0 +1,75 @@
package app
import (
"context"
"fmt"
"gitee.com/zhucheer/orange/cfg"
"gitee.com/zhucheer/orange/logger"
"net/http"
)
// ExampleHTTPServer a example http server
type AppSrv interface {
ServeMux()
}
func AppStart(appSrv AppSrv) {
consoleWelCome()
appSrv.ServeMux()
appAddr := cfg.Config.GetString("app.http_addr")
appPort := cfg.Config.GetInt("app.http_port")
bindAddr := fmt.Sprintf("%s:%d", appAddr, appPort)
httpApiMux := http.NewServeMux()
for _, item := range routers {
httpApiMux.HandleFunc(item.patten, handlerFunc(item))
consoleRouter(item.method, item.patten)
}
fmt.Println(fmt.Sprintf("[ORANGE] \033[0;33m start http server bind %v \033[0m ", bindAddr))
logger.Info("app start listen %s for http server.", bindAddr)
server := &http.Server{
Addr: bindAddr,
Handler: httpApiMux,
}
server.ListenAndServe()
}
// 处理HTTP方法
func handlerFunc(node routerNode) func(writer http.ResponseWriter, request *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) {
method := request.Method
if node.method != "ALL" && node.method != method {
writer.WriteHeader(http.StatusNotFound)
writer.Write([]byte("Not Found"))
} else {
ctx := NewCtx(context.Background(), writer, request)
// 中间件依次调用
var middleHandlerFunc HandlerFunc = node.appHandler
for _, item := range node.middlewares {
middleFunc := item.Func()
middleHandlerFunc = middleFunc(middleHandlerFunc)
}
middleHandlerFunc(ctx)
}
}
}
// 输出已注册路由方法到控制台
func consoleRouter(method, patten string) {
fmt.Println(fmt.Sprintf("[ORANGE] \033[0;33m %s \033[0m %s", method, patten))
}
// 输出已注册路由方法到控制台
func consoleWelCome() {
console := `
//////////////////////////////////////////
//////// ORANGE FRAMEWORK /////////
//////////////////////////////////////////
`
fmt.Println(fmt.Sprintf("\033[0;33m %v \033[0m", console))
}

51
app/context.go Normal file
View File

@ -0,0 +1,51 @@
package app
import (
"context"
"encoding/json"
"net/http"
)
type Context struct {
response http.ResponseWriter
request *http.Request
ctx context.Context
}
func NewCtx(ctx context.Context, w http.ResponseWriter, r *http.Request) *Context {
return &Context{
ctx: ctx,
response: w,
request: r,
}
}
func (c *Context) SetRW(w http.ResponseWriter, r *http.Request) {
c.response = w
c.request = r
}
func (c *Context) Response() http.ResponseWriter {
return c.response
}
func (c *Context) Request() *http.Request {
return c.request
}
func (c *Context) Context() context.Context {
return c.ctx
}
func (c *Context) JSON(data interface{}) error {
c.response.Header().Set("Content-Type", "application/json;charset=UTF-8")
jsonBytes, err := json.Marshal(data)
if err != nil {
c.response.WriteHeader(500)
c.response.Write([]byte(err.Error()))
return err
}
c.response.Write(jsonBytes)
return nil
}

9
app/init.go Normal file
View File

@ -0,0 +1,9 @@
package app
import (
_ "gitee.com/zhucheer/orange/cfg"
)
func Star() {
}

19
app/middleware.go Normal file
View File

@ -0,0 +1,19 @@
package app
// MiddlewareFunc defines a function to process middleware.
type MiddlewareFunc func(HandlerFunc) HandlerFunc
type HandlerFunc func(*Context) error
type MiddleWare interface {
Func() MiddlewareFunc
}
// 翻转中间件,因执行时是从最外层函数开始,定义时需要从最后一个中间件开始定义
func reverseMiddleWare(middlewares []MiddleWare) []MiddleWare {
length := len(middlewares)
for i := 0; i < length/2; i++ {
middlewares[length-1-i], middlewares[i] = middlewares[i], middlewares[length-1-i]
}
return middlewares
}

60
app/route.go Normal file
View File

@ -0,0 +1,60 @@
package app
import "sync"
type RouterServer struct {
prefix string
middlewares []MiddleWare
lock sync.RWMutex
}
type routerNode struct {
patten string
method string
middlewares []MiddleWare
appHandler func(*Context) error
}
var routers []routerNode
func GroupRouter(prefix string, middlewares ...MiddleWare) *RouterServer {
middlewares = reverseMiddleWare(middlewares)
return &RouterServer{
prefix: prefix,
middlewares: middlewares,
}
}
// GET 注册 get 请求
func (r *RouterServer) GET(patten string, handler func(ctx *Context) error) {
r.appendRoute("GET", patten, handler)
}
// POST 注册 post 请求
func (r *RouterServer) POST(patten string, handler func(ctx *Context) error) {
r.appendRoute("POST", patten, handler)
}
// PUT 注册 put 请求
func (r *RouterServer) PUT(patten string, handler func(ctx *Context) error) {
r.appendRoute("PUT", patten, handler)
}
// DELETE 注册 delete 请求
func (r *RouterServer) DELETE(patten string, handler func(ctx *Context) error) {
r.appendRoute("DELETE", patten, handler)
}
// ALL 兼容所有请求
func (r *RouterServer) ALL(patten string, handler func(ctx *Context) error) {
r.appendRoute("ALL", patten, handler)
}
// appendRoute 添加路由节点
func (r *RouterServer) appendRoute(method string, patten string, handler func(ctx *Context) error) {
r.lock.Lock()
defer r.lock.Unlock()
routers = append(routers, routerNode{r.prefix + patten, method, r.middlewares, handler})
}

18
cfg/init.go Normal file
View File

@ -0,0 +1,18 @@
package cfg
import (
"flag"
"gitee.com/zhucheer/cfg"
"log"
)
var Config = cfg.New("./config/config.toml")
func init() {
log.Printf("init flag")
path := flag.String("config", "./config/config.toml", "config file path")
flag.Parse()
Config = cfg.New(*path)
}

12
config/config.toml Normal file
View File

@ -0,0 +1,12 @@
[app]
name = "orange"
logger_level = "info"
http_addr = "127.0.0.1"
http_port = 8088
[database]
addr = "127.0.0.1"
username = "root"
password = "123456"
dbname = "orange"
port = 3306
connection_max = 200

5
init.go Normal file
View File

@ -0,0 +1,5 @@
package main
func init() {
}

45
logger/format.go Normal file
View File

@ -0,0 +1,45 @@
package logger
import (
"bytes"
"encoding/json"
"strings"
"text/template"
"time"
)
// Message log message struct
type Message struct {
FormatType string
ContentFormat string
TimeFormat string
}
// GetMessage, return formatted message string for output.
func (formatter *Message) GetMessage(logger *Logger) string {
// init format string
if formatter.TimeFormat == "" {
formatter.TimeFormat = time.RFC1123
}
if formatter.ContentFormat == "" {
formatter.ContentFormat = `{{.Color}}{{.LevelString}} [{{.Time}}] {{.Path}}({{.Line}})
{{.Message}} {{.ColorClear}}`
}
logBuffer := new(bytes.Buffer)
if formatter.FormatType == JsonType {
jsonByte, _ := json.Marshal(logger.Record)
logBuffer.Write(jsonByte)
} else {
logger.Record.Time = time.Now().Format(formatter.TimeFormat)
tpl := template.Must(template.New("messageFormat").Parse(formatter.ContentFormat))
tpl.Execute(logBuffer, *logger.Record)
}
message := logBuffer.String()
if strings.Index(message, "\n") != len(message)-1 {
message += "\n"
}
return message
}

51
logger/init.go Normal file
View File

@ -0,0 +1,51 @@
package logger
var logFacede *Logger
// 初始化logger快速方法
func init() {
logFacede = New(DEBUG, TextType, "", 0)
}
// Debug, record DEBUG message.
func Debug(format string, a ...interface{}) {
checkLogNil()
logFacede.Debug(format, a...)
}
// Info, record INFO message.
func Info(format string, a ...interface{}) {
checkLogNil()
logFacede.Info(format, a...)
}
// Notice, record INFO message.
func Notice(format string, a ...interface{}) {
checkLogNil()
logFacede.Notice(format, a...)
}
// Warning, record WARNING message.
func Warning(format string, a ...interface{}) {
checkLogNil()
logFacede.Warning(format, a...)
}
// Error, record ERROR message.
func Error(format string, a ...interface{}) {
checkLogNil()
logFacede.Error(format, a...)
}
// Critical, record CRITICAL message.
func Critical(format string, a ...interface{}) {
checkLogNil()
logFacede.Critical(format, a...)
}
// checkLogNil check log should not empty
func checkLogNil() {
if logFacede == nil {
panic("log is nil, init log first")
}
}

141
logger/log.go Normal file
View File

@ -0,0 +1,141 @@
package logger
import (
"fmt"
"time"
)
type LogLevel int
const LevelColorSetClear = "\033[0m"
const (
// different colors log text:
_ = iota + 30 // black
ColorRed // red
ColorGreen // green
ColorYellow // yellow
ColorBlue // blue
ColorMagenta // magenta
_ // cyan
ColorWhite // white
)
// LogType
const (
TextType = "text"
JsonType = "json"
)
// MessageLevel
const (
NOTSET = iota
DEBUG = LogLevel(10 * iota) // DEBUG = 10
INFO = LogLevel(10 * iota) // INFO = 20
NOTICE = LogLevel(10 * iota) // INFO = 30
WARNING = LogLevel(10 * iota) // WARNING = 40
ERROR = LogLevel(10 * iota) // ERROR = 50
CRITICAL = LogLevel(10 * iota) // CRITICAL = 60
)
// LevelColorFlag, MessageLevel color flag.
var LevelColorFlag = []string{
DEBUG: colorSet(ColorBlue, 0),
INFO: colorSet(ColorGreen, 0),
NOTICE: colorSet(ColorWhite, 0),
WARNING: colorSet(ColorYellow, 0),
ERROR: colorSet(ColorRed, 1),
CRITICAL: colorSet(ColorMagenta, 1),
}
var LevelString = map[LogLevel]string{
DEBUG: "DEBUG",
INFO: "INFO",
NOTICE: "NOTICE",
WARNING: "WARNING",
ERROR: "ERROR",
CRITICAL: "CRITICAL",
}
func colorSet(l LogLevel, way int) string {
return fmt.Sprintf("\033[%d;%dm", way, LogLevel(l))
}
// Logger main logger struct
type Logger struct {
Level LogLevel
Record *Record
Format *Message
StreamHandler *StreamMessageHandler
SyncInterval int
}
// New get logger struct
func New(level LogLevel, logType string, logPath string, syncInterval int) *Logger {
stream := &StreamMessageHandler{
LogPath: logPath,
}
if syncInterval > 0 {
go func() {
for {
stream.SyncLog()
time.Sleep(time.Duration(syncInterval) * time.Second)
}
}()
}
return &Logger{
Level: level,
Format: &Message{
FormatType: logType,
},
StreamHandler: stream,
SyncInterval: syncInterval,
}
}
// log, sed message to handler.
func (l *Logger) log(level LogLevel, format string, a ...interface{}) {
if level >= l.Level {
l.Record = GetMessageRecord(level, format, a...)
if l.StreamHandler != nil {
l.StreamHandler.Write([]byte(l.Format.GetMessage(l)))
if l.SyncInterval == 0 {
l.StreamHandler.SyncLog()
}
}
}
}
// Debug, record DEBUG message.
func (l *Logger) Debug(format string, a ...interface{}) {
l.log(DEBUG, format, a...)
}
// Info, record INFO message.
func (l *Logger) Info(format string, a ...interface{}) {
l.log(INFO, format, a...)
}
// Notice, record INFO message.
func (l *Logger) Notice(format string, a ...interface{}) {
l.log(NOTICE, format, a...)
}
// Warning, record WARNING message.
func (l *Logger) Warning(format string, a ...interface{}) {
l.log(WARNING, format, a...)
}
// Error, record ERROR message.
func (l *Logger) Error(format string, a ...interface{}) {
l.log(ERROR, format, a...)
}
// Critical, record CRITICAL message.
func (l *Logger) Critical(format string, a ...interface{}) {
l.log(CRITICAL, format, a...)
}

76
logger/logger_test.go Normal file
View File

@ -0,0 +1,76 @@
package logger
import (
"os"
"testing"
"time"
)
func TestLog(t *testing.T) {
logFile := "./stream.log"
defer func() {
os.Remove(logFile)
}()
logger := New(DEBUG, JsonType, logFile, 0)
logger.Debug("test log %s", "xxxx")
if fileExists(logFile) == false {
t.Error("log write file error")
}
}
func TestLogConsole(t *testing.T) {
logger := New(DEBUG, TextType, "", 1)
logger.Debug("test Debug")
logger.Info("test Info")
logger.Notice("test Notice")
logger.Warning("test Warning")
logger.Error("test Error")
logger.Critical("test Critical")
time.Sleep(1 * time.Second)
}
func TestLogNil(t *testing.T) {
logFacede = nil
defer func() {
err := recover()
if err == nil {
t.Error("checkLogNil func have an err")
}
}()
Debug("test log nil")
}
func TestLogFacede(t *testing.T) {
logFacede = New(DEBUG, JsonType, "", 1)
Debug("test Debug")
Info("test Info")
Notice("test Notice")
Warning("test Warning")
Error("test Error")
Critical("test Critical")
time.Sleep(1 * time.Second)
}
// 判断所给路径文件/文件夹是否存在
func fileExists(path string) bool {
_, err := os.Stat(path) //os.Stat获取文件信息
if err != nil {
if os.IsExist(err) {
return true
}
return false
}
return true
}

45
logger/record.go Normal file
View File

@ -0,0 +1,45 @@
package logger
import (
"fmt"
"os"
"path/filepath"
"runtime"
)
// Record
type Record struct {
Level LogLevel `json:"-"`
LevelString string `json:"level"`
Message string `json:"message"`
Pid int `json:"-"`
Program string `json:"program"`
Time string `json:"time"`
FuncName string `json:"func_name"`
Path string `json:"path"`
FileName string `json:"file_name"`
Line int `json:"line"`
Color string `json:"-"`
ColorClear string `json:"-"`
}
// GetMessageRecord, make a record and return it's reference.
func GetMessageRecord(level LogLevel, format string, a ...interface{}) *Record {
message := fmt.Sprintf(format, a...)
pc, file, line, _ := runtime.Caller(4)
record := &Record{
Level: level,
Message: message,
Pid: os.Getpid(),
Program: filepath.Base(os.Args[0]),
Time: "",
FuncName: runtime.FuncForPC(pc).Name(),
Path: file,
FileName: filepath.Base(file),
Line: line,
Color: LevelColorFlag[level],
ColorClear: LevelColorSetClear,
LevelString: LevelString[level],
}
return record
}

39
logger/stream.go Normal file
View File

@ -0,0 +1,39 @@
package logger
import (
"bytes"
"fmt"
"os"
)
// StreamMessageHandler
type StreamMessageHandler struct {
LogPath string
LogBuffer bytes.Buffer
}
// Write write log to buffer
func (handler *StreamMessageHandler) Write(message []byte) {
handler.LogBuffer.Write(message)
}
// SyncLog interval sync log stream buffer
func (handler *StreamMessageHandler) SyncLog() {
var logFile *os.File
if handler.LogPath != "" {
logFileTmp, err := os.OpenFile(handler.LogPath, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
if err != nil {
panic(fmt.Sprintf("log file create error: %v", err))
}
logFile = logFileTmp
defer logFile.Close()
} else {
logFile = os.Stdout
}
if handler.LogBuffer.Len() > 0 {
message := handler.LogBuffer.Bytes()
logFile.Write(message)
handler.LogBuffer.Reset()
}
}

15
main.go Normal file
View File

@ -0,0 +1,15 @@
package main
import (
"gitee.com/zhucheer/orange/app"
"gitee.com/zhucheer/orange/logger"
"gitee.com/zhucheer/orange/outapp"
)
func main() {
logger.Info("orange framework start")
router := &outapp.Route{}
app.AppStart(router)
}

22
outapp/app.go Normal file
View File

@ -0,0 +1,22 @@
package outapp
import "gitee.com/zhucheer/orange/app"
type Route struct {
}
func (s *Route) ServeMux() {
mm := app.GroupRouter("/api")
{
mm.GET("/hello", func(ctx *app.Context) error {
return ctx.JSON(map[string]interface{}{"data": "123"})
})
mm.GET("/ttc", func(ctx *app.Context) error {
return ctx.JSON(map[string]interface{}{"data": "ttc"})
})
}
}

48
outapp/mdd.go Normal file
View File

@ -0,0 +1,48 @@
package outapp
import (
"fmt"
"gitee.com/zhucheer/orange/app"
)
type Auth struct {
}
// Func implements Middleware interface.
func (w Auth) Func() app.MiddlewareFunc {
return func(next app.HandlerFunc) app.HandlerFunc {
return func(c *app.Context) error {
fmt.Println("return HandlerFunc111111111=========>")
return next(c)
}
}
}
func NewAuth() *Auth {
fmt.Println("auth middleware")
return &Auth{}
}
type Auth2 struct {
}
// Func implements Middleware interface.
func (w Auth2) Func() app.MiddlewareFunc {
return func(next app.HandlerFunc) app.HandlerFunc {
return func(c *app.Context) error {
fmt.Println("return HandlerFunc Auth222=========>")
return next(c)
}
}
}
func NewAuth2() *Auth2 {
fmt.Println("auth222 middleware")
return &Auth2{}
}