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
## 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

View File

@@ -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"]

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`.
* **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

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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 {