Update to use LOG_FORMAT environment variable
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user