From b497a04dcb8e5ca0c360dea7f89c3be9231a0e75 Mon Sep 17 00:00:00 2001 From: Arthur Barr Date: Thu, 22 Feb 2018 13:21:16 +0000 Subject: [PATCH] Update to use LOG_FORMAT environment variable --- CHANGELOG.md | 1 + Dockerfile-server | 6 ++-- README.md | 1 + cmd/runmqserver/logging.go | 46 ++++++++++++++--------------- cmd/runmqserver/main.go | 10 +++++-- test/docker/docker_api_test.go | 37 +++++++++++++++++------ test/docker/docker_api_test_util.go | 7 +++++ 7 files changed, 70 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a27d71..552fe90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Change log ## master +* Container's stdout can now be set to JSON format (set LOG_FORMAT=json) * MQ error logs (in JSON or plain text) are now mirrored on stdout for the container. * `chkmqready` now waits until MQSC scripts in `/etc/mqm` have been applied * `chkmqready` and `chkmqhealthy` now run as the "mqm" user diff --git a/Dockerfile-server b/Dockerfile-server index 635fcfb..93abc65 100644 --- a/Dockerfile-server +++ b/Dockerfile-server @@ -26,7 +26,9 @@ RUN go build ./cmd/runmqserver/ RUN go build ./cmd/chkmqready/ RUN go build ./cmd/chkmqhealthy/ # Run all unit tests -RUN go test -v ./cmd/... +RUN go test -v ./cmd/runmqserver/ +RUN go test -v ./cmd/chkmqready/ +RUN go test -v ./cmd/chkmqhealthy/ RUN go test -v ./internal/... ############################################################################### @@ -63,6 +65,6 @@ RUN chmod ug+x /usr/local/bin/runmqserver \ # Always use port 1414 EXPOSE 1414 -ENV LANG=en_US.UTF-8 AMQ_DIAGNOSTIC_MSG_SEVERITY=1 AMQ_ADDITIONAL_JSON_LOG=1 +ENV LANG=en_US.UTF-8 AMQ_DIAGNOSTIC_MSG_SEVERITY=1 AMQ_ADDITIONAL_JSON_LOG=1 LOG_FORMAT=simple ENTRYPOINT ["runmqserver"] diff --git a/README.md b/README.md index 30af323..aeb8b43 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ In order to use the image, it is necessary to accept the terms of the IBM MQ lic * **LICENSE** - Set this to `accept` to agree to the MQ Advanced for Developers license. If you wish to see the license you can set this to `view`. * **LANG** - Set this to the language you would like the license to be printed in. * **MQ_QMGR_NAME** - Set this to the name you want your Queue Manager to be created with. +* **LOG_FORMAT** - Set this to change the format of the logs which are printed on the container's stdout. Set to "json" to use JSON format (JSON object per line); set to "simple" to use a simple human-readable. Defaults to "simple". # Issues and contributions diff --git a/cmd/runmqserver/logging.go b/cmd/runmqserver/logging.go index f885baa..6370efc 100644 --- a/cmd/runmqserver/logging.go +++ b/cmd/runmqserver/logging.go @@ -74,23 +74,15 @@ func logTermination(args ...interface{}) { log.Error(msg) } -func jsonLogs() bool { - e := os.Getenv("MQ_ALPHA_JSON_LOGS") - if e == "true" || e == "1" { - return true - } - return false -} - -func mirrorToStdout(msg string) { - fmt.Println(msg) +func getLogFormat() string { + return os.Getenv("LOG_FORMAT") } func formatSimple(datetime string, message string) string { return fmt.Sprintf("%v %v\n", datetime, message) } -func mirrorLogs(ctx context.Context, wg *sync.WaitGroup, name string, fromStart bool) (chan error, error) { +func mirrorLogs(ctx context.Context, wg *sync.WaitGroup, name string, fromStart bool, mf mirrorFunc) (chan error, error) { // Always use the JSON log as the source // Put the queue manager name in quotes to handle cases like name=.. qm, err := mqini.GetQueueManager(name) @@ -99,15 +91,7 @@ func mirrorLogs(ctx context.Context, wg *sync.WaitGroup, name string, fromStart return nil, err } f := filepath.Join(mqini.GetErrorLogDirectory(qm), "AMQERR01.json") - if jsonLogs() { - return mirrorLog(ctx, wg, f, fromStart, mirrorToStdout) - } - return mirrorLog(ctx, wg, f, fromStart, func(msg string) { - // Parse the JSON message, and print a simplified version - var obj map[string]interface{} - json.Unmarshal([]byte(msg), &obj) - fmt.Printf(formatSimple(obj["ibm_datetime"].(string), obj["message"].(string))) - }) + return mirrorLog(ctx, wg, f, fromStart, mf) } func configureDebugLogger() { @@ -119,8 +103,12 @@ func configureDebugLogger() { } } -func configureLogger() { - if jsonLogs() { +func configureLogger() (mirrorFunc, error) { + // Set the simple formatter by default + log.SetFormatter(new(simpleTextFormatter)) + f := getLogFormat() + switch f { + case "json": formatter := logrus.JSONFormatter{ FieldMap: logrus.FieldMap{ logrus.FieldKeyMsg: "message", @@ -130,7 +118,17 @@ func configureLogger() { TimestampFormat: timestampFormat, } logrus.SetFormatter(&formatter) - } else { - log.SetFormatter(new(simpleTextFormatter)) + return func(msg string) { + fmt.Println(msg) + }, nil + case "simple": + return func(msg string) { + // Parse the JSON message, and print a simplified version + var obj map[string]interface{} + json.Unmarshal([]byte(msg), &obj) + fmt.Printf(formatSimple(obj["ibm_datetime"].(string), obj["message"].(string))) + }, nil + default: + return nil, fmt.Errorf("invalid value for LOG_FORMAT: %v", f) } } diff --git a/cmd/runmqserver/main.go b/cmd/runmqserver/main.go index 8a24dce..72efa06 100644 --- a/cmd/runmqserver/main.go +++ b/cmd/runmqserver/main.go @@ -30,9 +30,13 @@ import ( ) func doMain() error { - configureLogger() + mf, err := configureLogger() + if err != nil { + logTermination(err) + return err + } configureDebugLogger() - err := ready.Clear() + err = ready.Clear() if err != nil { logTermination(err) return err @@ -83,7 +87,7 @@ func doMain() error { cancelMirror() }() // TODO: Use the error channel - _, err = mirrorLogs(ctx, &wg, name, newQM) + _, err = mirrorLogs(ctx, &wg, name, newQM, mf) if err != nil { logTermination(err) return err diff --git a/test/docker/docker_api_test.go b/test/docker/docker_api_test.go index 842b95b..08be306 100644 --- a/test/docker/docker_api_test.go +++ b/test/docker/docker_api_test.go @@ -48,6 +48,7 @@ func TestLicenseNotSet(t *testing.T) { if rc != 1 { t.Errorf("Expected rc=1, got rc=%v", rc) } + expectTerminationMessage(t) } func TestLicenseView(t *testing.T) { @@ -222,6 +223,7 @@ func TestCreateQueueManagerFail(t *testing.T) { if rc != 1 { t.Errorf("Expected rc=1, got rc=%v", rc) } + expectTerminationMessage(t) } // TestStartQueueManagerFail causes a failure of `strmqm` @@ -234,7 +236,7 @@ func TestStartQueueManagerFail(t *testing.T) { img, _, err := cli.ImageInspectWithRaw(context.Background(), imageName()) oldEntrypoint := strings.Join(img.Config.Entrypoint, " ") containerConfig := container.Config{ - Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"}, + Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1", "DEBUG=1"}, // Override the entrypoint to replace `strmqm` with a script which deletes the queue manager. // This will cause `strmqm` to return with an exit code of 72. Entrypoint: []string{"bash", "-c", "echo '#!/bin/bash\ndltmqm $@ && strmqm $@' > /opt/mqm/bin/strmqm && exec " + oldEntrypoint}, @@ -245,10 +247,7 @@ func TestStartQueueManagerFail(t *testing.T) { if rc != 1 { t.Errorf("Expected rc=1, got rc=%v", rc) } - m := terminationMessage(t) - if m == "" { - t.Error("Expected termination message to be set") - } + expectTerminationMessage(t) } // TestVolumeUnmount runs a queue manager with a volume, and then forces an @@ -453,7 +452,7 @@ func TestErrorLogRotation(t *testing.T) { "LICENSE=accept", "MQ_QMGR_NAME=" + qmName, "MQMAXERRORLOGSIZE=65536", - "MQ_ALPHA_JSON_LOGS=true", + "LOG_FORMAT=json", }, ExposedPorts: nat.PortSet{ "1414/tcp": struct{}{}, @@ -505,7 +504,7 @@ func TestErrorLogRotation(t *testing.T) { } } -func TestJSONLogs(t *testing.T) { +func TestJSONLogFormat(t *testing.T) { t.Parallel() cli, err := client.NewEnvClient() if err != nil { @@ -514,8 +513,7 @@ func TestJSONLogs(t *testing.T) { containerConfig := container.Config{ Env: []string{ "LICENSE=accept", - "MQ_QMGR_NAME=qm1", - "MQ_ALPHA_JSON_LOGS=1", + "LOG_FORMAT=json", }, } id := runContainer(t, cli, &containerConfig) @@ -537,6 +535,27 @@ func TestJSONLogs(t *testing.T) { } } +func TestBadLogFormat(t *testing.T) { + t.Parallel() + cli, err := client.NewEnvClient() + if err != nil { + t.Fatal(err) + } + containerConfig := container.Config{ + Env: []string{ + "LICENSE=accept", + "LOG_FORMAT=fake", + }, + } + id := runContainer(t, cli, &containerConfig) + defer cleanContainer(t, cli, id) + rc := waitForContainer(t, cli, id, 5) + if rc != 1 { + t.Errorf("Expected rc=1, got rc=%v", rc) + } + expectTerminationMessage(t) +} + // TestMQJSONDisabled tests the case where MQ's JSON logging feature is // specifically disabled (which will disable log mirroring) func TestMQJSONDisabled(t *testing.T) { diff --git a/test/docker/docker_api_test_util.go b/test/docker/docker_api_test_util.go index 7bdd11b..0dd5092 100644 --- a/test/docker/docker_api_test_util.go +++ b/test/docker/docker_api_test_util.go @@ -102,6 +102,13 @@ func terminationMessage(t *testing.T) string { return string(b) } +func expectTerminationMessage(t *testing.T) { + m := terminationMessage(t) + if m == "" { + t.Error("Expected termination message to be set") + } +} + func cleanContainer(t *testing.T, cli *client.Client, ID string) { i, err := cli.ContainerInspect(context.Background(), ID) if err == nil {