Skip to content

Commit

Permalink
Merge pull request #27 from ybkuroki/logrotation
Browse files Browse the repository at this point in the history
Enable log rotation
  • Loading branch information
ybkuroki authored Nov 15, 2020
2 parents 68fcb61 + 676e853 commit f533995
Show file tree
Hide file tree
Showing 12 changed files with 309 additions and 178 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ The follwing figure is the map of this sample project.
- go-webapp-sample
+ config … Define configurations of this system.
+ logger … Provide loggers.
+ middleware … Define custom middleware.
+ migration … Provide database migration service for development.
+ router … Define routing.
+ controller … Define controllers.
Expand Down Expand Up @@ -100,6 +101,7 @@ This sample uses the following libraries.
|Echo|4.1.16|
|Gorm|1.9.12|
|go-playground/validator.v9|9.31.0|
|Zap/logger|1.16.0|
## License
The License of this sample is *MIT License*.
4 changes: 3 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"flag"
"fmt"
"os"

"github.com/jinzhu/configor"
)
Expand Down Expand Up @@ -57,7 +58,8 @@ func Load() {
flag.Parse()
config = &Config{}
if err := configor.Load(config, "application."+*env+".yml"); err != nil {
fmt.Println(err)
fmt.Printf("Failed to read application.%s.yml: %s", *env, err)
os.Exit(2)
}
}

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ require (
gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v9 v9.31.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.2.8
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXa
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M=
gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
118 changes: 118 additions & 0 deletions logger/gormlogger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package logger

import (
"database/sql/driver"
"fmt"
"reflect"
"regexp"
"time"
"unicode"
)

// ==============================================================
// Customize SQL Logger for gorm library
// ref: https://github.com/wantedly/gorm-zap
// ref: https://github.com/jinzhu/gorm/blob/master/logger.go
// ===============================================================

// Print passes arguments to Println
func (l *Logger) Print(values ...interface{}) {
l.Println(values)
}

// Println format & print log
func (l *Logger) Println(values []interface{}) {
sql := createLog(values)
if sql != "" {
l.zap.Debugf(sql)
}
}

// createLog returns log for output
func createLog(values []interface{}) string {
ret := ""

if len(values) > 1 {
var level = values[0]

if level == "sql" {
ret = "[gorm] : " + createSQL(values[3].(string), getFormattedValues(values))
}
}

return ret
}

func isPrintable(s string) bool {
for _, r := range s {
if !unicode.IsPrint(r) {
return false
}
}
return true
}

// getFormattedValues returns values of a SQL statement.
func getFormattedValues(values []interface{}) []string {
var formattedValues []string
for _, value := range values[4].([]interface{}) {
indirectValue := reflect.Indirect(reflect.ValueOf(value))
if indirectValue.IsValid() {
value = indirectValue.Interface()
if t, ok := value.(time.Time); ok {
if t.IsZero() {
formattedValues = append(formattedValues, fmt.Sprintf("'%v'", "0000-00-00 00:00:00"))
} else {
formattedValues = append(formattedValues, fmt.Sprintf("'%v'", t.Format("2006-01-02 15:04:05")))
}
} else if b, ok := value.([]byte); ok {
if str := string(b); isPrintable(str) {
formattedValues = append(formattedValues, fmt.Sprintf("'%v'", str))
} else {
formattedValues = append(formattedValues, "'<binary>'")
}
} else if r, ok := value.(driver.Valuer); ok {
if value, err := r.Value(); err == nil && value != nil {
formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
} else {
formattedValues = append(formattedValues, "NULL")
}
} else {
switch value.(type) {
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, bool:
formattedValues = append(formattedValues, fmt.Sprintf("%v", value))
default:
formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
}
}
} else {
formattedValues = append(formattedValues, "NULL")
}
}
return formattedValues
}

// createSQL returns complete SQL with values bound to a SQL statement.
func createSQL(sql string, values []string) string {
var (
sqlRegexp = regexp.MustCompile(`\?`)
numericPlaceHolderRegexp = regexp.MustCompile(`\$\d+`)
result = ""
)
// differentiate between $n placeholders or else treat like ?
if numericPlaceHolderRegexp.MatchString(sql) {
for index, value := range values {
placeholder := fmt.Sprintf(`\$%d([^\d]|$)`, index+1)
result = regexp.MustCompile(placeholder).ReplaceAllString(sql, value+"$1")
}
} else {
formattedValuesLength := len(values)
for index, value := range sqlRegexp.Split(sql, -1) {
result += value
if index < formattedValuesLength {
result += values[index]
}
}
}
return result
}
134 changes: 16 additions & 118 deletions logger/logger.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
package logger

import (
"database/sql/driver"
"fmt"
"io/ioutil"
"reflect"
"regexp"
"time"
"unicode"
"os"

"github.com/ybkuroki/go-webapp-sample/config"
"go.uber.org/zap"
"gopkg.in/natefinch/lumberjack.v2"
"gopkg.in/yaml.v2"
)

var logger *Logger

// Config is
type Config struct {
ZapConfig zap.Config `json:"zap_config" yaml:"zap_config"`
LogRotate lumberjack.Logger `json:"log_rotate" yaml:"log_rotate"`
}

// Logger is an alternative implementation of *gorm.Logger
type Logger struct {
zap *zap.SugaredLogger
Expand All @@ -36,7 +39,7 @@ func GetZapLogger() *zap.SugaredLogger {
return logger.zap
}

// newLogger create logger object for *gorm.DB from *echo.Logger
// NewLogger create logger object for *gorm.DB from *echo.Logger
func NewLogger(zap *zap.SugaredLogger) *Logger {
return &Logger{zap: zap}
}
Expand All @@ -45,127 +48,22 @@ func NewLogger(zap *zap.SugaredLogger) *Logger {
func InitLogger() {
configYaml, err := ioutil.ReadFile("./zaplogger." + *config.GetEnv() + ".yml")
if err != nil {
fmt.Printf("Failed to read zap logger configuration: %s", err)
fmt.Printf("Failed to read logger configuration: %s", err)
os.Exit(2)
}
var myConfig zap.Config
var myConfig *Config
if err := yaml.Unmarshal(configYaml, &myConfig); err != nil {
fmt.Printf("Failed to read zap logger configuration: %s", err)
os.Exit(2)
}
zap, err := myConfig.Build()
zap, err := build(myConfig)
if err != nil {
fmt.Printf("Error")
fmt.Printf("Failed to compose zap logger : %s", err)
os.Exit(2)
}
sugar := zap.Sugar()
// set package varriable logger.
logger = NewLogger(sugar)
logger.zap.Infof("Success to read zap logger configuration: zaplogger." + *config.GetEnv() + ".yml")
_ = zap.Sync()
}

// ==============================================================
// Customize SQL Logger for gorm library
// ref: https://github.com/wantedly/gorm-zap
// ref: https://github.com/jinzhu/gorm/blob/master/logger.go
// ===============================================================

// Print passes arguments to Println
func (l *Logger) Print(values ...interface{}) {
l.Println(values)
}

// Println format & print log
func (l *Logger) Println(values []interface{}) {
sql := createLog(values)
if sql != "" {
l.zap.Debugf(sql)
}
}

// createLog returns log for output
func createLog(values []interface{}) string {
ret := ""

if len(values) > 1 {
var level = values[0]

if level == "sql" {
ret = "[gorm] : " + createSQL(values[3].(string), getFormattedValues(values))
}
}

return ret
}

func isPrintable(s string) bool {
for _, r := range s {
if !unicode.IsPrint(r) {
return false
}
}
return true
}

// getFormattedValues returns values of a SQL statement.
func getFormattedValues(values []interface{}) []string {
var formattedValues []string
for _, value := range values[4].([]interface{}) {
indirectValue := reflect.Indirect(reflect.ValueOf(value))
if indirectValue.IsValid() {
value = indirectValue.Interface()
if t, ok := value.(time.Time); ok {
if t.IsZero() {
formattedValues = append(formattedValues, fmt.Sprintf("'%v'", "0000-00-00 00:00:00"))
} else {
formattedValues = append(formattedValues, fmt.Sprintf("'%v'", t.Format("2006-01-02 15:04:05")))
}
} else if b, ok := value.([]byte); ok {
if str := string(b); isPrintable(str) {
formattedValues = append(formattedValues, fmt.Sprintf("'%v'", str))
} else {
formattedValues = append(formattedValues, "'<binary>'")
}
} else if r, ok := value.(driver.Valuer); ok {
if value, err := r.Value(); err == nil && value != nil {
formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
} else {
formattedValues = append(formattedValues, "NULL")
}
} else {
switch value.(type) {
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, bool:
formattedValues = append(formattedValues, fmt.Sprintf("%v", value))
default:
formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
}
}
} else {
formattedValues = append(formattedValues, "NULL")
}
}
return formattedValues
}

// createSQL returns complete SQL with values bound to a SQL statement.
func createSQL(sql string, values []string) string {
var (
sqlRegexp = regexp.MustCompile(`\?`)
numericPlaceHolderRegexp = regexp.MustCompile(`\$\d+`)
result = ""
)
// differentiate between $n placeholders or else treat like ?
if numericPlaceHolderRegexp.MatchString(sql) {
for index, value := range values {
placeholder := fmt.Sprintf(`\$%d([^\d]|$)`, index+1)
result = regexp.MustCompile(placeholder).ReplaceAllString(sql, value+"$1")
}
} else {
formattedValuesLength := len(values)
for index, value := range sqlRegexp.Split(sql, -1) {
result += value
if index < formattedValuesLength {
result += values[index]
}
}
}
return result
}
Loading

0 comments on commit f533995

Please sign in to comment.