123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- // Copyright 2013 Ardan Studios. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE handle.
- /*
- Read the following blog post for more information:
- http://www.goinggo.net/2013/11/using-log-package-in-go.html
- Example Code:
- package main
- import (
- "fmt"
- "github.com/finapps/tracelog"
- )
- func main() {
- //tracelog.StartFile(tracelog.LevelTrace, "/Users/bill/Temp/logs", 1)
- tracelog.Start(tracelog.LevelTrace)
- tracelog.Trace("main", "main", "Hello Trace")
- tracelog.Info("main", "main", "Hello Info")
- tracelog.Warning("main", "main", "Hello Warn")
- tracelog.Errorf(fmt.Errorf("Exception At..."), "main", "main", "Hello Error")
- Example()
- tracelog.Stop()
- }
- func Example() {
- tracelog.Started("main", "Example")
- err := foo()
- if err != nil {
- tracelog.CompletedError(err, "main", "Example")
- return
- }
- tracelog.Completed("main", "Example")
- }
- Output:
- TRACE: 2013/11/07 08:24:32 main.go:12: main : main : Info : Hello Trace
- INFO: 2013/11/07 08:24:32 main.go:13: main : main : Info : Hello Info
- WARNING: 2013/11/07 08:24:32 main.go:14: main : main : Info : Hello Warn
- ERROR: 2013/11/07 08:24:32 main.go:15: main : main : Info : Hello Error : Exception At...
- TRACE: 2013/11/07 08:24:32 main.go:23: main : Example : Started
- TRACE: 2013/11/07 08:24:32 main.go:31: main : Example : Completed
- TRACE: 2013/11/07 08:24:32 tracelog.go:149: main : Stop : Started
- TRACE: 2013/11/07 08:24:32 tracelog.go:156: main : Stop : Completed
- */
- // Package tracelog implements a logging system to trace all aspect of your code. This is great for task oriented programs.
- // Based on the Go log standard library. It provides 4 destinations with logging levels plus you can attach a file for persistent
- // writes. A log clean process is provided to maintain disk space. There is also email support to send email alerts.
- package tracelog
- import (
- "bytes"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net/smtp"
- "os"
- "runtime"
- "strconv"
- "strings"
- "sync/atomic"
- "text/template"
- "time"
- )
- const systemAlertSubject = "TraceLog Exception"
- const (
- // LevelTrace logs everything
- LevelTrace int32 = 1
- // LevelInfo logs Info, Warnings and Errors
- LevelInfo int32 = 2
- // LevelWarn logs Warning and Errors
- LevelWarn int32 = 4
- // LevelError logs just Errors
- LevelError int32 = 8
- )
- // emailConfiguration contains configuration information required by the ConfigureEmailAlerts function.
- type emailConfiguration struct {
- Host string
- Port int
- UserName string
- Password string
- To []string
- Auth smtp.Auth
- Template *template.Template
- }
- // traceLog provides support to write to log files.
- type traceLog struct {
- LogLevel int32
- EmailConfiguration *emailConfiguration
- Trace *log.Logger
- Info *log.Logger
- Warning *log.Logger
- Error *log.Logger
- File *log.Logger
- LogFile *os.File
- }
- // log maintains a pointer to a singleton for the logging system.
- var logger traceLog
- // Called to init the logging system.
- func init() {
- log.SetPrefix("TRACE: ")
- log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
- }
- // Start initializes tracelog and only displays the specified logging level.
- func Start(logLevel int32) {
- turnOnLogging(logLevel, nil)
- }
- // StartFile initializes tracelog and only displays the specified logging level
- // and creates a file to capture writes.
- func StartFile(logLevel int32, baseFilePath string, daysToKeep int) {
- baseFilePath = strings.TrimRight(baseFilePath, "/")
- currentDate := time.Now().UTC()
- dateDirectory := time.Now().UTC().Format("2006-01-02")
- dateFile := currentDate.Format("2006-01-02T15-04-05")
- filePath := fmt.Sprintf("%s/%s/", baseFilePath, dateDirectory)
- fileName := strings.Replace(fmt.Sprintf("%s.txt", dateFile), " ", "-", -1)
- err := os.MkdirAll(filePath, os.ModePerm)
- if err != nil {
- log.Fatalf("main : Start : Failed to Create log directory : %s : %s\n", filePath, err)
- }
- logf, err := os.Create(fmt.Sprintf("%s%s", filePath, fileName))
- if err != nil {
- log.Fatalf("main : Start : Failed to Create log file : %s : %s\n", fileName, err)
- }
- // Turn the logging on
- turnOnLogging(logLevel, logf)
- // Cleanup any existing directories
- logger.LogDirectoryCleanup(baseFilePath, daysToKeep)
- }
- // Stop will release resources and shutdown all processing.
- func Stop() error {
- Started("main", "Stop")
- var err error
- if logger.LogFile != nil {
- Trace("main", "Stop", "Closing File")
- err = logger.LogFile.Close()
- }
- Completed("main", "Stop")
- return err
- }
- // ConfigureEmail configures the email system for use.
- func ConfigureEmail(host string, port int, userName string, password string, to []string) {
- logger.EmailConfiguration = &emailConfiguration{
- Host: host,
- Port: port,
- UserName: userName,
- Password: password,
- To: to,
- Auth: smtp.PlainAuth("", userName, password, host),
- Template: template.Must(template.New("emailTemplate").Parse(logger.EmailScript())),
- }
- }
- // SendEmailException will send an email along with the exception.
- func SendEmailException(subject string, message string, a ...interface{}) error {
- var err error
- defer logger.CatchPanic(&err, "SendEmailException")
- if logger.EmailConfiguration == nil {
- return err
- }
- parameters := struct {
- From string
- To string
- Subject string
- Message string
- }{
- logger.EmailConfiguration.UserName,
- strings.Join([]string(logger.EmailConfiguration.To), ","),
- subject,
- fmt.Sprintf(message, a...),
- }
- var emailMessage bytes.Buffer
- logger.EmailConfiguration.Template.Execute(&emailMessage, ¶meters)
- err = smtp.SendMail(fmt.Sprintf("%s:%d",
- logger.EmailConfiguration.Host, logger.EmailConfiguration.Port),
- logger.EmailConfiguration.Auth,
- logger.EmailConfiguration.UserName,
- logger.EmailConfiguration.To,
- emailMessage.Bytes())
- return err
- }
- // LogLevel returns the configured logging level.
- func LogLevel() int32 {
- return atomic.LoadInt32(&logger.LogLevel)
- }
- // turnOnLogging configures the logging writers.
- func turnOnLogging(logLevel int32, fileHandle io.Writer) {
- traceHandle := ioutil.Discard
- infoHandle := ioutil.Discard
- warnHandle := ioutil.Discard
- errorHandle := ioutil.Discard
- if logLevel&LevelTrace != 0 {
- traceHandle = os.Stdout
- infoHandle = os.Stdout
- warnHandle = os.Stdout
- errorHandle = os.Stderr
- }
- if logLevel&LevelInfo != 0 {
- infoHandle = os.Stdout
- warnHandle = os.Stdout
- errorHandle = os.Stderr
- }
- if logLevel&LevelWarn != 0 {
- warnHandle = os.Stdout
- errorHandle = os.Stderr
- }
- if logLevel&LevelError != 0 {
- errorHandle = os.Stderr
- }
- if fileHandle != nil {
- if traceHandle == os.Stdout {
- traceHandle = io.MultiWriter(fileHandle, traceHandle)
- }
- if infoHandle == os.Stdout {
- infoHandle = io.MultiWriter(fileHandle, infoHandle)
- }
- if warnHandle == os.Stdout {
- warnHandle = io.MultiWriter(fileHandle, warnHandle)
- }
- if errorHandle == os.Stderr {
- errorHandle = io.MultiWriter(fileHandle, errorHandle)
- }
- }
- logger.Trace = log.New(traceHandle, "TRACE: ", log.Ldate|log.Ltime|log.Lshortfile)
- logger.Info = log.New(infoHandle, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
- logger.Warning = log.New(warnHandle, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile)
- logger.Error = log.New(errorHandle, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
- atomic.StoreInt32(&logger.LogLevel, logLevel)
- }
- // LogDirectoryCleanup performs all the directory cleanup and maintenance.
- func (traceLog *traceLog) LogDirectoryCleanup(baseFilePath string, daysToKeep int) {
- defer traceLog.CatchPanic(nil, "LogDirectoryCleanup")
- Startedf("main", "LogDirectoryCleanup", "BaseFilePath[%s] DaysToKeep[%d]", baseFilePath, daysToKeep)
- // Get a list of existing directories.
- fileInfos, err := ioutil.ReadDir(baseFilePath)
- if err != nil {
- CompletedError(err, "main", "LogDirectoryCleanup")
- return
- }
- // Create the date to compare for directories to remove.
- currentDate := time.Now().UTC()
- compareDate := time.Date(currentDate.Year(), currentDate.Month(), currentDate.Day()-daysToKeep, 0, 0, 0, 0, time.UTC)
- Trace("main", "LogDirectoryCleanup", "CompareDate[%v]", compareDate)
- for _, fileInfo := range fileInfos {
- if fileInfo.IsDir() == false {
- continue
- }
- // The file name look like: YYYY-MM-DD
- parts := strings.Split(fileInfo.Name(), "-")
- year, err := strconv.Atoi(parts[0])
- if err != nil {
- Errorf(err, "main", "LogDirectoryCleanup", "Attempting To Convert Directory [%s]", fileInfo.Name())
- continue
- }
- month, err := strconv.Atoi(parts[1])
- if err != nil {
- Errorf(err, "main", "LogDirectoryCleanup", "Attempting To Convert Directory [%s]", fileInfo.Name())
- continue
- }
- day, err := strconv.Atoi(parts[2])
- if err != nil {
- Errorf(err, "main", "LogDirectoryCleanup", "Attempting To Convert Directory [%s]", fileInfo.Name())
- continue
- }
- // The directory to check.
- fullFileName := fmt.Sprintf("%s/%s", baseFilePath, fileInfo.Name())
- // Create a time type from the directory name.
- directoryDate := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
- // Compare the dates and convert to days.
- daysOld := int(compareDate.Sub(directoryDate).Hours() / 24)
- Trace("main", "LogDirectoryCleanup", "Checking Directory[%s] DaysOld[%d]", fullFileName, daysOld)
- if daysOld >= 0 {
- Trace("main", "LogDirectoryCleanup", "Removing Directory[%s]", fullFileName)
- err = os.RemoveAll(fullFileName)
- if err != nil {
- Trace("main", "LogDirectoryCleanup", "Attempting To Remove Directory [%s]", fullFileName)
- continue
- }
- Trace("main", "LogDirectoryCleanup", "Directory Removed [%s]", fullFileName)
- }
- }
- // We don't need the catch handler to log any errors.
- err = nil
- Completed("main", "LogDirectoryCleanup")
- return
- }
- // CatchPanic is used to catch any Panic and log exceptions to Stdout. It will also write the stack logger.
- func (traceLog *traceLog) CatchPanic(err *error, functionName string) {
- if r := recover(); r != nil {
- // Capture the stack trace
- buf := make([]byte, 10000)
- runtime.Stack(buf, false)
- SendEmailException(systemAlertSubject, "%s : PANIC Defered [%s] : Stack Trace : %s", functionName, r, string(buf))
- if err != nil {
- *err = fmt.Errorf("%v", r)
- }
- }
- }
- // EmailScript returns a template for the email message to be sent.
- func (traceLog *traceLog) EmailScript() (script string) {
- return `From: {{.From}}
- To: {{.To}}
- Subject: {{.Subject}}
- MIME-version: 1.0
- Content-Type: text/html; charset="UTF-8"
- <html><body>{{.Message}}</body></html>`
- }
|