You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
3.6 KiB

package utility
import (
"database/sql"
"io"
"os"
"time"
"strings"
"github.com/ipsusila/opt"
)
//SQLWriter write log to database i.e. SQLite
type SQLWriter struct {
db *sql.DB
insertQuery string
createQuery string
appID string
}
//LogWriter support multiple output
type LogWriter struct {
writers []io.WriteCloser
console bool
}
//NewLogWriter create multiple writer
func NewLogWriter(options *opt.Options) (*LogWriter, error) {
logOpt := options.Get("log")
//backward compatibility, get from server
appID := options.Get("server").GetString("id", "")
if appID == "" {
//if failed, get from `log.id`
appID = logOpt.GetString("id", "")
}
if appID == "" {
//if failed, get from `id``
appID = options.GetString("id", "*undefined*")
}
outTypes := logOpt.GetString("type", "")
typeItems := strings.Split(outTypes, ",")
writers := []io.WriteCloser{}
console := false
for _, item := range typeItems {
item = strings.TrimSpace(item)
switch item {
case "console":
console = true
case "file":
fileOpt := logOpt.Get("file")
name := fileOpt.GetString("name", "")
appendExisting := fileOpt.GetBool("append", true)
flag := os.O_WRONLY | os.O_CREATE
if appendExisting {
flag |= os.O_APPEND
} else {
flag |= os.O_TRUNC
}
f, err := os.OpenFile(name, flag, 0666)
if err != nil {
return nil, err
}
writers = append(writers, f)
case "sql":
w, err := newSQLWriter(logOpt, appID)
if err != nil {
return nil, err
}
writers = append(writers, w)
}
}
lw := &LogWriter{
writers: writers,
console: console,
}
return lw, nil
}
//NewSQLWriter open log writer to sql
func newSQLWriter(logOpt *opt.Options, appID string) (*SQLWriter, error) {
sqlOpt := logOpt.Get("sql")
dbDriver := sqlOpt.GetString("driver", "")
dbInfo := sqlOpt.GetString("connection", "")
createQuery := sqlOpt.GetString("createQuery", "")
insertQuery := sqlOpt.GetString("insertQuery", "")
dbMaxIdle := sqlOpt.GetInt("maxIdle", 0)
dbMaxOpen := sqlOpt.GetInt("maxOpen", 5)
dbMaxLifetime := time.Duration(sqlOpt.GetInt("maxLifetime", 60)) * time.Second
//open databae connection
db, err := sql.Open(dbDriver, dbInfo)
if err != nil {
return nil, err
}
err = db.Ping()
if err != nil {
db.Close()
return nil, err
}
//connection pooling
db.SetMaxIdleConns(dbMaxIdle)
db.SetMaxOpenConns(dbMaxOpen)
db.SetConnMaxLifetime(dbMaxLifetime)
//ignore error
//TODO, verify query (security, untrusted source)
db.Exec(createQuery)
w := &SQLWriter{
db: db,
createQuery: createQuery,
insertQuery: insertQuery,
appID: appID,
}
return w, nil
}
//Write to multiple log
func (lw *LogWriter) Write(data []byte) (n int, err error) {
//now := time.Now().Format("[2006-01-02 15:04:05]")
if lw.console {
n, err = os.Stderr.Write(data)
}
for _, w := range lw.writers {
n, err = w.Write(data)
}
return n, err
}
//Close all writer
func (lw *LogWriter) Close() error {
for _, w := range lw.writers {
w.Close()
}
return nil
}
//Write data with timestamp (ts, log)
func (w *SQLWriter) Write(data []byte) (int, error) {
//TODO, verify query (security, untrusted source)
result, err := w.db.Exec(w.insertQuery, time.Now(), w.appID, string(data))
if err != nil {
return 0, err
}
n := len(data)
_, err = result.LastInsertId()
if err != nil {
return n, err
}
nrows, err := result.RowsAffected()
if err != nil || nrows == 0 {
return n, err
}
return n, nil
}
func (w *SQLWriter) Close() error {
return w.db.Close()
}