Files
fn-serverless/api/common/logging.go
Reed Allman 3b261fc144 pipe swapparoo each slot (#721)
* pipe swapparoo each slot

previously, we made a pair of pipes for stdin and stdout for each container,
and then handed them out to each call (slot) to use. this meant that multiple
calls could have a handle on the same stdin pipe and stdout pipe to read/write
to/from from fn's perspective and could mix input/output and get garbage. this
also meant that each was blocked on the previous' reads.

now we make a new pipe every time we get a slot, and swap it out with the
previous ones. calls are no longer blocked from fn's perspective, and we don't
have to worry about timing out dispatch for any hot format. there is still the
issue that if a function does not finish reading the input from the previous
task, from its perspective, and reads the next call's it can error out the
second call. with fn deadline we provide the necessary tools to skirt this,
but without some additional coordination am not sure this is a closable hole
with our current protocols since terminating a previous calls input requires
some protocol specific bytes to go in (json in particular is tricky). anyway,
from fn's side fixing pipes was definitely a hole, but this client hole is
still hanging out. there was an attempt to send an io.EOF but the issue is
that will shut down docker's read on the stdin pipe (and the container). poop.

this adds a test for this behavior, and makes sure 2 containers don't get
launched.

this also closes the response writer header race a little, but not entirely, I
think there's still a chance that we read a full response from a function and
get a timeout while we're changing the headers. I guess we need a thread safe
header bucket, otherwise we have to rely on timings (racy). thinking on it.

* fix stats mu race
2018-01-31 17:25:24 -08:00

83 lines
2.4 KiB
Go

package common
import (
"net/url"
"os"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
func SetLogLevel(ll string) {
if ll == "" {
ll = "info"
}
// show full timestamps
formatter := &logrus.TextFormatter{
FullTimestamp: true,
}
logrus.SetFormatter(formatter)
logrus.WithFields(logrus.Fields{"level": ll}).Info("Setting log level to")
logLevel, err := logrus.ParseLevel(ll)
if err != nil {
logrus.WithFields(logrus.Fields{"level": ll}).Warn("Could not parse log level, setting to INFO")
logLevel = logrus.InfoLevel
}
logrus.SetLevel(logLevel)
// this effectively just adds more gin log goodies
gin.SetMode(gin.ReleaseMode)
if logLevel == logrus.DebugLevel {
gin.SetMode(gin.DebugMode)
}
}
func SetLogDest(to, prefix string) {
logrus.SetOutput(os.Stderr) // in case logrus changes their mind...
if to == "stderr" {
return
}
// possible schemes: { udp, tcp, file }
// file url must contain only a path, syslog must contain only a host[:port]
// expect: [scheme://][host][:port][/path]
// default scheme to udp:// if none given
url, err := url.Parse(to)
if url.Host == "" && url.Path == "" {
logrus.WithFields(logrus.Fields{"to": to}).Warn("No scheme on logging url, adding udp://")
// this happens when no scheme like udp:// is present
to = "udp://" + to
url, err = url.Parse(to)
}
if err != nil {
logrus.WithError(err).WithFields(logrus.Fields{"to": to}).Error("could not parse logging URI, defaulting to stderr")
return
}
// File URL must contain only `url.Path`. Syslog location must contain only `url.Host`
if (url.Host == "" && url.Path == "") || (url.Host != "" && url.Path != "") {
logrus.WithFields(logrus.Fields{"to": to, "uri": url}).Error("invalid logging location, defaulting to stderr")
return
}
switch url.Scheme {
case "udp", "tcp":
err = NewSyslogHook(url, prefix)
if err != nil {
logrus.WithFields(logrus.Fields{"uri": url, "to": to}).WithError(err).Error("unable to connect to syslog, defaulting to stderr")
return
}
case "file":
f, err := os.OpenFile(url.Path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
if err != nil {
logrus.WithError(err).WithFields(logrus.Fields{"to": to, "path": url.Path}).Error("cannot open file, defaulting to stderr")
return
}
logrus.SetOutput(f)
default:
logrus.WithFields(logrus.Fields{"scheme": url.Scheme, "to": to}).Error("unknown logging location scheme, defaulting to stderr")
}
}