feat(): output hook execution logs to server logs

BREAKING CHANGES for configuration

- rename `-log-dir` to `-hook-log-dir`
- add `-hook-log-output`
- add `-log-level`
- remove `-debug`

close #44
This commit is contained in:
Nicolas Carlier 2021-05-29 15:30:02 +00:00
parent ed67fc72f6
commit 07fbb6ee3a
9 changed files with 51 additions and 19 deletions

View File

@ -26,7 +26,7 @@ $ go get -v github.com/ncarlier/webhookd
```bash
$ sudo curl -s https://raw.githubusercontent.com/ncarlier/webhookd/master/install.sh | bash
or
$ curl -sf https://gobinaries.com/ncarlier/za | sh
$ curl -sf https://gobinaries.com/ncarlier/webhookd | sh
```
**Or** use Docker:
@ -204,10 +204,12 @@ $ # Retrieve logs afterwards
$ curl http://localhost:8080/echo/2
```
If needed, you can also redirect hook logs to the server output (configured by the `WHD_HOOK_LOG_OUTPUT` environment variable).
### Post hook notifications
The output of the script is collected and stored into a log file
(configured by the `WHD_LOG_DIR` environment variable).
(configured by the `WHD_HOOK_LOG_DIR` environment variable).
Once the script is executed, you can send the result and this log file to a notification channel.
Currently, only two channels are supported: `Email` and `HTTP`.
@ -255,9 +257,11 @@ The following JSON payload is POST to the target URL:
}
```
Note that because the payload have a `text` attribute, you can use a [Mattermost][mattermost] webhook endpoint.
Note that because the payload have a `text` attribute, you can use a [Mattermost][mattermost], [Slack][slack] or [Discord][discord] webhook endpoint.
[mattermost]: https://docs.mattermost.com/developer/webhooks-incoming.html
[discord]: https://discord.com/developers/docs/resources/webhook#execute-slackcompatible-webhook
[slack]: https://api.slack.com/messaging/webhooks
#### Email notification

View File

@ -2,8 +2,11 @@
# Webhookd configuration
###
# Output debug logs, default is false
#WHD_DEBUG=false
# Hook execution logs location, default is OS temporary directory
#WHD_HOOK_LOG_DIR="/tmp"
# Output hook execution logs to server logs, default is false
#WHD_HOOK_LOG_OUTPUT=false
# Maximum hook execution time in second, default is 10
#WHD_HOOK_TIMEOUT=10
@ -12,8 +15,8 @@
# Example: `localhost:8080` or `:8080` for all interfaces
#WHD_LISTEN_ADDR=":8080"
# Hook execution logs location, default is OS temporary directory
#WHD_LOG_DIR="/tmp"
# Log level (debug, info, warn, error), default is "info"
#WHD_LOG_LEVEL=info
# Number of workers to start, default is 2
#WHD_NB_WORKERS=2

12
main.go
View File

@ -30,14 +30,14 @@ func main() {
os.Exit(0)
}
level := "info"
if conf.Debug {
level = "debug"
if conf.HookLogOutput {
logger.Init(conf.LogLevel, "out")
} else {
logger.Init(conf.LogLevel)
}
logger.Init(level)
if conf.LogDir == "" {
conf.LogDir = os.TempDir()
if conf.HookLogDir == "" {
conf.HookLogDir = os.TempDir()
}
logger.Debug.Println("starting webhookd server...")

View File

@ -33,7 +33,7 @@ func atoiFallback(str string, fallback int) int {
func index(conf *config.Config) http.Handler {
defaultTimeout = conf.HookTimeout
scriptDir = conf.ScriptDir
outputDir = conf.LogDir
outputDir = conf.HookLogDir
return http.HandlerFunc(webhookHandler)
}

View File

@ -8,11 +8,12 @@ type Config struct {
TLSKeyFile string `flag:"tls-key-file" desc:"TLS key file" default:"server.key"`
TLSDomain string `flag:"tls-domain" desc:"TLS domain name used by ACME"`
NbWorkers int `flag:"nb-workers" desc:"Number of workers to start" default:"2"`
Debug bool `flag:"debug" desc:"Output debug logs" default:"false"`
HookTimeout int `flag:"hook-timeout" desc:"Maximum hook execution time in second" default:"10"`
HookLogDir string `flag:"hook-log-dir" desc:"Hook execution logs location" default:""`
HookLogOutput bool `flag:"hook-log-output" desc:"Output hook execution logs to server logs" default:"false"`
ScriptDir string `flag:"scripts" desc:"Scripts location" default:"scripts"`
PasswdFile string `flag:"passwd-file" desc:"Password file for basic HTTP authentication" default:".htpasswd"`
LogDir string `flag:"log-dir" desc:"Hook execution logs location" default:""`
LogLevel string `flag:"log-level" desc:"Log level (debug, info, warn, error)" default:"info"`
StaticDir string `flag:"static-dir" desc:"Static file directory to serve on /static path" default:""`
NotificationURI string `flag:"notification-uri" desc:"Notification URI"`
TrustStoreFile string `flag:"trust-store-file" desc:"Trust store used by HTTP signature verifier (.pem or .p12)"`

View File

@ -43,3 +43,8 @@ func Orange(text string) string {
func Red(text string) string {
return colorize(text, red)
}
// Purple ANSI color applied to a string
func Purple(text string) string {
return colorize(text, purple)
}

View File

@ -16,15 +16,18 @@ var (
Warning *log.Logger
// Error level
Error *log.Logger
// Output special level used for script output
Output *log.Logger
)
// Init logger level
func Init(level string) {
var debugHandle, infoHandle, warnHandle, errorHandle io.Writer
func Init(level string, with ...string) {
var debugHandle, infoHandle, warnHandle, errorHandle, outputHandle io.Writer
debugHandle = os.Stdout
infoHandle = os.Stdout
warnHandle = os.Stderr
errorHandle = os.Stderr
outputHandle = ioutil.Discard
switch level {
case "info":
debugHandle = ioutil.Discard
@ -37,6 +40,10 @@ func Init(level string) {
warnHandle = ioutil.Discard
}
if contains(with, "out") {
outputHandle = os.Stdout
}
commonFlags := log.LstdFlags | log.Lmicroseconds
if level == "debug" {
commonFlags = log.LstdFlags | log.Lmicroseconds | log.Lshortfile
@ -46,4 +53,14 @@ func Init(level string) {
Info = log.New(infoHandle, Green("INF "), commonFlags)
Warning = log.New(warnHandle, Orange("WRN "), commonFlags)
Error = log.New(errorHandle, Red("ERR "), commonFlags)
Output = log.New(outputHandle, Purple("OUT "), commonFlags)
}
func contains(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}

View File

@ -24,7 +24,7 @@ func printWorkMessages(work *model.WorkRequest) {
}
func TestWorkRunner(t *testing.T) {
logger.Init("debug")
logger.Init("debug", "out")
script := "./test_simple.sh"
args := []string{
"name=foo",

View File

@ -88,6 +88,8 @@ func Run(work *model.WorkRequest) error {
logger.Error.Printf("hook %s#%d is over ; unable to write more data into the channel: %s\n", work.Name, work.ID, line)
break
}
// write to stdout if configured
logger.Output.Println(line)
// writing to outfile
if _, err := wLogFile.WriteString(line + "\n"); err != nil {
logger.Error.Println("error while writing into the log file:", logFile.Name(), err)