diff --git a/cmd/ooniprobe/main.go b/cmd/ooniprobe/main.go index 5b6a6ce..cb90811 100644 --- a/cmd/ooniprobe/main.go +++ b/cmd/ooniprobe/main.go @@ -1,9 +1,8 @@ package main import ( - // commands - "github.com/apex/log" + "github.com/ooni/probe-cli/internal/cli/app" _ "github.com/ooni/probe-cli/internal/cli/geoip" _ "github.com/ooni/probe-cli/internal/cli/info" _ "github.com/ooni/probe-cli/internal/cli/list" @@ -15,13 +14,10 @@ import ( _ "github.com/ooni/probe-cli/internal/cli/upload" _ "github.com/ooni/probe-cli/internal/cli/version" "github.com/ooni/probe-cli/internal/crashreport" - - "github.com/ooni/probe-cli/internal/cli/app" ) func main() { - err, _ := crashreport.CapturePanic(app.Run, nil) - if err != nil { + if err, _ := crashreport.CapturePanic(app.Run, nil); err != nil { log.WithError(err.(error)).Error("panic in app.Run") crashreport.Wait() } diff --git a/internal/cli/root/root.go b/internal/cli/root/root.go index bcaadbd..a7f9954 100644 --- a/internal/cli/root/root.go +++ b/internal/cli/root/root.go @@ -5,6 +5,7 @@ import ( "github.com/apex/log" "github.com/ooni/probe-cli/internal/log/handlers/batch" "github.com/ooni/probe-cli/internal/log/handlers/cli" + "github.com/ooni/probe-cli/internal/log/handlers/syslog" "github.com/ooni/probe-cli/internal/ooni" "github.com/ooni/probe-cli/internal/utils" "github.com/ooni/probe-cli/internal/version" @@ -33,6 +34,9 @@ func init() { isVerbose := Cmd.Flag("verbose", "Enable verbose log output.").Short('v').Bool() isBatch := Cmd.Flag("batch", "Enable batch command line usage.").Bool() + logHandler := Cmd.Flag( + "log-handler", "Set the desired log handler (one of: batch, cli, syslog)", + ).String() softwareName := Cmd.Flag( "software-name", "Override application name", @@ -42,10 +46,23 @@ func init() { ).Default(version.Version).String() Cmd.PreAction(func(ctx *kingpin.ParseContext) error { + // TODO(bassosimone): we need to properly deprecate --batch + // in favour of more granular command line flags. + if *isBatch && *logHandler != "" { + log.Fatal("cannot specify --batch and --log-handler together") + } if *isBatch { + *logHandler = "batch" + } + switch *logHandler { + case "batch": log.SetHandler(batch.Default) - } else { + case "cli", "": log.SetHandler(cli.Default) + case "syslog": + log.SetHandler(syslog.Default) + default: + log.Fatalf("unknown --log-handler: %s", *logHandler) } if *isVerbose { log.SetLevel(log.DebugLevel) diff --git a/internal/log/handlers/syslog/syslog.c b/internal/log/handlers/syslog/syslog.c new file mode 100644 index 0000000..a7f73c6 --- /dev/null +++ b/internal/log/handlers/syslog/syslog.c @@ -0,0 +1,39 @@ +#ifndef _WIN32 +#include +#endif + +void ooniprobe_openlog(void) { +#ifndef _WIN32 + (void)openlog("ooniprobe", LOG_PID, LOG_USER); +#endif +} + +void ooniprobe_log_debug(const char *message) { +#ifndef _WIN32 + (void)syslog(LOG_DEBUG, "%s", message); +#endif +} + +void ooniprobe_log_info(const char *message) { +#ifndef _WIN32 + (void)syslog(LOG_INFO, "%s", message); +#endif +} + +void ooniprobe_log_warning(const char *message) { +#ifndef _WIN32 + (void)syslog(LOG_WARNING, "%s", message); +#endif +} + +void ooniprobe_log_err(const char *message) { +#ifndef _WIN32 + (void)syslog(LOG_ERR, "%s", message); +#endif +} + +void ooniprobe_log_crit(const char *message) { +#ifndef _WIN32 + (void)syslog(LOG_CRIT, "%s", message); +#endif +} diff --git a/internal/log/handlers/syslog/syslog.go b/internal/log/handlers/syslog/syslog.go new file mode 100644 index 0000000..30e5ef0 --- /dev/null +++ b/internal/log/handlers/syslog/syslog.go @@ -0,0 +1,53 @@ +// Package syslog contains a syslog handler. +// +// We use this handler on macOS systems to log messages +// when ooniprobe is running in the background. +package syslog + +import ( + "fmt" + "unsafe" + + "github.com/apex/log" +) + +/* +#include + +void ooniprobe_openlog(void); +void ooniprobe_log_debug(const char *message); +void ooniprobe_log_info(const char *message); +void ooniprobe_log_warning(const char *message); +void ooniprobe_log_err(const char *message); +void ooniprobe_log_crit(const char *message); +*/ +import "C" + +// Default is the handler that emits logs with syslog +var Default log.Handler = newhandler() + +type handler struct{} + +func newhandler() handler { + C.ooniprobe_openlog() + return handler{} +} + +func (h handler) HandleLog(e *log.Entry) error { + message := fmt.Sprintf("%s %+v", e.Message, e.Fields) + cstr := C.CString(message) + defer C.free(unsafe.Pointer(cstr)) + switch e.Level { + case log.DebugLevel: + C.ooniprobe_log_debug(cstr) + case log.InfoLevel: + C.ooniprobe_log_info(cstr) + case log.WarnLevel: + C.ooniprobe_log_warning(cstr) + case log.ErrorLevel: + C.ooniprobe_log_err(cstr) + default: + C.ooniprobe_log_crit(cstr) + } + return nil +}