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
5 changes: 3 additions & 2 deletions cns/logger/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (
)

var (
Log *CNSLogger
aiMetadata string // this var is set at build time.
Log *CNSLogger
aiMetadata string // this var is set at build time.
AppInsightsIKey = aiMetadata
)

// todo: the functions below should be removed. CNSLogger should be injected where needed and not used from package level scope.
Expand Down
74 changes: 74 additions & 0 deletions cns/logger/v2/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package logger

import (
"encoding/json"

loggerv1 "github.com/Azure/azure-container-networking/cns/logger"
"github.com/Azure/azure-container-networking/internal/time"
"github.com/pkg/errors"
"go.uber.org/zap/zapcore"
)

//nolint:unused // will be used
const (
defaultMaxBackups = 10
defaultMaxSize = 10 // MB
defaultMaxBatchInterval = 30 * time.Second
defaultMaxBatchSize = 32000
defaultGracePeriod = 30 * time.Second
)

//nolint:unused // will be used
var defaultIKey = loggerv1.AppInsightsIKey

// UnmarshalJSON implements json.Unmarshaler for the Config.
// It only differs from the default by parsing the
// Level string into a zapcore.Level and setting the level field.
func (c *Config) UnmarshalJSON(data []byte) error {
type Alias Config
aux := &struct {
*Alias
}{
Alias: (*Alias)(c),
}
if err := json.Unmarshal(data, &aux); err != nil { //nolint:musttag // doesn't understand the embedding strategy
return errors.Wrap(err, "failed to unmarshal Config")
}
lvl, err := zapcore.ParseLevel(c.Level)
if err != nil {
return errors.Wrap(err, "failed to parse Config Level")
}
c.level = lvl
return nil
}

// Normalize checks the Config for missing or illegal values and sets them
// to defaults if appropriate.
func (c *Config) Normalize() {
if c.File != nil {
if c.File.Filepath == "" {
c.File.Filepath = defaultFilePath
}
if c.File.MaxBackups == 0 {
c.File.MaxBackups = defaultMaxBackups
}
if c.File.MaxSize == 0 {
c.File.MaxSize = defaultMaxSize
}
}
if c.AppInsights != nil {
if c.AppInsights.IKey == "" {
c.AppInsights.IKey = defaultIKey
}
if c.AppInsights.GracePeriod.Duration == 0 {
c.AppInsights.GracePeriod.Duration = defaultGracePeriod
}
if c.AppInsights.MaxBatchInterval.Duration == 0 {
c.AppInsights.MaxBatchInterval.Duration = defaultMaxBatchInterval
}
if c.AppInsights.MaxBatchSize == 0 {
c.AppInsights.MaxBatchSize = defaultMaxBatchSize
}
}
c.normalize()
}
18 changes: 18 additions & 0 deletions cns/logger/v2/config_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package logger

import (
cores "github.com/Azure/azure-container-networking/cns/logger/v2/cores"
"go.uber.org/zap/zapcore"
)

const defaultFilePath = "/var/log/azure-cns.log"

type Config struct {
// Level is the general logging Level. If cores have more specific config it will override this.
Level string `json:"level"`
level zapcore.Level `json:"-"`
AppInsights *cores.AppInsightsConfig `json:"appInsights,omitempty"`
File *cores.FileConfig `json:"file,omitempty"`
}

func (c *Config) normalize() {}
55 changes: 55 additions & 0 deletions cns/logger/v2/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package logger

import (
"encoding/json"
"testing"

cores "github.com/Azure/azure-container-networking/cns/logger/v2/cores"
"github.com/stretchr/testify/require"
)

func TestUnmarshalJSON(t *testing.T) {
tests := []struct {
name string
have []byte
want *Config
wantErr bool
}{
{
name: "valid",
have: []byte(`{"level":"info"}`),
want: &Config{
Level: "info",
level: 0,
},
},
{
name: "invalid level",
have: []byte(`{"level":"invalid"}`),
wantErr: true,
},
{
name: "valid with file",
have: []byte(`{"level":"info","file":{"filepath":"/k/azurecns/azure-cns.log"}}`),
want: &Config{
Level: "info",
level: 0,
File: &cores.FileConfig{
Filepath: "/k/azurecns/azure-cns.log",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Config{}
err := json.Unmarshal(tt.have, c)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tt.want, c)
})
}
}
28 changes: 28 additions & 0 deletions cns/logger/v2/config_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package logger

import (
cores "github.com/Azure/azure-container-networking/cns/logger/v2/cores"
"go.uber.org/zap/zapcore"
)

const defaultFilePath = "/k/azurecns/azure-cns.log"

type Config struct {
// Level is the general logging Level. If cores have more specific config it will override this.
Level string `json:"level"`
level zapcore.Level `json:"-"`
AppInsights *cores.AppInsightsConfig `json:"appInsights,omitempty"`
File *cores.FileConfig `json:"file,omitempty"`
ETW *cores.ETWConfig `json:"etw,omitempty"`
}

func (c *Config) normalize() {
if c.ETW != nil {
if c.ETW.EventName == "" {
c.ETW.EventName = "AzureCNS"
}
if c.ETW.ProviderName == "" {
c.ETW.ProviderName = "ACN-Monitoring"
}
}
}
67 changes: 67 additions & 0 deletions cns/logger/v2/cores/ai.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package logger

import (
"encoding/json"

"github.com/Azure/azure-container-networking/internal/time"
"github.com/Azure/azure-container-networking/zapai"
"github.com/microsoft/ApplicationInsights-Go/appinsights"
"github.com/pkg/errors"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

type AppInsightsConfig struct {
level zapcore.Level `json:"-"` // Zero value is default Info level.
Level string `json:"level"`
IKey string `json:"ikey"`
GracePeriod time.Duration `json:"grace_period"`
MaxBatchInterval time.Duration `json:"max_batch_interval"`
MaxBatchSize int `json:"max_batch_size"`
Fields []zapcore.Field `json:"fields"`
}

// UnmarshalJSON implements json.Unmarshaler for the Config.
// It only differs from the default by parsing the
// Level string into a zapcore.Level and setting the level field.
func (c *AppInsightsConfig) UnmarshalJSON(data []byte) error {
type Alias AppInsightsConfig
aux := &struct {
*Alias
}{
Alias: (*Alias)(c),
}
if err := json.Unmarshal(data, &aux); err != nil {
return errors.Wrap(err, "failed to unmarshal AppInsightsConfig")
}
lvl, err := zapcore.ParseLevel(c.Level)
if err != nil {
return errors.Wrap(err, "failed to parse AppInsightsConfig Level")
}
c.level = lvl
return nil
}

// ApplicationInsightsCore builds a zapcore.Core that sends logs to Application Insights.
// The first return is the core, the second is a function to close the sink.
func ApplicationInsightsCore(cfg *AppInsightsConfig) (zapcore.Core, func(), error) {
// build the AI config
aicfg := *appinsights.NewTelemetryConfiguration(cfg.IKey)
aicfg.MaxBatchSize = cfg.MaxBatchSize
aicfg.MaxBatchInterval = cfg.MaxBatchInterval.Duration
sinkcfg := zapai.SinkConfig{
GracePeriod: cfg.GracePeriod.Duration,
TelemetryConfiguration: aicfg,
}
// open the AI zap sink
sink, aiclose, err := zap.Open(sinkcfg.URI())
if err != nil {
return nil, aiclose, errors.Wrap(err, "failed to open AI sink")
}
// build the AI core
core := zapai.NewCore(cfg.level, sink)
core = core.WithFieldMappers(zapai.DefaultMappers)
// add normalized fields for the built-in AI Tags
// TODO(rbtr): move to the caller
return core.With(cfg.Fields), aiclose, nil
}
48 changes: 48 additions & 0 deletions cns/logger/v2/cores/ai_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package logger

import (
"encoding/json"
"testing"

"github.com/Azure/azure-container-networking/internal/time"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"
)

func TestAIConfigUnmarshalJSON(t *testing.T) {
tests := []struct {
name string
have []byte
want *AppInsightsConfig
wantErr bool
}{
{
name: "valid",
have: []byte(`{"grace_period":"30s","level":"panic","max_batch_interval":"30s","max_batch_size":32000}`),
want: &AppInsightsConfig{
GracePeriod: time.Duration{Duration: 30 * time.Second},
Level: "panic",
level: zapcore.PanicLevel,
MaxBatchInterval: time.Duration{Duration: 30 * time.Second},
MaxBatchSize: 32000,
},
},
{
name: "invalid level",
have: []byte(`{"level":"invalid"}`),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &AppInsightsConfig{}
err := json.Unmarshal(tt.have, c)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tt.want, c)
})
}
}
22 changes: 22 additions & 0 deletions cns/logger/v2/cores/etw_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package logger

import (
"github.com/Azure/azure-container-networking/zapetw"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

type ETWConfig struct {
EventName string
Level zapcore.Level
ProviderName string
}

// ETWCore builds a zapcore.Core that sends logs to ETW.
// The first return is the core, the second is a function to close the sink.
func ETWCore(cfg *ETWConfig) (zapcore.Core, func(), error) {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
jsonEncoder := zapcore.NewJSONEncoder(encoderConfig)
return zapetw.New(cfg.ProviderName, cfg.EventName, jsonEncoder, cfg.Level) //nolint:wrapcheck // ignore
}
53 changes: 53 additions & 0 deletions cns/logger/v2/cores/file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package logger

import (
"encoding/json"

"github.com/pkg/errors"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)

type FileConfig struct {
Filepath string `json:"filepath"`
Level string `json:"level"`
level zapcore.Level `json:"-"`
MaxBackups int `json:"maxBackups"`
MaxSize int `json:"maxSize"`
}

// UnmarshalJSON implements json.Unmarshaler for the Config.
// It only differs from the default by parsing the
// Level string into a zapcore.Level and setting the level field.
func (cfg *FileConfig) UnmarshalJSON(data []byte) error {
type Alias FileConfig
aux := &struct {
*Alias
}{
Alias: (*Alias)(cfg),
}
if err := json.Unmarshal(data, &aux); err != nil {
return errors.Wrap(err, "failed to unmarshal FileConfig")
}
lvl, err := zapcore.ParseLevel(cfg.Level)
if err != nil {
return errors.Wrap(err, "failed to parse FileConfig Level")
}
cfg.level = lvl
return nil
}

// FileCore builds a zapcore.Core that writes to a file.
// The first return is the core, the second is a function to close the file.
func FileCore(cfg *FileConfig) (zapcore.Core, func(), error) {
filesink := &lumberjack.Logger{
Filename: cfg.Filepath,
MaxSize: cfg.MaxSize, // MB
MaxBackups: cfg.MaxBackups,
}
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
jsonEncoder := zapcore.NewJSONEncoder(encoderConfig)
return zapcore.NewCore(jsonEncoder, zapcore.AddSync(filesink), cfg.level), func() { _ = filesink.Close() }, nil
}
Loading
Loading