diff --git a/admin.go b/admin.go index 570d94c4..d6f1787a 100644 --- a/admin.go +++ b/admin.go @@ -711,6 +711,12 @@ traverseLoop: return fmt.Errorf("unrecognized method %s", method) } } else { + // if we are "PUTting" a new resource, the key(s) in its path + // might not exist yet; that's OK but we need to make them as + // we go, while we still have a pointer from the level above + if v[part] == nil && method == http.MethodPut { + v[part] = make(map[string]interface{}) + } ptr = v[part] } diff --git a/caddy.go b/caddy.go index f13540d6..cde42b10 100644 --- a/caddy.go +++ b/caddy.go @@ -134,7 +134,7 @@ func changeConfig(method, path string, input []byte, forceReload bool) error { // if nothing changed, no need to do a whole reload unless the client forces it if !forceReload && bytes.Equal(rawCfgJSON, newCfg) { - Log().Named("admin.api.change_config").Info("config is unchanged") + Log().Named("admin.api").Info("config is unchanged") return nil } diff --git a/logging.go b/logging.go index a4a1473f..790f7001 100644 --- a/logging.go +++ b/logging.go @@ -225,10 +225,11 @@ func (logging *Logging) openWriter(opener WriterOpener) (io.WriteCloser, bool, e w, err := opener.OpenWriter() return writerDestructor{w}, err }) - if err == nil { - logging.writerKeys = append(logging.writerKeys, key) + if err != nil { + return nil, false, err } - return writer.(io.WriteCloser), !loaded, err + logging.writerKeys = append(logging.writerKeys, key) + return writer.(io.WriteCloser), !loaded, nil } // WriterOpener is a module that can open a log writer. diff --git a/modules/logging/netwriter.go b/modules/logging/netwriter.go new file mode 100644 index 00000000..1df80b65 --- /dev/null +++ b/modules/logging/netwriter.go @@ -0,0 +1,82 @@ +// 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 ( + "fmt" + "io" + "net" + + "github.com/caddyserver/caddy/v2" +) + +func init() { + caddy.RegisterModule(NetWriter{}) +} + +// NetWriter implements a log writer that outputs to a network socket. +type NetWriter struct { + Address string `json:"address,omitempty"` + + addr caddy.ParsedAddress +} + +// CaddyModule returns the Caddy module information. +func (NetWriter) CaddyModule() caddy.ModuleInfo { + return caddy.ModuleInfo{ + ID: "caddy.logging.writers.net", + New: func() caddy.Module { return new(NetWriter) }, + } +} + +// Provision sets up the module. +func (nw *NetWriter) Provision(ctx caddy.Context) error { + repl := caddy.NewReplacer() + address, err := repl.ReplaceOrErr(nw.Address, true, true) + if err != nil { + return fmt.Errorf("invalid host in address: %v", err) + } + + nw.addr, err = caddy.ParseNetworkAddress(address) + if err != nil { + return fmt.Errorf("parsing network address '%s': %v", address, err) + } + + if nw.addr.PortRangeSize() != 1 { + return fmt.Errorf("multiple ports not supported") + } + + return nil +} + +func (nw NetWriter) String() string { + return nw.addr.String() +} + +// WriterKey returns a unique key representing this nw. +func (nw NetWriter) WriterKey() string { + return nw.addr.String() +} + +// OpenWriter opens a new network connection. +func (nw NetWriter) OpenWriter() (io.WriteCloser, error) { + return net.Dial(nw.addr.Network, nw.addr.JoinHostPort(0)) +} + +// Interface guards +var ( + _ caddy.Provisioner = (*NetWriter)(nil) + _ caddy.WriterOpener = (*NetWriter)(nil) +)