Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 89 additions & 5 deletions log/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
package log

import (
"fmt"
"io"
"log"
"os"

"github.com/Azure/azure-container-networking/platform"
)

// Log level
Expand All @@ -24,15 +29,27 @@ const (
)

const (
// Log file properties.
logPrefix = ""
logFileExtension = ".log"
logFilePerm = os.FileMode(0664)

// Log file rotation default limits, in bytes.
maxLogFileSize = 5 * 1024 * 1024
maxLogFileCount = 8
rotationCheckFrq = 8
)

// Logger object
type Logger struct {
l *log.Logger
name string
level int
l *log.Logger
out io.WriteCloser
name string
level int
target int
maxFileSize int
maxFileCount int
callCount int
}

// NewLogger creates a new Logger.
Expand All @@ -43,6 +60,8 @@ func NewLogger(name string, level int, target int) *Logger {
logger.name = name
logger.level = level
logger.SetTarget(target)
logger.maxFileSize = maxLogFileSize
logger.maxFileCount = maxLogFileCount

return &logger
}
Expand All @@ -57,6 +76,61 @@ func (logger *Logger) SetLevel(level int) {
logger.level = level
}

// SetLogFileLimits sets the log file limits.
func (logger *Logger) SetLogFileLimits(maxFileSize int, maxFileCount int) {
logger.maxFileSize = maxFileSize
logger.maxFileCount = maxFileCount
}

// Close closes the log stream.
func (logger *Logger) Close() {
if logger.out != nil {
logger.out.Close()
}
}

// GetLogFileName returns the full log file name.
func (logger *Logger) getLogFileName() string {
return platform.LogPath + logger.name + logFileExtension
}

// Rotate checks the active log file size and rotates log files if necessary.
func (logger *Logger) rotate() {
// Return if target is not a log file.
if logger.target != TargetLogfile || logger.out == nil {
return
}

fileName := logger.getLogFileName()
fileInfo, err := os.Stat(fileName)
if err != nil {
logger.Printf("[log] Failed to query log file info %+v.", err)
return
}

// Rotate if size limit is reached.
if fileInfo.Size() >= int64(logger.maxFileSize) {
logger.out.Close()
var fn1, fn2 string

// Rotate log files, keeping the last maxFileCount files.
for n := logger.maxFileCount - 1; n >= 0; n-- {
fn2 = fn1
if n == 0 {
fn1 = fileName
} else {
fn1 = fmt.Sprintf("%v.%v", fileName, n)
}
if fn2 != "" {
os.Rename(fn1, fn2)
}
}

// Create a new log file.
logger.SetTarget(TargetLogfile)
}
}

// Request logs a structured request.
func (logger *Logger) Request(tag string, request interface{}, err error) {
if err == nil {
Expand All @@ -75,16 +149,26 @@ func (logger *Logger) Response(tag string, response interface{}, err error) {
}
}

// Logf logs a formatted string.
func (logger *Logger) logf(format string, args ...interface{}) {
if logger.callCount%rotationCheckFrq == 0 {
logger.rotate()
}
logger.callCount++

logger.l.Printf(format, args...)
}

// Printf logs a formatted string at info level.
func (logger *Logger) Printf(format string, args ...interface{}) {
if logger.level >= LevelInfo {
logger.l.Printf(format, args...)
logger.logf(format, args...)
}
}

// Debugf logs a formatted string at debug level.
func (logger *Logger) Debugf(format string, args ...interface{}) {
if logger.level >= LevelDebug {
logger.l.Printf(format, args...)
logger.logf(format, args...)
}
}
19 changes: 5 additions & 14 deletions log/logger_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,29 @@ package log

import (
"fmt"
"io"
"log"
"log/syslog"
"os"

"github.com/Azure/azure-container-networking/platform"
)

// Log file properties.
const logFilePerm = os.FileMode(0664)

const syslogTag = "AzureContainerNet"

// SetTarget sets the log target.
func (logger *Logger) SetTarget(target int) error {
var out io.Writer
var err error

switch target {
case TargetStderr:
out = os.Stderr
logger.out = os.Stderr
case TargetSyslog:
out, err = syslog.New(log.LstdFlags, syslogTag)
logger.out, err = syslog.New(log.LstdFlags, logger.name)
case TargetLogfile:
fileName := platform.LogPath + logger.name + logFileExtension
out, err = os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_RDWR, logFilePerm)
logger.out, err = os.OpenFile(logger.getLogFileName(), os.O_CREATE|os.O_APPEND|os.O_RDWR, logFilePerm)
default:
err = fmt.Errorf("Invalid log target %d", target)
}

if err == nil {
logger.l.SetOutput(out)
logger.l.SetOutput(logger.out)
logger.target = target
}

return err
Expand Down
50 changes: 50 additions & 0 deletions log/logger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2017 Microsoft. All rights reserved.
// MIT License

package log

import (
"os"
"testing"
)

const (
logName = "test"
)

// Tests that the log file rotates when size limit is reached.
func TestLogFileRotatesWhenSizeLimitIsReached(t *testing.T) {
l := NewLogger(logName, LevelInfo, TargetLogfile)
if l == nil {
t.Fatalf("Failed to create logger.\n")
}

l.SetLogFileLimits(512, 2)

for i := 1; i <= 100; i++ {
l.Printf("LogText %v", i)
}

l.Close()

fn := logName + ".log"
_, err := os.Stat(fn)
if err != nil {
t.Errorf("Failed to find active log file.")
}
os.Remove(fn)

fn = logName + ".log.1"
_, err = os.Stat(fn)
if err != nil {
t.Errorf("Failed to find the 1st rotated log file.")
}
os.Remove(fn)

fn = logName + ".log.2"
_, err = os.Stat(fn)
if err == nil {
t.Errorf("Found the 2nd rotated log file which should have been deleted.")
}
os.Remove(fn)
}
15 changes: 4 additions & 11 deletions log/logger_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,25 @@ package log

import (
"fmt"
"io"
"os"

"github.com/Azure/azure-container-networking/platform"
)

// Log file properties.
const logFilePerm = os.FileMode(0664)

// SetTarget sets the log target.
func (logger *Logger) SetTarget(target int) error {
var out io.Writer
var err error

switch target {
case TargetStderr:
out = os.Stderr
logger.out = os.Stderr
case TargetLogfile:
fileName := platform.LogPath + logger.name + logFileExtension
out, err = os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_RDWR, logFilePerm)
logger.out, err = os.OpenFile(logger.getLogFileName(), os.O_CREATE|os.O_APPEND|os.O_RDWR, logFilePerm)
default:
err = fmt.Errorf("Invalid log target %d", target)
}

if err == nil {
logger.l.SetOutput(out)
logger.l.SetOutput(logger.out)
logger.target = target
}

return err
Expand Down
8 changes: 8 additions & 0 deletions log/stdapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ func SetLevel(level int) {
stdLog.SetLevel(level)
}

func SetLogFileLimits(maxFileSize int, maxFileCount int) {
stdLog.SetLogFileLimits(maxFileSize, maxFileCount)
}

func Close() {
stdLog.Close()
}

func Request(tag string, request interface{}, err error) {
stdLog.Request(tag, request, err)
}
Expand Down