diff options
Diffstat (limited to 'modules/logging')
| -rw-r--r-- | modules/logging/encoders.go | 268 | ||||
| -rw-r--r-- | modules/logging/filewriter.go | 91 | ||||
| -rw-r--r-- | modules/logging/filterencoder.go | 321 | ||||
| -rw-r--r-- | modules/logging/filters.go | 94 | ||||
| -rw-r--r-- | modules/logging/nopencoder.go | 114 | 
5 files changed, 888 insertions, 0 deletions
diff --git a/modules/logging/encoders.go b/modules/logging/encoders.go new file mode 100644 index 0000000..c3c8aba --- /dev/null +++ b/modules/logging/encoders.go @@ -0,0 +1,268 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logging + +import ( +	"encoding/json" +	"fmt" +	"strings" +	"time" + +	"github.com/caddyserver/caddy/v2" +	zaplogfmt "github.com/jsternberg/zap-logfmt" +	"go.uber.org/zap" +	"go.uber.org/zap/buffer" +	"go.uber.org/zap/zapcore" +) + +func init() { +	caddy.RegisterModule(ConsoleEncoder{}) +	caddy.RegisterModule(JSONEncoder{}) +	caddy.RegisterModule(LogfmtEncoder{}) +	caddy.RegisterModule(StringEncoder{}) +} + +// ConsoleEncoder encodes log entries that are mostly human-readable. +type ConsoleEncoder struct { +	zapcore.Encoder +	LogEncoderConfig +} + +// CaddyModule returns the Caddy module information. +func (ConsoleEncoder) CaddyModule() caddy.ModuleInfo { +	return caddy.ModuleInfo{ +		Name: "caddy.logging.encoders.console", +		New:  func() caddy.Module { return new(ConsoleEncoder) }, +	} +} + +// Provision sets up the encoder. +func (ce *ConsoleEncoder) Provision(_ caddy.Context) error { +	ce.Encoder = zapcore.NewConsoleEncoder(ce.ZapcoreEncoderConfig()) +	return nil +} + +// JSONEncoder encodes entries as JSON. +type JSONEncoder struct { +	zapcore.Encoder +	*LogEncoderConfig +} + +// CaddyModule returns the Caddy module information. +func (JSONEncoder) CaddyModule() caddy.ModuleInfo { +	return caddy.ModuleInfo{ +		Name: "caddy.logging.encoders.json", +		New:  func() caddy.Module { return new(JSONEncoder) }, +	} +} + +// Provision sets up the encoder. +func (je *JSONEncoder) Provision(_ caddy.Context) error { +	je.Encoder = zapcore.NewJSONEncoder(je.ZapcoreEncoderConfig()) +	return nil +} + +// LogfmtEncoder encodes log entries as logfmt: +// https://www.brandur.org/logfmt +type LogfmtEncoder struct { +	zapcore.Encoder +	LogEncoderConfig +} + +// CaddyModule returns the Caddy module information. +func (LogfmtEncoder) CaddyModule() caddy.ModuleInfo { +	return caddy.ModuleInfo{ +		Name: "caddy.logging.encoders.logfmt", +		New:  func() caddy.Module { return new(LogfmtEncoder) }, +	} +} + +// Provision sets up the encoder. +func (lfe *LogfmtEncoder) Provision(_ caddy.Context) error { +	lfe.Encoder = zaplogfmt.NewEncoder(lfe.ZapcoreEncoderConfig()) +	return nil +} + +// StringEncoder writes a log entry that consists entirely +// of a single string field in the log entry. This is useful +// for custom, self-encoded log entries that consist of a +// single field in the structured log entry. +type StringEncoder struct { +	zapcore.Encoder +	FieldName   string          `json:"field,omitempty"` +	FallbackRaw json.RawMessage `json:"fallback,omitempty"` +} + +// CaddyModule returns the Caddy module information. +func (StringEncoder) CaddyModule() caddy.ModuleInfo { +	return caddy.ModuleInfo{ +		Name: "caddy.logging.encoders.string", +		New:  func() caddy.Module { return new(StringEncoder) }, +	} +} + +// Provision sets up the encoder. +func (se *StringEncoder) Provision(ctx caddy.Context) error { +	if se.FallbackRaw != nil { +		val, err := ctx.LoadModuleInline("format", "caddy.logging.encoders", se.FallbackRaw) +		if err != nil { +			return fmt.Errorf("loading fallback encoder module: %v", err) +		} +		se.FallbackRaw = nil // allow GC to deallocate +		se.Encoder = val.(zapcore.Encoder) +	} +	if se.Encoder == nil { +		se.Encoder = nopEncoder{} +	} +	return nil +} + +// Clone wraps the underlying encoder's Clone. This is +// necessary because we implement our own EncodeEntry, +// and if we simply let the embedded encoder's Clone +// be promoted, it would return a clone of that, and +// we'd lose our StringEncoder's EncodeEntry. +func (se StringEncoder) Clone() zapcore.Encoder { +	return StringEncoder{ +		Encoder:   se.Encoder.Clone(), +		FieldName: se.FieldName, +	} +} + +// EncodeEntry partially implements the zapcore.Encoder interface. +func (se StringEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) { +	for _, f := range fields { +		if f.Key == se.FieldName { +			buf := bufferpool.Get() +			buf.AppendString(f.String) +			if !strings.HasSuffix(f.String, "\n") { +				buf.AppendByte('\n') +			} +			return buf, nil +		} +	} +	if se.Encoder == nil { +		return nil, fmt.Errorf("no fallback encoder defined") +	} +	return se.Encoder.EncodeEntry(ent, fields) +} + +// LogEncoderConfig holds configuration common to most encoders. +type LogEncoderConfig struct { +	MessageKey     *string `json:"message_key,omitempty"` +	LevelKey       *string `json:"level_key,omitempty"` +	TimeKey        *string `json:"time_key,omitempty"` +	NameKey        *string `json:"name_key,omitempty"` +	CallerKey      *string `json:"caller_key,omitempty"` +	StacktraceKey  *string `json:"stacktrace_key,omitempty"` +	LineEnding     *string `json:"line_ending,omitempty"` +	TimeFormat     string  `json:"time_format,omitempty"` +	DurationFormat string  `json:"duration_format,omitempty"` +	LevelFormat    string  `json:"level_format,omitempty"` +} + +// ZapcoreEncoderConfig returns the equivalent zapcore.EncoderConfig. +// If lec is nil, zap.NewProductionEncoderConfig() is returned. +func (lec *LogEncoderConfig) ZapcoreEncoderConfig() zapcore.EncoderConfig { +	cfg := zap.NewProductionEncoderConfig() +	if lec == nil { +		lec = new(LogEncoderConfig) +	} +	if lec.MessageKey != nil { +		cfg.MessageKey = *lec.MessageKey +	} +	if lec.TimeKey != nil { +		cfg.TimeKey = *lec.TimeKey +	} +	if lec.NameKey != nil { +		cfg.NameKey = *lec.NameKey +	} +	if lec.CallerKey != nil { +		cfg.CallerKey = *lec.CallerKey +	} +	if lec.StacktraceKey != nil { +		cfg.StacktraceKey = *lec.StacktraceKey +	} +	if lec.LineEnding != nil { +		cfg.LineEnding = *lec.LineEnding +	} + +	// time format +	var timeFormatter zapcore.TimeEncoder +	switch lec.TimeFormat { +	case "", "unix_seconds_float": +		timeFormatter = zapcore.EpochTimeEncoder +	case "unix_milli_float": +		timeFormatter = zapcore.EpochMillisTimeEncoder +	case "unix_nano": +		timeFormatter = zapcore.EpochNanosTimeEncoder +	case "iso8601": +		timeFormatter = zapcore.ISO8601TimeEncoder +	default: +		timeFormat := lec.TimeFormat +		switch lec.TimeFormat { +		case "rfc3339": +			timeFormat = time.RFC3339 +		case "rfc3339_nano": +			timeFormat = time.RFC3339Nano +		case "wall": +			timeFormat = "2006/01/02 15:04:05" +		case "wall_milli": +			timeFormat = "2006/01/02 15:04:05.000" +		case "wall_nano": +			timeFormat = "2006/01/02 15:04:05.000000000" +		} +		timeFormatter = func(ts time.Time, encoder zapcore.PrimitiveArrayEncoder) { +			encoder.AppendString(ts.UTC().Format(timeFormat)) +		} +	} +	cfg.EncodeTime = timeFormatter + +	// duration format +	var durFormatter zapcore.DurationEncoder +	switch lec.DurationFormat { +	case "", "seconds": +		durFormatter = zapcore.SecondsDurationEncoder +	case "nano": +		durFormatter = zapcore.NanosDurationEncoder +	case "string": +		durFormatter = zapcore.StringDurationEncoder +	} +	cfg.EncodeDuration = durFormatter + +	// level format +	var levelFormatter zapcore.LevelEncoder +	switch lec.LevelFormat { +	case "", "lower": +		levelFormatter = zapcore.LowercaseLevelEncoder +	case "upper": +		levelFormatter = zapcore.CapitalLevelEncoder +	case "color": +		levelFormatter = zapcore.CapitalColorLevelEncoder +	} +	cfg.EncodeLevel = levelFormatter + +	return cfg +} + +var bufferpool = buffer.NewPool() + +// Interface guards +var ( +	_ zapcore.Encoder = (*ConsoleEncoder)(nil) +	_ zapcore.Encoder = (*JSONEncoder)(nil) +	_ zapcore.Encoder = (*LogfmtEncoder)(nil) +	_ zapcore.Encoder = (*StringEncoder)(nil) +) diff --git a/modules/logging/filewriter.go b/modules/logging/filewriter.go new file mode 100644 index 0000000..29e805e --- /dev/null +++ b/modules/logging/filewriter.go @@ -0,0 +1,91 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logging + +import ( +	"io" +	"os" +	"path/filepath" + +	"github.com/caddyserver/caddy/v2" +	"gopkg.in/natefinch/lumberjack.v2" +) + +func init() { +	caddy.RegisterModule(FileWriter{}) +} + +// FileWriter can write logs to files. +type FileWriter struct { +	Filename      string `json:"filename,omitempty"` +	Roll          *bool  `json:"roll,omitempty"` +	RollSizeMB    int    `json:"roll_size_mb,omitempty"` +	RollCompress  *bool  `json:"roll_gzip,omitempty"` +	RollLocalTime bool   `json:"roll_local_time,omitempty"` +	RollKeep      int    `json:"roll_keep,omitempty"` +	RollKeepDays  int    `json:"roll_keep_days,omitempty"` +} + +// CaddyModule returns the Caddy module information. +func (FileWriter) CaddyModule() caddy.ModuleInfo { +	return caddy.ModuleInfo{ +		Name: "caddy.logging.writers.file", +		New:  func() caddy.Module { return new(FileWriter) }, +	} +} + +func (fw FileWriter) String() string { +	fpath, err := filepath.Abs(fw.Filename) +	if err == nil { +		return fpath +	} +	return fw.Filename +} + +// WriterKey returns a unique key representing this fw. +func (fw FileWriter) WriterKey() string { +	return "file:" + fw.Filename +} + +// OpenWriter opens a new file writer. +func (fw FileWriter) OpenWriter() (io.WriteCloser, error) { +	// roll log files by default +	if fw.Roll == nil || *fw.Roll == true { +		if fw.RollSizeMB == 0 { +			fw.RollSizeMB = 100 +		} +		if fw.RollCompress == nil { +			compress := true +			fw.RollCompress = &compress +		} +		if fw.RollKeep == 0 { +			fw.RollKeep = 10 +		} +		if fw.RollKeepDays == 0 { +			fw.RollKeepDays = 90 +		} +		return &lumberjack.Logger{ +			Filename:   fw.Filename, +			MaxSize:    fw.RollSizeMB, +			MaxAge:     fw.RollKeepDays, +			MaxBackups: fw.RollKeep, +			LocalTime:  fw.RollLocalTime, +			Compress:   *fw.RollCompress, +		}, nil +	} + +	// otherwise just open a regular file +	return os.OpenFile(fw.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) +} diff --git a/modules/logging/filterencoder.go b/modules/logging/filterencoder.go new file mode 100644 index 0000000..eff0279 --- /dev/null +++ b/modules/logging/filterencoder.go @@ -0,0 +1,321 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logging + +import ( +	"encoding/json" +	"fmt" +	"time" + +	"github.com/caddyserver/caddy/v2" +	"go.uber.org/zap" +	"go.uber.org/zap/buffer" +	"go.uber.org/zap/zapcore" +) + +func init() { +	caddy.RegisterModule(FilterEncoder{}) +} + +// FilterEncoder wraps an underlying encoder. It does +// not do any encoding itself, but it can manipulate +// (filter) fields before they are actually encoded. +// A wrapped encoder is required. +type FilterEncoder struct { +	WrappedRaw json.RawMessage            `json:"wrap,omitempty"` +	FieldsRaw  map[string]json.RawMessage `json:"fields,omitempty"` + +	wrapped zapcore.Encoder +	Fields  map[string]LogFieldFilter `json:"-"` + +	// used to keep keys unique across nested objects +	keyPrefix string +} + +// CaddyModule returns the Caddy module information. +func (FilterEncoder) CaddyModule() caddy.ModuleInfo { +	return caddy.ModuleInfo{ +		Name: "caddy.logging.encoders.filter", +		New:  func() caddy.Module { return new(FilterEncoder) }, +	} +} + +// Provision sets up the encoder. +func (fe *FilterEncoder) Provision(ctx caddy.Context) error { +	if fe.WrappedRaw == nil { +		return fmt.Errorf("missing \"wrap\" (must specify an underlying encoder)") +	} + +	// set up wrapped encoder (required) +	val, err := ctx.LoadModuleInline("format", "caddy.logging.encoders", fe.WrappedRaw) +	if err != nil { +		return fmt.Errorf("loading fallback encoder module: %v", err) +	} +	fe.WrappedRaw = nil // allow GC to deallocate +	fe.wrapped = val.(zapcore.Encoder) + +	// set up each field filter +	if fe.Fields == nil { +		fe.Fields = make(map[string]LogFieldFilter) +	} +	for field, filterRaw := range fe.FieldsRaw { +		if filterRaw == nil { +			continue +		} +		val, err := ctx.LoadModuleInline("filter", "caddy.logging.encoders.filter", filterRaw) +		if err != nil { +			return fmt.Errorf("loading log filter module: %v", err) +		} +		fe.Fields[field] = val.(LogFieldFilter) +	} +	fe.FieldsRaw = nil // allow GC to deallocate + +	return nil +} + +// AddArray is part of the zapcore.ObjectEncoder interface. +// Array elements do not get filtered. +func (fe FilterEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error { +	if filter, ok := fe.Fields[fe.keyPrefix+key]; ok { +		filter.Filter(zap.Array(key, marshaler)).AddTo(fe.wrapped) +		return nil +	} +	return fe.wrapped.AddArray(key, marshaler) +} + +// AddObject is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) error { +	fe.keyPrefix += key + ">" +	return fe.wrapped.AddObject(key, logObjectMarshalerWrapper{ +		enc:   fe, +		marsh: marshaler, +	}) +} + +// AddBinary is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddBinary(key string, value []byte) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddBinary(key, value) +	} +} + +// AddByteString is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddByteString(key string, value []byte) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddByteString(key, value) +	} +} + +// AddBool is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddBool(key string, value bool) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddBool(key, value) +	} +} + +// AddComplex128 is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddComplex128(key string, value complex128) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddComplex128(key, value) +	} +} + +// AddComplex64 is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddComplex64(key string, value complex64) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddComplex64(key, value) +	} +} + +// AddDuration is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddDuration(key string, value time.Duration) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddDuration(key, value) +	} +} + +// AddFloat64 is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddFloat64(key string, value float64) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddFloat64(key, value) +	} +} + +// AddFloat32 is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddFloat32(key string, value float32) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddFloat32(key, value) +	} +} + +// AddInt is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddInt(key string, value int) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddInt(key, value) +	} +} + +// AddInt64 is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddInt64(key string, value int64) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddInt64(key, value) +	} +} + +// AddInt32 is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddInt32(key string, value int32) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddInt32(key, value) +	} +} + +// AddInt16 is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddInt16(key string, value int16) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddInt16(key, value) +	} +} + +// AddInt8 is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddInt8(key string, value int8) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddInt8(key, value) +	} +} + +// AddString is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddString(key, value string) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddString(key, value) +	} +} + +// AddTime is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddTime(key string, value time.Time) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddTime(key, value) +	} +} + +// AddUint is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddUint(key string, value uint) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddUint(key, value) +	} +} + +// AddUint64 is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddUint64(key string, value uint64) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddUint64(key, value) +	} +} + +// AddUint32 is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddUint32(key string, value uint32) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddUint32(key, value) +	} +} + +// AddUint16 is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddUint16(key string, value uint16) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddUint16(key, value) +	} +} + +// AddUint8 is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddUint8(key string, value uint8) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddUint8(key, value) +	} +} + +// AddUintptr is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddUintptr(key string, value uintptr) { +	if !fe.filtered(key, value) { +		fe.wrapped.AddUintptr(key, value) +	} +} + +// AddReflected is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) AddReflected(key string, value interface{}) error { +	if !fe.filtered(key, value) { +		return fe.wrapped.AddReflected(key, value) +	} +	return nil +} + +// OpenNamespace is part of the zapcore.ObjectEncoder interface. +func (fe FilterEncoder) OpenNamespace(key string) { +	fe.wrapped.OpenNamespace(key) +} + +// Clone is part of the zapcore.ObjectEncoder interface. +// We don't use it as of Oct 2019 (v2 beta 7), I'm not +// really sure what it'd be useful for in our case. +func (fe FilterEncoder) Clone() zapcore.Encoder { +	return FilterEncoder{ +		Fields:    fe.Fields, +		wrapped:   fe.wrapped.Clone(), +		keyPrefix: fe.keyPrefix, +	} +} + +// EncodeEntry partially implements the zapcore.Encoder interface. +func (fe FilterEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) { +	// without this clone and storing it to fe.wrapped, fields +	// from subsequent log entries get appended to previous +	// ones, and I'm not 100% sure why; see end of +	// https://github.com/uber-go/zap/issues/750 +	fe.wrapped = fe.wrapped.Clone() +	for _, field := range fields { +		field.AddTo(fe) +	} +	return fe.wrapped.EncodeEntry(ent, nil) +} + +// filtered returns true if the field was filtered. +// If true is returned, the field was filtered and +// added to the underlying encoder (so do not do +// that again). If false was returned, the field has +// not yet been added to the underlying encoder. +func (fe FilterEncoder) filtered(key string, value interface{}) bool { +	filter, ok := fe.Fields[fe.keyPrefix+key] +	if !ok { +		return false +	} +	filter.Filter(zap.Any(key, value)).AddTo(fe.wrapped) +	return true +} + +// logObjectMarshalerWrapper allows us to recursively +// filter fields of objects as they get encoded. +type logObjectMarshalerWrapper struct { +	enc   FilterEncoder +	marsh zapcore.ObjectMarshaler +} + +// MarshalLogObject implements the zapcore.ObjectMarshaler interface. +func (mom logObjectMarshalerWrapper) MarshalLogObject(_ zapcore.ObjectEncoder) error { +	return mom.marsh.MarshalLogObject(mom.enc) +} + +// Interface guards +var ( +	_ zapcore.Encoder         = (*FilterEncoder)(nil) +	_ zapcore.ObjectMarshaler = (*logObjectMarshalerWrapper)(nil) +) diff --git a/modules/logging/filters.go b/modules/logging/filters.go new file mode 100644 index 0000000..b44e084 --- /dev/null +++ b/modules/logging/filters.go @@ -0,0 +1,94 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logging + +import ( +	"net" + +	"github.com/caddyserver/caddy/v2" +	"go.uber.org/zap/zapcore" +) + +func init() { +	caddy.RegisterModule(DeleteFilter{}) +	caddy.RegisterModule(IPMaskFilter{}) +} + +// LogFieldFilter can filter (or manipulate) +// a field in a log entry. If delete is true, +// out will be ignored and the field will be +// removed from the output. +type LogFieldFilter interface { +	Filter(zapcore.Field) zapcore.Field +} + +// DeleteFilter is a Caddy log field filter that +// deletes the field. +type DeleteFilter struct{} + +// CaddyModule returns the Caddy module information. +func (DeleteFilter) CaddyModule() caddy.ModuleInfo { +	return caddy.ModuleInfo{ +		Name: "caddy.logging.encoders.filter.delete", +		New:  func() caddy.Module { return new(DeleteFilter) }, +	} +} + +// Filter filters the input field. +func (DeleteFilter) Filter(in zapcore.Field) zapcore.Field { +	in.Type = zapcore.SkipType +	return in +} + +// IPMaskFilter is a Caddy log field filter that +// masks IP addresses. +type IPMaskFilter struct { +	IPv4CIDR int `json:"ipv4_cidr,omitempty"` +	IPv6CIDR int `json:"ipv6_cidr,omitempty"` +} + +// CaddyModule returns the Caddy module information. +func (IPMaskFilter) CaddyModule() caddy.ModuleInfo { +	return caddy.ModuleInfo{ +		Name: "caddy.logging.encoders.filter.ip_mask", +		New:  func() caddy.Module { return new(IPMaskFilter) }, +	} +} + +// Filter filters the input field. +func (m IPMaskFilter) Filter(in zapcore.Field) zapcore.Field { +	host, port, err := net.SplitHostPort(in.String) +	if err != nil { +		host = in.String // assume whole thing was IP address +	} +	ipAddr := net.ParseIP(host) +	if ipAddr == nil { +		return in +	} +	bitLen := 32 +	cidrPrefix := m.IPv4CIDR +	if ipAddr.To16() != nil { +		bitLen = 128 +		cidrPrefix = m.IPv6CIDR +	} +	mask := net.CIDRMask(cidrPrefix, bitLen) +	masked := ipAddr.Mask(mask) +	if port == "" { +		in.String = masked.String() +	} else { +		in.String = net.JoinHostPort(masked.String(), port) +	} +	return in +} diff --git a/modules/logging/nopencoder.go b/modules/logging/nopencoder.go new file mode 100644 index 0000000..fc3d70d --- /dev/null +++ b/modules/logging/nopencoder.go @@ -0,0 +1,114 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logging + +import ( +	"time" + +	"go.uber.org/zap/buffer" +	"go.uber.org/zap/zapcore" +) + +// nopEncoder is a zapcore.Encoder that does nothing. +type nopEncoder struct{} + +// AddArray is part of the zapcore.ObjectEncoder interface. +// Array elements do not get filtered. +func (nopEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error { return nil } + +// AddObject is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) error { return nil } + +// AddBinary is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddBinary(key string, value []byte) {} + +// AddByteString is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddByteString(key string, value []byte) {} + +// AddBool is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddBool(key string, value bool) {} + +// AddComplex128 is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddComplex128(key string, value complex128) {} + +// AddComplex64 is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddComplex64(key string, value complex64) {} + +// AddDuration is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddDuration(key string, value time.Duration) {} + +// AddFloat64 is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddFloat64(key string, value float64) {} + +// AddFloat32 is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddFloat32(key string, value float32) {} + +// AddInt is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddInt(key string, value int) {} + +// AddInt64 is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddInt64(key string, value int64) {} + +// AddInt32 is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddInt32(key string, value int32) {} + +// AddInt16 is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddInt16(key string, value int16) {} + +// AddInt8 is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddInt8(key string, value int8) {} + +// AddString is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddString(key, value string) {} + +// AddTime is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddTime(key string, value time.Time) {} + +// AddUint is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddUint(key string, value uint) {} + +// AddUint64 is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddUint64(key string, value uint64) {} + +// AddUint32 is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddUint32(key string, value uint32) {} + +// AddUint16 is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddUint16(key string, value uint16) {} + +// AddUint8 is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddUint8(key string, value uint8) {} + +// AddUintptr is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddUintptr(key string, value uintptr) {} + +// AddReflected is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) AddReflected(key string, value interface{}) error { return nil } + +// OpenNamespace is part of the zapcore.ObjectEncoder interface. +func (nopEncoder) OpenNamespace(key string) {} + +// Clone is part of the zapcore.ObjectEncoder interface. +// We don't use it as of Oct 2019 (v2 beta 7), I'm not +// really sure what it'd be useful for in our case. +func (ne nopEncoder) Clone() zapcore.Encoder { return ne } + +// EncodeEntry partially implements the zapcore.Encoder interface. +func (nopEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) { +	return bufferpool.Get(), nil +} + +// Interface guard +var _ zapcore.Encoder = (*nopEncoder)(nil)  | 
