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
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() |
|
}
|
|
|