Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure log timestamp format #5050

Merged
merged 3 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"os"
"path/filepath"

"github.com/spf13/cobra"

"github.com/open-policy-agent/opa/cmd/internal/exec"
"github.com/open-policy-agent/opa/internal/config"
internal_logging "github.com/open-policy-agent/opa/internal/logging"
Expand All @@ -19,7 +21,6 @@ import (
"github.com/open-policy-agent/opa/plugins/status"
"github.com/open-policy-agent/opa/sdk"
"github.com/open-policy-agent/opa/util"
"github.com/spf13/cobra"
)

func init() {
Expand Down Expand Up @@ -69,13 +70,14 @@ e.g., opa exec --decision /foo/bar/baz ...`,
cmd.Flags().StringVarP(&params.Decision, "decision", "", "", "set decision to evaluate")
cmd.Flags().VarP(params.LogLevel, "log-level", "l", "set log level")
cmd.Flags().Var(params.LogFormat, "log-format", "set log format")
cmd.Flags().StringVar(&params.LogTimestampFormat, "log-timestamp-format", "", "set log timestamp format (OPA_LOG_TIMESTAMP_FORMAT environment variable)")

RootCommand.AddCommand(cmd)
}

func runExec(params *exec.Params) error {

stdLogger, consoleLogger, err := setupLogging(params.LogLevel.String(), params.LogFormat.String())
stdLogger, consoleLogger, err := setupLogging(params.LogLevel.String(), params.LogFormat.String(), params.LogTimestampFormat)
if err != nil {
return fmt.Errorf("config error: %w", err)
}
Expand Down Expand Up @@ -126,22 +128,26 @@ func triggerPlugins(ctx context.Context, opa *sdk.OPA, names []string) error {
return nil
}

func setupLogging(level, format string) (logging.Logger, logging.Logger, error) {
func setupLogging(level, format, timestampFormat string) (logging.Logger, logging.Logger, error) {

lvl, err := internal_logging.GetLevel(level)
if err != nil {
return nil, nil, err
}

logging.Get().SetFormatter(internal_logging.GetFormatter(format))
if timestampFormat == "" {
timestampFormat = os.Getenv("OPA_LOG_TIMESTAMP_FORMAT")
}

logging.Get().SetFormatter(internal_logging.GetFormatter(format, timestampFormat))
logging.Get().SetLevel(lvl)

stdLogger := logging.New()
stdLogger.SetLevel(lvl)
stdLogger.SetFormatter(internal_logging.GetFormatter(format))
stdLogger.SetFormatter(internal_logging.GetFormatter(format, timestampFormat))

consoleLogger := logging.New()
consoleLogger.SetFormatter(internal_logging.GetFormatter(format))
consoleLogger.SetFormatter(internal_logging.GetFormatter(format, timestampFormat))

return stdLogger, consoleLogger, nil
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/internal/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Params struct {
OutputFormat *util.EnumFlag // output format (default: pretty)
LogLevel *util.EnumFlag // log level for plugins
LogFormat *util.EnumFlag // log format for plugins
LogTimestampFormat string // log timestamp format for plugins
BundlePaths []string // explicit paths of bundles to inject into the configuration
Decision string // decision to evaluate (overrides default decision set by configuration)
}
Expand All @@ -40,7 +41,7 @@ func NewParams(w io.Writer) *Params {
//
// NOTE(tsandall): consider expanding functionality:
//
// * specialized output formats (e.g., pretty/non-JSON outputs)
// * specialized output formats (e.g., pretty/non-JSON outputs)
// * exit codes set by convention or policy (e.g,. non-empty set => error)
// * support for new input file formats beyond JSON and YAML
func Exec(ctx context.Context, opa *sdk.OPA, params *Params) error {
Expand Down
12 changes: 10 additions & 2 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type runCmdParams struct {
minTLSVersion *util.EnumFlag
logLevel *util.EnumFlag
logFormat *util.EnumFlag
logTimestampFormat string
algorithm string
scope string
pubKey string
Expand Down Expand Up @@ -186,6 +187,7 @@ To skip bundle verification, use the --skip-verify flag.
runCommand.Flags().Var(cmdParams.minTLSVersion, "min-tls-version", "set minimum TLS version to be used by OPA's server")
runCommand.Flags().VarP(cmdParams.logLevel, "log-level", "l", "set log level")
runCommand.Flags().Var(cmdParams.logFormat, "log-format", "set log format")
runCommand.Flags().StringVar(&cmdParams.logTimestampFormat, "log-timestamp-format", "", "set log timestamp format (OPA_LOG_TIMESTAMP_FORMAT environment variable)")
runCommand.Flags().IntVar(&cmdParams.rt.GracefulShutdownPeriod, "shutdown-grace-period", 10, "set the time (in seconds) that the server will wait to gracefully shut down")
runCommand.Flags().IntVar(&cmdParams.rt.ShutdownWaitPeriod, "shutdown-wait-period", 0, "set the time (in seconds) that the server will wait before initiating shutdown")
addConfigOverrides(runCommand.Flags(), &cmdParams.rt.ConfigOverrides)
Expand Down Expand Up @@ -254,9 +256,15 @@ func initRuntime(ctx context.Context, params runCmdParams, args []string) (*runt
params.rt.Authorization = authorizationScheme[params.authorization.String()]
params.rt.MinTLSVersion = minTLSVersions[params.minTLSVersion.String()]
params.rt.Certificate = cert

timestampFormat := params.logTimestampFormat
if timestampFormat == "" {
timestampFormat = os.Getenv("OPA_LOG_TIMESTAMP_FORMAT")
}
params.rt.Logging = runtime.LoggingConfig{
Level: params.logLevel.String(),
Format: params.logFormat.String(),
Level: params.logLevel.String(),
Format: params.logFormat.String(),
TimestampFormat: timestampFormat,
}
params.rt.Paths = args
params.rt.Filter = loaderFilter{
Expand Down
72 changes: 72 additions & 0 deletions cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ package cmd
import (
"bytes"
"context"
"encoding/json"
"strings"
"testing"
"time"

"github.com/open-policy-agent/opa/logging"
"github.com/open-policy-agent/opa/test/e2e"
)

Expand Down Expand Up @@ -100,6 +104,74 @@ func TestInitRuntimeVerifyNonBundle(t *testing.T) {
}
}

func TestRunServerCheckLogTimestampFormat(t *testing.T) {
for _, format := range []string{time.Kitchen, time.RFC3339Nano} {
t.Run(format, func(t *testing.T) {
t.Run("parameter", func(t *testing.T) {
params := newTestRunParams()
params.logTimestampFormat = format
checkLogTimeStampFormat(t, params, format)
})
t.Run("environment variable", func(t *testing.T) {
t.Setenv("OPA_LOG_TIMESTAMP_FORMAT", format)
Copy link
Contributor

@srenatus srenatus Aug 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat. I didn't know that function. Pretty handy. https://pkg.go.dev/testing#T.Setenv

params := newTestRunParams()
checkLogTimeStampFormat(t, params, format)
})
})
}
}

func checkLogTimeStampFormat(t *testing.T, params runCmdParams, format string) {
ctx, cancel := context.WithCancel(context.Background())

rt, err := initRuntime(ctx, params, nil)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
var buf bytes.Buffer
logger := rt.Manager.Logger().(*logging.StandardLogger)
logger.SetOutput(&buf)
testRuntime := e2e.WrapRuntime(ctx, cancel, rt)

done := make(chan bool)
go func() {
err := rt.Serve(ctx)
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
done <- true
}()

err = testRuntime.WaitForServer()
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}

validateBasicServe(t, testRuntime)

cancel()
<-done

for _, line := range strings.Split(buf.String(), "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
var rec struct {
Time string `json:"time"`
}
if err := json.Unmarshal([]byte(line), &rec); err != nil {
t.Fatalf("incorrect log message %s: %v", line, err)
}
if rec.Time == "" {
t.Fatalf("the time field is empty in log message: %s", line)
}
if _, err := time.Parse(format, rec.Time); err != nil {
t.Fatalf("incorrect timestamp format %q: %v", rec.Time, err)
}
}
}

func newTestRunParams() runCmdParams {
params := newRunParams()
params.rt.GracefulShutdownPeriod = 1
Expand Down
4 changes: 2 additions & 2 deletions docs/content/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ opa exec <path> [<path> [...]] [flags]
-h, --help help for exec
--log-format {text,json,json-pretty} set log format (default json)
-l, --log-level {debug,info,error} set log level (default error)
--log-timestamp-format string set log timestamp format (OPA_LOG_TIMESTAMP_FORMAT environment variable)
--set stringArray override config values on the command line (use commas to specify multiple values)
--set-file stringArray override config values with files on the command line (use commas to specify multiple values)
```
Expand Down Expand Up @@ -824,6 +825,7 @@ opa run [flags]
--ignore strings set file and directory names to ignore during loading (e.g., '.*' excludes hidden files)
--log-format {text,json,json-pretty} set log format (default json)
-l, --log-level {debug,info,error} set log level (default info)
--log-timestamp-format string set log timestamp format (OPA_LOG_TIMESTAMP_FORMAT environment variable)
-m, --max-errors int set the number of errors to allow before compilation fails early (default 10)
--min-tls-version {1.0,1.1,1.2,1.3} set minimum TLS version to be used by OPA's server (default 1.2)
--pprof enables pprof endpoints
Expand Down Expand Up @@ -1071,5 +1073,3 @@ opa version [flags]
-c, --check check for latest OPA release
-h, --help help for version
```


9 changes: 5 additions & 4 deletions internal/logging/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import (
"fmt"
"strings"

"github.com/open-policy-agent/opa/logging"
"github.com/sirupsen/logrus"

"github.com/open-policy-agent/opa/logging"
)

func GetLevel(level string) (logging.Level, error) {
Expand All @@ -29,14 +30,14 @@ func GetLevel(level string) (logging.Level, error) {
}
}

func GetFormatter(format string) logrus.Formatter {
func GetFormatter(format, timestampFormat string) logrus.Formatter {
switch format {
case "text":
return &prettyFormatter{}
case "json-pretty":
return &logrus.JSONFormatter{PrettyPrint: true}
return &logrus.JSONFormatter{PrettyPrint: true, TimestampFormat: timestampFormat}
default:
return &logrus.JSONFormatter{}
return &logrus.JSONFormatter{TimestampFormat: timestampFormat}
}
}

Expand Down
11 changes: 6 additions & 5 deletions runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,9 @@ type Params struct {

// LoggingConfig stores the configuration for OPA's logging behaviour.
type LoggingConfig struct {
Level string
Format string
Level string
Format string
TimestampFormat string
}

// NewParams returns a new Params object.
Expand Down Expand Up @@ -267,7 +268,7 @@ func NewRuntime(ctx context.Context, params Params) (*Runtime, error) {
// that the logging configuration is applied. Once we remove all usage of
// the global logger and we remove the API that allows callers to access the
// global logger, we can remove this.
logging.Get().SetFormatter(internal_logging.GetFormatter(params.Logging.Format))
logging.Get().SetFormatter(internal_logging.GetFormatter(params.Logging.Format, params.Logging.TimestampFormat))
logging.Get().SetLevel(level)

var logger logging.Logger
Expand All @@ -277,7 +278,7 @@ func NewRuntime(ctx context.Context, params Params) (*Runtime, error) {
} else {
stdLogger := logging.New()
stdLogger.SetLevel(level)
stdLogger.SetFormatter(internal_logging.GetFormatter(params.Logging.Format))
stdLogger.SetFormatter(internal_logging.GetFormatter(params.Logging.Format, params.Logging.TimestampFormat))
logger = stdLogger
}

Expand Down Expand Up @@ -308,7 +309,7 @@ func NewRuntime(ctx context.Context, params Params) (*Runtime, error) {
consoleLogger := params.ConsoleLogger
if consoleLogger == nil {
l := logging.New()
l.SetFormatter(internal_logging.GetFormatter(params.Logging.Format))
l.SetFormatter(internal_logging.GetFormatter(params.Logging.Format, params.Logging.TimestampFormat))
consoleLogger = l
}

Expand Down