Update to use LOG_FORMAT environment variable

This commit is contained in:
Arthur Barr
2018-02-22 13:21:16 +00:00
parent c9cc1741c7
commit b497a04dcb
7 changed files with 70 additions and 38 deletions

View File

@@ -1,6 +1,7 @@
# Change log # Change log
## master ## 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. * 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` now waits until MQSC scripts in `/etc/mqm` have been applied
* `chkmqready` and `chkmqhealthy` now run as the "mqm" user * `chkmqready` and `chkmqhealthy` now run as the "mqm" user

View File

@@ -26,7 +26,9 @@ RUN go build ./cmd/runmqserver/
RUN go build ./cmd/chkmqready/ RUN go build ./cmd/chkmqready/
RUN go build ./cmd/chkmqhealthy/ RUN go build ./cmd/chkmqhealthy/
# Run all unit tests # 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/... RUN go test -v ./internal/...
############################################################################### ###############################################################################
@@ -63,6 +65,6 @@ RUN chmod ug+x /usr/local/bin/runmqserver \
# Always use port 1414 # Always use port 1414
EXPOSE 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"] ENTRYPOINT ["runmqserver"]

View File

@@ -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`. * **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. * **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. * **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 # Issues and contributions

View File

@@ -74,23 +74,15 @@ func logTermination(args ...interface{}) {
log.Error(msg) log.Error(msg)
} }
func jsonLogs() bool { func getLogFormat() string {
e := os.Getenv("MQ_ALPHA_JSON_LOGS") return os.Getenv("LOG_FORMAT")
if e == "true" || e == "1" {
return true
}
return false
}
func mirrorToStdout(msg string) {
fmt.Println(msg)
} }
func formatSimple(datetime string, message string) string { func formatSimple(datetime string, message string) string {
return fmt.Sprintf("%v %v\n", datetime, message) 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 // Always use the JSON log as the source
// Put the queue manager name in quotes to handle cases like name=.. // Put the queue manager name in quotes to handle cases like name=..
qm, err := mqini.GetQueueManager(name) qm, err := mqini.GetQueueManager(name)
@@ -99,15 +91,7 @@ func mirrorLogs(ctx context.Context, wg *sync.WaitGroup, name string, fromStart
return nil, err return nil, err
} }
f := filepath.Join(mqini.GetErrorLogDirectory(qm), "AMQERR01.json") f := filepath.Join(mqini.GetErrorLogDirectory(qm), "AMQERR01.json")
if jsonLogs() { return mirrorLog(ctx, wg, f, fromStart, mf)
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)))
})
} }
func configureDebugLogger() { func configureDebugLogger() {
@@ -119,8 +103,12 @@ func configureDebugLogger() {
} }
} }
func configureLogger() { func configureLogger() (mirrorFunc, error) {
if jsonLogs() { // Set the simple formatter by default
log.SetFormatter(new(simpleTextFormatter))
f := getLogFormat()
switch f {
case "json":
formatter := logrus.JSONFormatter{ formatter := logrus.JSONFormatter{
FieldMap: logrus.FieldMap{ FieldMap: logrus.FieldMap{
logrus.FieldKeyMsg: "message", logrus.FieldKeyMsg: "message",
@@ -130,7 +118,17 @@ func configureLogger() {
TimestampFormat: timestampFormat, TimestampFormat: timestampFormat,
} }
logrus.SetFormatter(&formatter) logrus.SetFormatter(&formatter)
} else { return func(msg string) {
log.SetFormatter(new(simpleTextFormatter)) 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)
} }
} }

View File

@@ -30,9 +30,13 @@ import (
) )
func doMain() error { func doMain() error {
configureLogger() mf, err := configureLogger()
if err != nil {
logTermination(err)
return err
}
configureDebugLogger() configureDebugLogger()
err := ready.Clear() err = ready.Clear()
if err != nil { if err != nil {
logTermination(err) logTermination(err)
return err return err
@@ -83,7 +87,7 @@ func doMain() error {
cancelMirror() cancelMirror()
}() }()
// TODO: Use the error channel // TODO: Use the error channel
_, err = mirrorLogs(ctx, &wg, name, newQM) _, err = mirrorLogs(ctx, &wg, name, newQM, mf)
if err != nil { if err != nil {
logTermination(err) logTermination(err)
return err return err

View File

@@ -48,6 +48,7 @@ func TestLicenseNotSet(t *testing.T) {
if rc != 1 { if rc != 1 {
t.Errorf("Expected rc=1, got rc=%v", rc) t.Errorf("Expected rc=1, got rc=%v", rc)
} }
expectTerminationMessage(t)
} }
func TestLicenseView(t *testing.T) { func TestLicenseView(t *testing.T) {
@@ -222,6 +223,7 @@ func TestCreateQueueManagerFail(t *testing.T) {
if rc != 1 { if rc != 1 {
t.Errorf("Expected rc=1, got rc=%v", rc) t.Errorf("Expected rc=1, got rc=%v", rc)
} }
expectTerminationMessage(t)
} }
// TestStartQueueManagerFail causes a failure of `strmqm` // TestStartQueueManagerFail causes a failure of `strmqm`
@@ -234,7 +236,7 @@ func TestStartQueueManagerFail(t *testing.T) {
img, _, err := cli.ImageInspectWithRaw(context.Background(), imageName()) img, _, err := cli.ImageInspectWithRaw(context.Background(), imageName())
oldEntrypoint := strings.Join(img.Config.Entrypoint, " ") oldEntrypoint := strings.Join(img.Config.Entrypoint, " ")
containerConfig := container.Config{ 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. // 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. // 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}, 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 { if rc != 1 {
t.Errorf("Expected rc=1, got rc=%v", rc) t.Errorf("Expected rc=1, got rc=%v", rc)
} }
m := terminationMessage(t) expectTerminationMessage(t)
if m == "" {
t.Error("Expected termination message to be set")
}
} }
// TestVolumeUnmount runs a queue manager with a volume, and then forces an // TestVolumeUnmount runs a queue manager with a volume, and then forces an
@@ -453,7 +452,7 @@ func TestErrorLogRotation(t *testing.T) {
"LICENSE=accept", "LICENSE=accept",
"MQ_QMGR_NAME=" + qmName, "MQ_QMGR_NAME=" + qmName,
"MQMAXERRORLOGSIZE=65536", "MQMAXERRORLOGSIZE=65536",
"MQ_ALPHA_JSON_LOGS=true", "LOG_FORMAT=json",
}, },
ExposedPorts: nat.PortSet{ ExposedPorts: nat.PortSet{
"1414/tcp": struct{}{}, "1414/tcp": struct{}{},
@@ -505,7 +504,7 @@ func TestErrorLogRotation(t *testing.T) {
} }
} }
func TestJSONLogs(t *testing.T) { func TestJSONLogFormat(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewEnvClient()
if err != nil { if err != nil {
@@ -514,8 +513,7 @@ func TestJSONLogs(t *testing.T) {
containerConfig := container.Config{ containerConfig := container.Config{
Env: []string{ Env: []string{
"LICENSE=accept", "LICENSE=accept",
"MQ_QMGR_NAME=qm1", "LOG_FORMAT=json",
"MQ_ALPHA_JSON_LOGS=1",
}, },
} }
id := runContainer(t, cli, &containerConfig) 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 // TestMQJSONDisabled tests the case where MQ's JSON logging feature is
// specifically disabled (which will disable log mirroring) // specifically disabled (which will disable log mirroring)
func TestMQJSONDisabled(t *testing.T) { func TestMQJSONDisabled(t *testing.T) {

View File

@@ -102,6 +102,13 @@ func terminationMessage(t *testing.T) string {
return string(b) 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) { func cleanContainer(t *testing.T, cli *client.Client, ID string) {
i, err := cli.ContainerInspect(context.Background(), ID) i, err := cli.ContainerInspect(context.Background(), ID)
if err == nil { if err == nil {