mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-03 19:17:29 -05:00 
			
		
		
		
	* proof of concept * Initial implementation with debug code * Tidy up debug code * remove unneeded import * removed extra line * Move ShouldLog function to rule entry Logger type * add tests for ShouldLog * Added tests for log exceptions * Fix logic * fix govet fail for test * Updates requested for code clarity * Update requested for style * log: Minor style tweaks to logic of log exceptions
		
			
				
	
	
		
			197 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			197 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2015 Light Code Labs, LLC
 | 
						|
//
 | 
						|
// 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 httpserver
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"io"
 | 
						|
	"log"
 | 
						|
	"net"
 | 
						|
	"os"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"github.com/hashicorp/go-syslog"
 | 
						|
	"github.com/mholt/caddy"
 | 
						|
)
 | 
						|
 | 
						|
var remoteSyslogPrefixes = map[string]string{
 | 
						|
	"syslog+tcp://": "tcp",
 | 
						|
	"syslog+udp://": "udp",
 | 
						|
	"syslog://":     "udp",
 | 
						|
}
 | 
						|
 | 
						|
// Logger is shared between errors and log plugins and supports both logging to
 | 
						|
// a file (with an optional file roller), local and remote syslog servers.
 | 
						|
type Logger struct {
 | 
						|
	Output string
 | 
						|
	*log.Logger
 | 
						|
	Roller       *LogRoller
 | 
						|
	writer       io.Writer
 | 
						|
	fileMu       *sync.RWMutex
 | 
						|
	V4ipMask     net.IPMask
 | 
						|
	V6ipMask     net.IPMask
 | 
						|
	IPMaskExists bool
 | 
						|
	Exceptions   []string
 | 
						|
}
 | 
						|
 | 
						|
// NewTestLogger creates logger suitable for testing purposes
 | 
						|
func NewTestLogger(buffer *bytes.Buffer) *Logger {
 | 
						|
	return &Logger{
 | 
						|
		Logger: log.New(buffer, "", 0),
 | 
						|
		fileMu: new(sync.RWMutex),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Println wraps underlying logger with mutex
 | 
						|
func (l Logger) Println(args ...interface{}) {
 | 
						|
	l.fileMu.RLock()
 | 
						|
	l.Logger.Println(args...)
 | 
						|
	l.fileMu.RUnlock()
 | 
						|
}
 | 
						|
 | 
						|
// Printf wraps underlying logger with mutex
 | 
						|
func (l Logger) Printf(format string, args ...interface{}) {
 | 
						|
	l.fileMu.RLock()
 | 
						|
	l.Logger.Printf(format, args...)
 | 
						|
	l.fileMu.RUnlock()
 | 
						|
}
 | 
						|
 | 
						|
func (l Logger) MaskIP(ip string) string {
 | 
						|
	var reqIP net.IP
 | 
						|
	// If unable to parse, simply return IP as provided.
 | 
						|
	reqIP = net.ParseIP(ip)
 | 
						|
	if reqIP == nil {
 | 
						|
		return ip
 | 
						|
	}
 | 
						|
 | 
						|
	if reqIP.To4() != nil {
 | 
						|
		return reqIP.Mask(l.V4ipMask).String()
 | 
						|
	} else {
 | 
						|
		return reqIP.Mask(l.V6ipMask).String()
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
// ShouldLog returns true if the path is not exempted from
 | 
						|
// being logged (i.e. it is not found in l.Exceptions).
 | 
						|
func (l Logger) ShouldLog(path string) bool {
 | 
						|
	for _, exc := range l.Exceptions {
 | 
						|
		if Path(path).Matches(exc) {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// Attach binds logger Start and Close functions to
 | 
						|
// controller's OnStartup and OnShutdown hooks.
 | 
						|
func (l *Logger) Attach(controller *caddy.Controller) {
 | 
						|
	if controller != nil {
 | 
						|
		// Opens file or connect to local/remote syslog
 | 
						|
		controller.OnStartup(l.Start)
 | 
						|
 | 
						|
		// Closes file or disconnects from local/remote syslog
 | 
						|
		controller.OnShutdown(l.Close)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type syslogAddress struct {
 | 
						|
	network string
 | 
						|
	address string
 | 
						|
}
 | 
						|
 | 
						|
func parseSyslogAddress(location string) *syslogAddress {
 | 
						|
	for prefix, network := range remoteSyslogPrefixes {
 | 
						|
		if strings.HasPrefix(location, prefix) {
 | 
						|
			return &syslogAddress{
 | 
						|
				network: network,
 | 
						|
				address: strings.TrimPrefix(location, prefix),
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Start initializes logger opening files or local/remote syslog connections
 | 
						|
func (l *Logger) Start() error {
 | 
						|
	// initialize mutex on start
 | 
						|
	l.fileMu = new(sync.RWMutex)
 | 
						|
 | 
						|
	var err error
 | 
						|
 | 
						|
selectwriter:
 | 
						|
	switch l.Output {
 | 
						|
	case "", "stderr":
 | 
						|
		l.writer = os.Stderr
 | 
						|
	case "stdout":
 | 
						|
		l.writer = os.Stdout
 | 
						|
	case "syslog":
 | 
						|
		l.writer, err = gsyslog.NewLogger(gsyslog.LOG_ERR, "LOCAL0", "caddy")
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		if address := parseSyslogAddress(l.Output); address != nil {
 | 
						|
			l.writer, err = gsyslog.DialLogger(address.network, address.address, gsyslog.LOG_ERR, "LOCAL0", "caddy")
 | 
						|
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
 | 
						|
			break selectwriter
 | 
						|
		}
 | 
						|
 | 
						|
		var file *os.File
 | 
						|
 | 
						|
		file, err = os.OpenFile(l.Output, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		if l.Roller != nil {
 | 
						|
			file.Close()
 | 
						|
			l.Roller.Filename = l.Output
 | 
						|
			l.writer = l.Roller.GetLogWriter()
 | 
						|
		} else {
 | 
						|
			l.writer = file
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	l.Logger = log.New(l.writer, "", 0)
 | 
						|
 | 
						|
	return nil
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
// Close closes open log files or connections to syslog.
 | 
						|
func (l *Logger) Close() error {
 | 
						|
	// don't close stdout or stderr
 | 
						|
	if l.writer == os.Stdout || l.writer == os.Stderr {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// Will close local/remote syslog connections too :)
 | 
						|
	if closer, ok := l.writer.(io.WriteCloser); ok {
 | 
						|
		l.fileMu.Lock()
 | 
						|
		err := closer.Close()
 | 
						|
		l.fileMu.Unlock()
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 |