Logs can now be sent to files or syslog in addition to stderr

This commit is contained in:
Frank Denis 2018-01-18 14:25:45 +01:00
parent b0f6a04dc4
commit 0a63975d48
14 changed files with 518 additions and 10 deletions

8
Gopkg.lock generated
View File

@ -43,6 +43,12 @@
packages = ["."]
revision = "59b67882ec612f43b9d4c4fd97cebd507be4b3ee"
[[projects]]
branch = "master"
name = "github.com/hashicorp/go-syslog"
packages = ["."]
revision = "326bf4a7f709d263f964a6a96558676b103f3534"
[[projects]]
branch = "master"
name = "github.com/hashicorp/golang-lru"
@ -56,7 +62,7 @@
branch = "master"
name = "github.com/jedisct1/dlog"
packages = ["."]
revision = "8c253f4161c5b23a5fedd1d1ccee28d7ea312c6c"
revision = "fcbcc457e6a2b4eab2325a0902fd3d0a7d17e548"
[[projects]]
branch = "master"

View File

@ -69,7 +69,6 @@ The current 2.0.0 beta version includes all the major features from dnscrypt-pro
* New super simple (to copy&paste), extensible format for servers parameters: "stamps"
* Offline responses
* Local DNSSEC validation
* Flexible logging
* [DNS-over-HTTP2 (DoH)](https://datatracker.ietf.org/wg/doh/about/), the successor to DNS-over-TLS
* Support for the V1 plugin API
* Real documentation

View File

@ -51,7 +51,7 @@ type App struct {
}
func main() {
dlog.Init("dnscrypt-proxy", dlog.SeverityNotice)
dlog.Init("dnscrypt-proxy", dlog.SeverityNotice, "DAEMON")
cdLocal()
svcConfig := &service.Config{

22
vendor/github.com/hashicorp/go-syslog/.gitignore generated vendored Normal file
View File

@ -0,0 +1,22 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe

20
vendor/github.com/hashicorp/go-syslog/LICENSE generated vendored Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014 Armon Dadgar
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

11
vendor/github.com/hashicorp/go-syslog/README.md generated vendored Normal file
View File

@ -0,0 +1,11 @@
go-syslog
=========
This repository provides a very simple `gsyslog` package. The point of this
package is to allow safe importing of syslog without introducing cross-compilation
issues. The stdlib `log/syslog` cannot be imported on Windows systems, and without
conditional compilation this adds complications.
Instead, `gsyslog` provides a very simple wrapper around `log/syslog` but returns
a runtime error if attempting to initialize on a non Linux or OSX system.

214
vendor/github.com/hashicorp/go-syslog/builtin.go generated vendored Normal file
View File

@ -0,0 +1,214 @@
// This file is taken from the log/syslog in the standard lib.
// However, there is a bug with overwhelming syslog that causes writes
// to block indefinitely. This is fixed by adding a write deadline.
//
// +build !windows,!nacl,!plan9
package gsyslog
import (
"errors"
"fmt"
"log/syslog"
"net"
"os"
"strings"
"sync"
"time"
)
const severityMask = 0x07
const facilityMask = 0xf8
const localDeadline = 20 * time.Millisecond
const remoteDeadline = 50 * time.Millisecond
// A builtinWriter is a connection to a syslog server.
type builtinWriter struct {
priority syslog.Priority
tag string
hostname string
network string
raddr string
mu sync.Mutex // guards conn
conn serverConn
}
// This interface and the separate syslog_unix.go file exist for
// Solaris support as implemented by gccgo. On Solaris you can not
// simply open a TCP connection to the syslog daemon. The gccgo
// sources have a syslog_solaris.go file that implements unixSyslog to
// return a type that satisfies this interface and simply calls the C
// library syslog function.
type serverConn interface {
writeString(p syslog.Priority, hostname, tag, s, nl string) error
close() error
}
type netConn struct {
local bool
conn net.Conn
}
// New establishes a new connection to the system log daemon. Each
// write to the returned writer sends a log message with the given
// priority and prefix.
func newBuiltin(priority syslog.Priority, tag string) (w *builtinWriter, err error) {
return dialBuiltin("", "", priority, tag)
}
// Dial establishes a connection to a log daemon by connecting to
// address raddr on the specified network. Each write to the returned
// writer sends a log message with the given facility, severity and
// tag.
// If network is empty, Dial will connect to the local syslog server.
func dialBuiltin(network, raddr string, priority syslog.Priority, tag string) (*builtinWriter, error) {
if priority < 0 || priority > syslog.LOG_LOCAL7|syslog.LOG_DEBUG {
return nil, errors.New("log/syslog: invalid priority")
}
if tag == "" {
tag = os.Args[0]
}
hostname, _ := os.Hostname()
w := &builtinWriter{
priority: priority,
tag: tag,
hostname: hostname,
network: network,
raddr: raddr,
}
w.mu.Lock()
defer w.mu.Unlock()
err := w.connect()
if err != nil {
return nil, err
}
return w, err
}
// connect makes a connection to the syslog server.
// It must be called with w.mu held.
func (w *builtinWriter) connect() (err error) {
if w.conn != nil {
// ignore err from close, it makes sense to continue anyway
w.conn.close()
w.conn = nil
}
if w.network == "" {
w.conn, err = unixSyslog()
if w.hostname == "" {
w.hostname = "localhost"
}
} else {
var c net.Conn
c, err = net.DialTimeout(w.network, w.raddr, remoteDeadline)
if err == nil {
w.conn = &netConn{conn: c}
if w.hostname == "" {
w.hostname = c.LocalAddr().String()
}
}
}
return
}
// Write sends a log message to the syslog daemon.
func (w *builtinWriter) Write(b []byte) (int, error) {
return w.writeAndRetry(w.priority, string(b))
}
// Close closes a connection to the syslog daemon.
func (w *builtinWriter) Close() error {
w.mu.Lock()
defer w.mu.Unlock()
if w.conn != nil {
err := w.conn.close()
w.conn = nil
return err
}
return nil
}
func (w *builtinWriter) writeAndRetry(p syslog.Priority, s string) (int, error) {
pr := (w.priority & facilityMask) | (p & severityMask)
w.mu.Lock()
defer w.mu.Unlock()
if w.conn != nil {
if n, err := w.write(pr, s); err == nil {
return n, err
}
}
if err := w.connect(); err != nil {
return 0, err
}
return w.write(pr, s)
}
// write generates and writes a syslog formatted string. The
// format is as follows: <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG
func (w *builtinWriter) write(p syslog.Priority, msg string) (int, error) {
// ensure it ends in a \n
nl := ""
if !strings.HasSuffix(msg, "\n") {
nl = "\n"
}
err := w.conn.writeString(p, w.hostname, w.tag, msg, nl)
if err != nil {
return 0, err
}
// Note: return the length of the input, not the number of
// bytes printed by Fprintf, because this must behave like
// an io.Writer.
return len(msg), nil
}
func (n *netConn) writeString(p syslog.Priority, hostname, tag, msg, nl string) error {
if n.local {
// Compared to the network form below, the changes are:
// 1. Use time.Stamp instead of time.RFC3339.
// 2. Drop the hostname field from the Fprintf.
timestamp := time.Now().Format(time.Stamp)
n.conn.SetWriteDeadline(time.Now().Add(localDeadline))
_, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s",
p, timestamp,
tag, os.Getpid(), msg, nl)
return err
}
timestamp := time.Now().Format(time.RFC3339)
n.conn.SetWriteDeadline(time.Now().Add(remoteDeadline))
_, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s",
p, timestamp, hostname,
tag, os.Getpid(), msg, nl)
return err
}
func (n *netConn) close() error {
return n.conn.Close()
}
// unixSyslog opens a connection to the syslog daemon running on the
// local machine using a Unix domain socket.
func unixSyslog() (conn serverConn, err error) {
logTypes := []string{"unixgram", "unix"}
logPaths := []string{"/dev/log", "/var/run/syslog", "/var/run/log"}
for _, network := range logTypes {
for _, path := range logPaths {
conn, err := net.DialTimeout(network, path, localDeadline)
if err != nil {
continue
} else {
return &netConn{conn: conn, local: true}, nil
}
}
}
return nil, errors.New("Unix syslog delivery error")
}

27
vendor/github.com/hashicorp/go-syslog/syslog.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
package gsyslog
// Priority maps to the syslog priority levels
type Priority int
const (
LOG_EMERG Priority = iota
LOG_ALERT
LOG_CRIT
LOG_ERR
LOG_WARNING
LOG_NOTICE
LOG_INFO
LOG_DEBUG
)
// Syslogger interface is used to write log messages to syslog
type Syslogger interface {
// WriteLevel is used to write a message at a given level
WriteLevel(Priority, []byte) error
// Write is used to write a message at the default level
Write([]byte) (int, error)
// Close is used to close the connection to the logger
Close() error
}

123
vendor/github.com/hashicorp/go-syslog/unix.go generated vendored Normal file
View File

@ -0,0 +1,123 @@
// +build linux darwin dragonfly freebsd netbsd openbsd solaris
package gsyslog
import (
"fmt"
"log/syslog"
"strings"
)
// builtinLogger wraps the Golang implementation of a
// syslog.Writer to provide the Syslogger interface
type builtinLogger struct {
*builtinWriter
}
// NewLogger is used to construct a new Syslogger
func NewLogger(p Priority, facility, tag string) (Syslogger, error) {
fPriority, err := facilityPriority(facility)
if err != nil {
return nil, err
}
priority := syslog.Priority(p) | fPriority
l, err := newBuiltin(priority, tag)
if err != nil {
return nil, err
}
return &builtinLogger{l}, nil
}
// DialLogger is used to construct a new Syslogger that establishes connection to remote syslog server
func DialLogger(network, raddr string, p Priority, facility, tag string) (Syslogger, error) {
fPriority, err := facilityPriority(facility)
if err != nil {
return nil, err
}
priority := syslog.Priority(p) | fPriority
l, err := dialBuiltin(network, raddr, priority, tag)
if err != nil {
return nil, err
}
return &builtinLogger{l}, nil
}
// WriteLevel writes out a message at the given priority
func (b *builtinLogger) WriteLevel(p Priority, buf []byte) error {
var err error
m := string(buf)
switch p {
case LOG_EMERG:
_, err = b.writeAndRetry(syslog.LOG_EMERG, m)
case LOG_ALERT:
_, err = b.writeAndRetry(syslog.LOG_ALERT, m)
case LOG_CRIT:
_, err = b.writeAndRetry(syslog.LOG_CRIT, m)
case LOG_ERR:
_, err = b.writeAndRetry(syslog.LOG_ERR, m)
case LOG_WARNING:
_, err = b.writeAndRetry(syslog.LOG_WARNING, m)
case LOG_NOTICE:
_, err = b.writeAndRetry(syslog.LOG_NOTICE, m)
case LOG_INFO:
_, err = b.writeAndRetry(syslog.LOG_INFO, m)
case LOG_DEBUG:
_, err = b.writeAndRetry(syslog.LOG_DEBUG, m)
default:
err = fmt.Errorf("Unknown priority: %v", p)
}
return err
}
// facilityPriority converts a facility string into
// an appropriate priority level or returns an error
func facilityPriority(facility string) (syslog.Priority, error) {
facility = strings.ToUpper(facility)
switch facility {
case "KERN":
return syslog.LOG_KERN, nil
case "USER":
return syslog.LOG_USER, nil
case "MAIL":
return syslog.LOG_MAIL, nil
case "DAEMON":
return syslog.LOG_DAEMON, nil
case "AUTH":
return syslog.LOG_AUTH, nil
case "SYSLOG":
return syslog.LOG_SYSLOG, nil
case "LPR":
return syslog.LOG_LPR, nil
case "NEWS":
return syslog.LOG_NEWS, nil
case "UUCP":
return syslog.LOG_UUCP, nil
case "CRON":
return syslog.LOG_CRON, nil
case "AUTHPRIV":
return syslog.LOG_AUTHPRIV, nil
case "FTP":
return syslog.LOG_FTP, nil
case "LOCAL0":
return syslog.LOG_LOCAL0, nil
case "LOCAL1":
return syslog.LOG_LOCAL1, nil
case "LOCAL2":
return syslog.LOG_LOCAL2, nil
case "LOCAL3":
return syslog.LOG_LOCAL3, nil
case "LOCAL4":
return syslog.LOG_LOCAL4, nil
case "LOCAL5":
return syslog.LOG_LOCAL5, nil
case "LOCAL6":
return syslog.LOG_LOCAL6, nil
case "LOCAL7":
return syslog.LOG_LOCAL7, nil
default:
return 0, fmt.Errorf("invalid syslog facility: %s", facility)
}
}

17
vendor/github.com/hashicorp/go-syslog/unsupported.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
// +build windows plan9 nacl
package gsyslog
import (
"fmt"
)
// NewLogger is used to construct a new Syslogger
func NewLogger(p Priority, facility, tag string) (Syslogger, error) {
return nil, fmt.Errorf("Platform does not support syslog")
}
// DialLogger is used to construct a new Syslogger that establishes connection to remote syslog server
func DialLogger(network, raddr string, p Priority, facility, tag string) (Syslogger, error) {
return nil, fmt.Errorf("Platform does not support syslog")
}

15
vendor/github.com/jedisct1/dlog/Gopkg.lock generated vendored Normal file
View File

@ -0,0 +1,15 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/hashicorp/go-syslog"
packages = ["."]
revision = "326bf4a7f709d263f964a6a96558676b103f3534"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "47c6ffc1a8ae6fd34bff3832f6d38cf0e950ca98e55fb4af939dfb80e61f0409"
solver-name = "gps-cdcl"
solver-version = 1

3
vendor/github.com/jedisct1/dlog/Gopkg.toml generated vendored Normal file
View File

@ -0,0 +1,3 @@
[[constraint]]
branch = "master"
name = "github.com/hashicorp/go-syslog"

View File

@ -4,6 +4,8 @@ Go's standard logger is fairly limited. As result, kazilion alternatives loggers
All of these are wonderful. They can make your logs look colorful and pretty, format them for ElasticSearch, and more.
Cool, but all I wanted is something super dumb, that just exposes `log.Info()`, `log.Error()` and a couple other standard levels. I don't need a super flexible kitchen sink. Just something super basic and trivial to use.
Cool, but all I wanted is something super dumb, that just exposes `log.Info()`, `log.Error()` and a couple other standard levels.
I don't need a super flexible kitchen sink. Just something super basic and trivial to use. I just want it to handle different log levels, and be able to write simple logs to `stderr`, to a local file or to `syslog`.
So, here's one more logging library for Go. The dumbest of them all. Enjoy.

View File

@ -9,14 +9,21 @@ import (
"sync"
"sync/atomic"
"time"
"github.com/hashicorp/go-syslog"
)
type Severity int32
type globals struct {
sync.Mutex
logLevel Severity
appName string
logLevel Severity
useSyslog *bool
appName string
syslogFacility string
syslogger *gsyslog.Syslogger
fileName *string
outFd *os.File
}
var (
@ -47,6 +54,16 @@ var SeverityName = []string{
SeverityFatal: "FATAL",
}
var severityToSyslogPriority = []gsyslog.Priority{
SeverityDebug: gsyslog.LOG_DEBUG,
SeverityInfo: gsyslog.LOG_INFO,
SeverityNotice: gsyslog.LOG_NOTICE,
SeverityWarning: gsyslog.LOG_WARNING,
SeverityError: gsyslog.LOG_ERR,
SeverityCritical: gsyslog.LOG_CRIT,
SeverityFatal: gsyslog.LOG_ALERT,
}
func Debugf(format string, args ...interface{}) {
logf(SeverityDebug, format, args...)
}
@ -125,9 +142,18 @@ func (s *Severity) Set(strVal string) error {
return nil
}
func Init(appName string, logLevel Severity) {
func Init(appName string, logLevel Severity, syslogFacility string) error {
_globals.logLevel.set(logLevel)
if len(syslogFacility) == 0 {
syslogFacility = "DAEMON"
}
_globals.appName = appName
_globals.syslogFacility = syslogFacility
_globals.useSyslog = flag.Bool("syslog", false, "Send logs to the local system logger")
_globals.fileName = flag.String("logfile", "", "Write logs to file")
flag.Var(&_globals.logLevel, "loglevel", fmt.Sprintf("Log level (%d-%d)", SeverityDebug, SeverityFatal))
return nil
}
func logf(severity Severity, format string, args ...interface{}) {
@ -142,10 +168,33 @@ func logf(severity Severity, format string, args ...interface{}) {
if len(message) <= 0 {
return
}
line := fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d] [%s] [%s] %s\n", year, int(month), day, hour, minute, second, _globals.appName, SeverityName[severity], message)
_globals.Lock()
os.Stderr.WriteString(line)
_globals.Unlock()
defer _globals.Unlock()
if *_globals.useSyslog && _globals.syslogger == nil {
syslogger, err := gsyslog.NewLogger(gsyslog.LOG_INFO, _globals.syslogFacility, _globals.appName)
if err != nil {
panic(err)
}
_globals.syslogger = &syslogger
}
if _globals.fileName != nil && len(*_globals.fileName) > 0 && _globals.outFd == nil {
outFd, err := os.OpenFile(*_globals.fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
if err != nil {
panic(err)
}
_globals.outFd = outFd
}
if _globals.syslogger != nil {
(*_globals.syslogger).WriteLevel(severityToSyslogPriority[severity], []byte(message))
} else {
line := fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d] [%s] [%s] %s\n", year, int(month), day, hour, minute, second, _globals.appName, SeverityName[severity], message)
if _globals.outFd != nil {
_globals.outFd.WriteString(line)
_globals.outFd.Sync()
} else {
os.Stderr.WriteString(line)
}
}
if severity >= SeverityFatal {
os.Exit(255)
}