Enhanced console logging

Introduce new environment variables:
  * MQ_LOGGING_CONSOLE_SOURCE
  * MQ_LOGGING_CONSOLE_FORMAT
  * MQ_LOGGING_CONSOLE_EXCLUDE_ID

Authored-by: Avinash Ganesh <avinash.v.g@in.ibm.com> and BHAVYA K R <bhavkris@in.ibm.com>
This commit is contained in:
Avinash Ganesh
2023-01-30 19:49:58 +05:30
committed by GitHub Enterprise
parent 5c4422badf
commit 4588cd44f9
8 changed files with 645 additions and 41 deletions

View File

@@ -138,6 +138,8 @@ EXPOSE 1414 9157 9443
ENV MQ_OVERRIDE_DATA_PATH=/mnt/mqm/data MQ_OVERRIDE_INSTALLATION_NAME=Installation1 MQ_USER_NAME="mqm" PATH="${PATH}:/opt/mqm/bin" ENV MQ_OVERRIDE_DATA_PATH=/mnt/mqm/data MQ_OVERRIDE_INSTALLATION_NAME=Installation1 MQ_USER_NAME="mqm" PATH="${PATH}:/opt/mqm/bin"
ENV MQ_GRACE_PERIOD=30 ENV MQ_GRACE_PERIOD=30
ENV LANG=en_US.UTF-8 AMQ_DIAGNOSTIC_MSG_SEVERITY=1 AMQ_ADDITIONAL_JSON_LOG=1 LOG_FORMAT=basic ENV LANG=en_US.UTF-8 AMQ_DIAGNOSTIC_MSG_SEVERITY=1 AMQ_ADDITIONAL_JSON_LOG=1 LOG_FORMAT=basic
ENV MQ_LOGGING_CONSOLE_EXCLUDE_ID=AMQ5041I,AMQ5052I,AMQ5051I,AMQ5037I,AMQ5975I
ENV WLP_LOGGING_MESSAGE_FORMAT=json
# We can run as any UID # We can run as any UID
USER 1001 USER 1001
ENV MQ_CONNAUTH_USE_HTP=false ENV MQ_CONNAUTH_USE_HTP=false

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018, 2021 © Copyright IBM Corporation 2018, 2023
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"strings"
"syscall" "syscall"
"github.com/ibm-messaging/mq-container/internal/htpasswd" "github.com/ibm-messaging/mq-container/internal/htpasswd"
@@ -30,7 +31,20 @@ import (
var log *logger.Logger var log *logger.Logger
func getLogFormat() string { func getLogFormat() string {
return os.Getenv("LOG_FORMAT") logFormat := strings.ToLower(strings.TrimSpace(os.Getenv("MQ_LOGGING_CONSOLE_FORMAT")))
//old-style env var is used.
if logFormat == "" {
logFormat = strings.ToLower(strings.TrimSpace(os.Getenv("LOG_FORMAT")))
}
if logFormat != "" && (logFormat == "basic" || logFormat == "json") {
return logFormat
} else {
//this is the case where value is either empty string or set to something other than "basic"/"json"
logFormat = "basic"
}
return logFormat
} }
func getDebug() bool { func getDebug() bool {

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2022 © Copyright IBM Corporation 2017, 2023
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -58,7 +58,20 @@ func logTermination(args ...interface{}) {
} }
func getLogFormat() string { func getLogFormat() string {
return os.Getenv("LOG_FORMAT") logFormat := strings.ToLower(strings.TrimSpace(os.Getenv("MQ_LOGGING_CONSOLE_FORMAT")))
//old-style env var is used.
if logFormat == "" {
logFormat = strings.ToLower(strings.TrimSpace(os.Getenv("LOG_FORMAT")))
}
if logFormat != "" && (logFormat == "basic" || logFormat == "json") {
return logFormat
} else {
//this is the case where value is either empty string or set to something other than "basic"/"json"
logFormat = "basic"
}
return logFormat
} }
// formatBasic formats a log message parsed from JSON, as "basic" text // formatBasic formats a log message parsed from JSON, as "basic" text
@@ -81,6 +94,92 @@ func formatBasic(obj map[string]interface{}) string {
} }
// Convert time zone information from some logs (e.g. Liberty) for consistency // Convert time zone information from some logs (e.g. Liberty) for consistency
obj["ibm_datetime"] = strings.Replace(obj["ibm_datetime"].(string), "+0000", "Z", 1) obj["ibm_datetime"] = strings.Replace(obj["ibm_datetime"].(string), "+0000", "Z", 1)
if obj["type"] != nil && (obj["type"] == "liberty_message" || obj["type"] == "liberty_trace") {
timeStamp := obj["ibm_datetime"]
threadID := ""
srtModuleName := ""
logLevel := ""
ibmClassName := ""
srtIbmClassName := ""
ibmMethodName := ""
message := ""
if obj["loglevel"] != nil {
//threadID is captured below
if obj["ibm_threadId"] != nil {
threadID = obj["ibm_threadId"].(string)
}
//logLevel character to be mirrored in console web server logging is decided below
logLevelTmp := obj["loglevel"].(string)
switch logLevelTmp {
case "AUDIT":
logLevel = "A"
case "INFO":
logLevel = "I"
case "EVENT":
logLevel = "1"
case "ENTRY":
logLevel = ">"
case "EXIT":
logLevel = "<"
case "FINE":
logLevel = "1"
case "FINER":
logLevel = "2"
case "FINEST":
logLevel = "3"
default:
logLevel = string(logLevelTmp[0])
}
//This is a 13 characters string present in extracted out of module node
if obj["module"] != nil {
srtModuleNameArr := strings.Split(obj["module"].(string), ".")
arrLen := len(srtModuleNameArr)
srtModuleName = srtModuleNameArr[arrLen-1]
if len(srtModuleName) > 13 {
srtModuleName = srtModuleName[0:13]
}
}
if obj["ibm_className"] != nil {
ibmClassName = obj["ibm_className"].(string)
//A 13 character string is extracted from class name. This is required for FINE, FINER & FINEST log lines
ibmClassNameArr := strings.Split(ibmClassName, ".")
arrLen := len(ibmClassNameArr)
srtIbmClassName = ibmClassNameArr[arrLen-1]
if len(srtModuleName) > 13 {
srtIbmClassName = srtIbmClassName[0:13]
}
}
if obj["ibm_methodName"] != nil {
ibmMethodName = obj["ibm_methodName"].(string)
}
if obj["message"] != nil {
message = obj["message"].(string)
}
//For AUDIT & INFO logging
if logLevel == "A" || logLevel == "I" {
return fmt.Sprintf("%s %s %-13s %s %s %s %s\n", timeStamp, threadID, srtModuleName, logLevel, ibmClassName, ibmMethodName, message)
}
//For EVENT logLevel
if logLevelTmp == "EVENT" {
return fmt.Sprintf("%s %s %-13s %s %s\n", timeStamp, threadID, srtModuleName, logLevel, message)
}
//For ENTRY & EXIT
if logLevel == ">" || logLevel == "<" {
return fmt.Sprintf("%s %s %-13s %s %s %s\n", timeStamp, threadID, srtModuleName, logLevel, ibmMethodName, message)
}
//For deeper log levels
if logLevelTmp == "FINE" || logLevel == "2" || logLevel == "3" {
return fmt.Sprintf("%s %s %-13s %s %s %s %s\n", timeStamp, threadID, srtIbmClassName, logLevel, ibmClassName, ibmMethodName, message)
}
}
}
return fmt.Sprintf("%s %s\n", obj["ibm_datetime"], obj["message"]) return fmt.Sprintf("%s %s\n", obj["ibm_datetime"], obj["message"])
} }
@@ -131,6 +230,11 @@ func configureLogger(name string) (mirrorFunc, error) {
return nil, err return nil, err
} }
return func(msg string, isQMLog bool) bool { return func(msg string, isQMLog bool) bool {
arrLoggingConsoleExcludeIds := strings.Split(strings.ToUpper(os.Getenv("MQ_LOGGING_CONSOLE_EXCLUDE_ID")), ",")
if isExcludedMsgIdPresent(msg, arrLoggingConsoleExcludeIds) {
//If excluded id is present do not mirror it, return back
return false
}
// Check if the message is JSON // Check if the message is JSON
if len(msg) > 0 && msg[0] == '{' { if len(msg) > 0 && msg[0] == '{' {
obj, err := processLogMessage(msg) obj, err := processLogMessage(msg)
@@ -155,6 +259,11 @@ func configureLogger(name string) (mirrorFunc, error) {
return nil, err return nil, err
} }
return func(msg string, isQMLog bool) bool { return func(msg string, isQMLog bool) bool {
arrLoggingConsoleExcludeIds := strings.Split(strings.ToUpper(os.Getenv("MQ_LOGGING_CONSOLE_EXCLUDE_ID")), ",")
if isExcludedMsgIdPresent(msg, arrLoggingConsoleExcludeIds) {
//If excluded id is present do not mirror it, return back
return false
}
// Check if the message is JSON // Check if the message is JSON
if len(msg) > 0 && msg[0] == '{' { if len(msg) > 0 && msg[0] == '{' {
// Parse the JSON message, and print a simplified version // Parse the JSON message, and print a simplified version
@@ -197,6 +306,16 @@ func filterQMLogMessage(obj map[string]interface{}) bool {
return false return false
} }
// Function to check if ids provided in MQ_LOGGING_CONSOLE_EXCLUDE_ID are present in given log line or not
func isExcludedMsgIdPresent(msg string, envExcludeIds []string) bool {
for _, id := range envExcludeIds {
if id != "" && strings.Contains(msg, strings.TrimSpace(id)) {
return true
}
}
return false
}
func logDiagnostics() { func logDiagnostics() {
if getDebug() { if getDebug() {
log.Debug("--- Start Diagnostics ---") log.Debug("--- Start Diagnostics ---")
@@ -238,3 +357,68 @@ func logDiagnostics() {
log.Debug("--- End Diagnostics ---") log.Debug("--- End Diagnostics ---")
} }
} }
// Returns the value of MQ_LOGGING_CONSOLE_SOURCE environment variable
func getMQLogConsoleSource() string {
return strings.ToLower(strings.TrimSpace(os.Getenv("MQ_LOGGING_CONSOLE_SOURCE")))
}
// Function to check if valid values are provided for environment variable MQ_LOGGING_CONSOLE_SOURCE. If not valid, main program throws a warning to console
func isLogConsoleSourceValid() bool {
mqLogSource := getMQLogConsoleSource()
retValue := false
//If nothing is set, we will mirror all, so valid
if mqLogSource == "" {
return true
}
logConsoleSource := strings.Split(mqLogSource, ",")
//This will find out if the environment variable contains permitted values and is comma separated
for _, src := range logConsoleSource {
switch strings.TrimSpace(src) {
//If it is a permitted value, it is valid. Keep it as true, but dont return it. We may encounter something junk soon
case "qmgr", "web", "":
retValue = true
//If invalid entry arrives in-between/anywhere, just return false, there is no turning back
default:
return false
}
}
return retValue
}
// To check which all logs have to be mirrored
func checkLogSourceForMirroring(source string) bool {
logsrcs := getMQLogConsoleSource()
//Nothing set, this is when we mirror all
if logsrcs == "" {
return true
}
//Split the csv environment value so that we get an accurate comparison instead of a contains() check
logSrcArr := strings.Split(logsrcs, ",")
//Iterate through the array to decide on mirroring
for _, arr := range logSrcArr {
switch strings.TrimSpace(arr) {
case "qmgr":
//If value of source is qmgr and it exists in environment variable, mirror qmgr logs
if source == "qmgr" {
return true
}
case "web":
//If value of source is web and it exists in environment variable, and mirror web logs
if source == "web" {
//If older environment variable is set make sure to print appropriate message
if os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG") != "" {
log.Println("Environment variable MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG has now been replaced. Use MQ_LOGGING_CONSOLE_SOURCE instead.")
}
return true
}
}
}
return false
}

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2020 © Copyright IBM Corporation 2020, 2023
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"strings" "strings"
"testing" "testing"
) )
@@ -53,3 +54,86 @@ func TestFormatBasic(t *testing.T) {
}) })
} }
} }
// This test covers for functions isLogConsoleSourceValid() & checkLogSourceForMirroring()
var mqLogSourcesTests = []struct {
testNum int
logsrc string
exptValid bool
exptQmgrSrc bool
exptWebSrc bool
}{
{1, "qmgr,web", true, true, true},
{2, "qmgr", true, true, false},
{3, "web,qmgr", true, true, true},
{4, "web", true, false, true},
{5, " ", true, true, true},
{6, "QMGR,WEB", true, true, true},
{7, "qmgr, ", true, true, false},
{8, "qmgr , web", true, true, true},
{9, "qmgr,dummy", false, true, false},
{10, "fake,dummy", false, false, false},
{11, "qmgr,fake,dummy", false, true, false},
{12, "fake,dummy,web", false, false, true},
{13, "true", false, false, false},
{14, "false", false, false, false},
{15, "", true, true, true},
}
func TestLoggingConsoleSourceInputs(t *testing.T) {
for _, mqlogsrctest := range mqLogSourcesTests {
err := os.Setenv("MQ_LOGGING_CONSOLE_SOURCE", mqlogsrctest.logsrc)
if err != nil {
t.Error(err)
}
isValid := isLogConsoleSourceValid()
if isValid != mqlogsrctest.exptValid {
t.Errorf("Expected return value from isLogConsoleSourceValid() is %v for MQ_LOGGING_CONSOLE_SOURCE='%v', got %v\n", mqlogsrctest.exptValid, mqlogsrctest.logsrc, isValid)
}
isLogSrcQmgr := checkLogSourceForMirroring("qmgr")
if isLogSrcQmgr != mqlogsrctest.exptQmgrSrc {
t.Errorf("Expected return value from checkLogSourceForMirroring() is %v for MQ_LOGGING_CONSOLE_SOURCE='%v', got %v\n", mqlogsrctest.exptQmgrSrc, mqlogsrctest.logsrc, isLogSrcQmgr)
}
isLogSrcWeb := checkLogSourceForMirroring("web")
if isLogSrcWeb != mqlogsrctest.exptWebSrc {
t.Errorf("Expected return value from checkLogSourceForMirroring() is %v for MQ_LOGGING_CONSOLE_SOURCE='%v', got %v\n", mqlogsrctest.exptWebSrc, mqlogsrctest.logsrc, isLogSrcWeb)
}
}
}
// This test covers for function isExcludedMsgIdPresent()
var mqExcludeIDTests = []struct {
testNum int
exculdeIDsArr []string
expectedRetVal bool
logEntry string
}{
{
1,
[]string{"AMQ5051I", "AMQ5037I", "AMQ5975I"},
true,
"{\"ibm_messageId\":\"AMQ5051I\",\"ibm_arithInsert1\":0,\"ibm_arithInsert2\":1,\"message\":\"AMQ5051I: The queue manager task 'AUTOCONFIG' has started.\"}",
},
{
2,
[]string{"AMQ5975I", "AMQ5037I"},
false,
"{\"ibm_messageId\":\"AMQ5051I\",\"ibm_arithInsert1\":0,\"ibm_arithInsert2\":1,\"message\":\"AMQ5051I: The queue manager task 'AUTOCONFIG' has started.\"}",
},
{
3,
[]string{""},
false,
"{\"ibm_messageId\":\"AMQ5051I\",\"ibm_arithInsert1\":0,\"ibm_arithInsert2\":1,\"message\":\"AMQ5051I: The queue manager task 'AUTOCONFIG' has started.\"}",
},
}
func TestIsExcludedMsgIDPresent(t *testing.T) {
for _, excludeIDTest := range mqExcludeIDTests {
retVal := isExcludedMsgIdPresent(excludeIDTest.logEntry, excludeIDTest.exculdeIDsArr)
if retVal != excludeIDTest.expectedRetVal {
t.Errorf("%v. Expected return value from isExcludedMsgIdPresent() is %v for MQ_LOGGING_CONSOLE_EXCLUDE_ID='%v', got %v\n",
excludeIDTest.testNum, excludeIDTest.expectedRetVal, excludeIDTest.exculdeIDsArr, retVal)
}
}
}

View File

@@ -160,6 +160,11 @@ func doMain() error {
return err return err
} }
//Validate MQ_LOG_CONSOLE_SOURCE variable
if !isLogConsoleSourceValid() {
log.Println("One or more invalid value is provided for MQ_LOGGING_CONSOLE_SOURCE. Allowed values are 'qmgr' & 'web' in csv format")
}
err = postInit(name, keyLabel, defaultP12Truststore) err = postInit(name, keyLabel, defaultP12Truststore)
if err != nil { if err != nil {
logTermination(err) logTermination(err)
@@ -210,17 +215,24 @@ func doMain() error {
log.Debug("Cancel log mirroring") log.Debug("Cancel log mirroring")
cancelMirror() cancelMirror()
}() }()
// TODO: Use the error channel
//For mirroring mq system logs and qm logs, if environment variable is set
if checkLogSourceForMirroring("qmgr") {
//Mirror MQ system logs
_, err = mirrorSystemErrorLogs(ctx, &wg, mf) _, err = mirrorSystemErrorLogs(ctx, &wg, mf)
if err != nil { if err != nil {
logTermination(err) logTermination(err)
return err return err
} }
//Mirror queue manager logs
_, err = mirrorQueueManagerErrorLogs(ctx, &wg, name, newQM, mf) _, err = mirrorQueueManagerErrorLogs(ctx, &wg, name, newQM, mf)
if err != nil { if err != nil {
logTermination(err) logTermination(err)
return err return err
} }
}
if *devFlag { if *devFlag {
_, err = mirrorHTPasswdLogs(ctx, &wg, name, newQM, mf) _, err = mirrorHTPasswdLogs(ctx, &wg, name, newQM, mf)
if err != nil { if err != nil {
@@ -228,9 +240,9 @@ func doMain() error {
return err return err
} }
} }
// Recommended to use this option in conjunction with setting WLP_LOGGING_MESSAGE_FORMAT=JSON
mirrorWebLog := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG") //For mirroring web server logs if source variable is set
if mirrorWebLog == "true" || mirrorWebLog == "1" { if checkLogSourceForMirroring("web") {
_, err = mirrorWebServerLogs(ctx, &wg, name, newQM, mf) _, err = mirrorWebServerLogs(ctx, &wg, name, newQM, mf)
if err != nil { if err != nil {
logTermination(err) logTermination(err)

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2020 © Copyright IBM Corporation 2017, 2023
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -102,5 +102,10 @@ func LogContainerDetails(log *logger.Logger) error {
} }
} }
} }
if os.Getenv("MQ_LOGGING_CONSOLE_FORMAT") == "" && os.Getenv("LOG_FORMAT") != "" {
log.Println("Environment variable LOG_FORMAT is deprecated. Use MQ_LOGGING_CONSOLE_FORMAT instead.")
}
return nil return nil
} }

View File

@@ -106,6 +106,19 @@ func goldenPath(t *testing.T, metrics bool) {
defer cleanContainer(t, cli, id) defer cleanContainer(t, cli, id)
waitForReady(t, cli, id) waitForReady(t, cli, id)
//By default AMQ5041I,AMQ5052I,AMQ5051I,AMQ5037I,AMQ5975I are excluded
jsonLogs := inspectLogs(t, cli, id)
isMessageFound := scanForExcludedEntries(jsonLogs)
if isMessageFound == true {
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
}
if strings.Contains(jsonLogs, "CWWKF0011I") {
t.Errorf("Web Server is off....CWWKF0011I message id is not expected")
}
t.Run("Validate Default LogFilePages", func(t *testing.T) { t.Run("Validate Default LogFilePages", func(t *testing.T) {
testLogFilePages(t, cli, id, "qm1", "4096") testLogFilePages(t, cli, id, "qm1", "4096")
}) })
@@ -1158,28 +1171,6 @@ func jsonLogFormat(t *testing.T, metric bool) {
} }
} }
func TestBadLogFormat(t *testing.T) {
t.Parallel()
cli, err := client.NewClientWithOpts(client.FromEnv)
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, 20*time.Second)
if rc != 1 {
t.Errorf("Expected rc=1, got rc=%v", rc)
}
expectTerminationMessage(t, cli, id)
}
// 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) {
@@ -1503,3 +1494,290 @@ func TestCustomLogFilePages(t *testing.T) {
testLogFilePages(t, cli, id, "qmlfp", "8192") testLogFilePages(t, cli, id, "qmlfp", "8192")
} }
// TestLoggingConsoleSource tests default behavior which is
// MQ_LOGGING_CONSOLE_SOURCE set to qmgr,web
func TestLoggingConsoleSource(t *testing.T) {
t.Parallel()
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
t.Fatal(err)
}
containerConfig := container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=qm1",
"MQ_ENABLE_EMBEDDED_WEB_SERVER=true",
},
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
waitForReady(t, cli, id)
jsonLogs := inspectLogs(t, cli, id)
isMessageFound := scanForExcludedEntries(jsonLogs)
if isMessageFound == true {
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
}
if !strings.Contains(jsonLogs, "AMQ6206I") && !strings.Contains(jsonLogs, "CWWKF0011I") {
t.Errorf("Expected messageIDs AMQ6206I and CWWKF0011I are not present")
}
// Stop the container cleanly
stopContainer(t, cli, id)
}
// TestOldBehaviorWebConsole sets LOG_FORMAT to json and verify logs are indeed in json format
func TestOldBehaviorWebConsole(t *testing.T) {
t.Parallel()
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
t.Fatal(err)
}
containerConfig := container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=qm1",
"LOG_FORMAT=json",
},
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
waitForReady(t, cli, id)
jsonLogs := inspectLogs(t, cli, id)
isMessageFound := scanForExcludedEntries(jsonLogs)
if isMessageFound == true {
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
}
if !strings.Contains(jsonLogs, "Environment variable LOG_FORMAT is deprecated. Use MQ_LOGGING_CONSOLE_FORMAT instead.") {
t.Errorf("Expected Message stating LOG_FORMAT is deprecated is not in the log")
}
isValidJSON := checkLogForValidJSON(jsonLogs)
if !isValidJSON {
t.Fatalf("Expected all log lines to be valid JSON. But got error %v ", err)
}
// Stop the container cleanly
stopContainer(t, cli, id)
}
// TestLoggingConsoleWithContRestart restarts the container and checks
// that setting of env variable persists
func TestLoggingConsoleWithContRestart(t *testing.T) {
t.Parallel()
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
t.Fatal(err)
}
containerConfig := container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=qm1",
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
"MQ_LOGGING_CONSOLE_SOURCE=qmgr",
},
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
waitForReady(t, cli, id)
jsonLogs := inspectLogs(t, cli, id)
if !strings.Contains(jsonLogs, "AMQ6206I") {
t.Errorf("Expected messageID AMQ6206I is not present in the message")
}
isMessageFound := scanForExcludedEntries(jsonLogs)
if isMessageFound == true {
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
}
stopContainer(t, cli, id)
startContainer(t, cli, id)
waitForReady(t, cli, id)
jsonLogs = inspectLogs(t, cli, id)
if !strings.Contains(jsonLogs, "Stopped queue manager") || strings.Contains(jsonLogs, "CWWKF0011I") {
t.Errorf("CWWKF0011I which is not expected is present!!!!!")
}
isMessageFoundAfterRestart := scanForExcludedEntries(jsonLogs)
if isMessageFoundAfterRestart == true {
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
}
// Stop the container cleanly
stopContainer(t, cli, id)
}
// TestLoggingWithQmgrAndExcludeId tests MQ_LOGGING_CONSOLE_SOURCE set to qmgr
// and exclude ID set to amq7230I.
func TestLoggingWithQmgrAndExcludeId(t *testing.T) {
qmgrName := "qm1"
t.Parallel()
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
t.Fatal(err)
}
containerConfig := container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=qm1",
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
"MQ_LOGGING_CONSOLE_SOURCE=qmgr",
"MQ_LOGGING_CONSOLE_FORMAT=json",
"MQ_LOGGING_CONSOLE_EXCLUDE_ID=amq7230I",
},
}
dir := "/var/mqm/qmgrs/" + qmgrName + "/errors"
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
waitForReady(t, cli, id)
jsonLogs := inspectLogs(t, cli, id)
isValidJSON := checkLogForValidJSON(jsonLogs)
if !isValidJSON {
t.Fatalf("Expected all log lines to be valid JSON. But got error %v ", err)
}
if strings.Contains(jsonLogs, "AMQ7230I") {
t.Errorf("Expected to exclude messageId by default; but messageId \"AMQ7230I\" is present")
}
stopContainer(t, cli, id)
//checking that message is only excluded from the console log, but not from the MQ error log
b := copyFromContainer(t, cli, id, filepath.Join(dir, "AMQERR01.json"))
foundInLog := 0
r := bytes.NewReader(b)
scannernew := bufio.NewScanner(r)
for scannernew.Scan() {
textData := scannernew.Text()
if strings.Contains(textData, "AMQ7230I") {
foundInLog = 1
}
}
if foundInLog == 0 {
t.Errorf("mesageID AMQ7230I is not present in MQ LOG!!!!")
}
}
// TestLoggingConsoleSetToWeb tests MQ_LOGGING_CONSOLE_SOURCE set to web
func TestLoggingConsoleSetToWeb(t *testing.T) {
t.Parallel()
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
t.Fatal(err)
}
containerConfig := container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=qm1",
"MQ_ENABLE_EMBEDDED_WEB_SERVER=true",
"MQ_LOGGING_CONSOLE_SOURCE=web",
"MQ_LOGGING_CONSOLE_EXCLUDE_ID=CWWKG0028A,CWWKS4105I",
"MQ_LOGGING_CONSOLE_FORMAT=json",
},
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
waitForReady(t, cli, id)
// At the most we will wait for 60 seconds for the log to appear. If it doesn't
// appear within the time, we'll fail.
currentTime := time.Now()
waitTime := currentTime.Add(time.Second * 60)
var jsonLogs string
msgFound := false
for time.Now().Unix() < waitTime.Unix() {
jsonLogs = inspectLogs(t, cli, id)
if !strings.Contains(jsonLogs, "CWWKF0011I") {
time.Sleep(1 * time.Second)
continue
} else {
msgFound = true
break
}
}
if !msgFound {
t.Errorf("Expected messageID CWWKF0011I is not present in the message")
}
if strings.Contains(jsonLogs, "AMQ5041I") || strings.Contains(jsonLogs, "AMQ5052I") ||
strings.Contains(jsonLogs, "AMQ5051I") || strings.Contains(jsonLogs, "AMQ5037I") ||
strings.Contains(jsonLogs, "AMQ5975I") || strings.Contains(jsonLogs, "CWWKG0028A") ||
strings.Contains(jsonLogs, "CWWKS4105I") {
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
}
// Stop the container cleanly
stopContainer(t, cli, id)
}
// TestLoggingConsoleSetToQmgr test sets LOG_FORMAT to BASIC and MQ_LOGGING_CONSOLE_FORMAT to
// json and check that log is in json format
func TestLoggingConsoleSetToQmgr(t *testing.T) {
t.Parallel()
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
t.Fatal(err)
}
containerConfig := container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=qm1",
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
"MQ_LOGGING_CONSOLE_SOURCE=qmgr",
"LOG_FORMAT=BASIC",
"MQ_LOGGING_CONSOLE_FORMAT=json",
},
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
waitForReady(t, cli, id)
jsonLogs := inspectLogs(t, cli, id)
isMessageFound := scanForExcludedEntries(jsonLogs)
if isMessageFound == true {
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
}
if !strings.Contains(jsonLogs, "AMQ6206I") {
t.Errorf("Expected messageID AMQ6206I is not present in the message")
}
isValidJSON := checkLogForValidJSON(jsonLogs)
if !isValidJSON {
t.Fatalf("Expected all log lines to be valid JSON. But got error %v ", err)
}
// Stop the container cleanly
stopContainer(t, cli, id)
}

View File

@@ -745,6 +745,7 @@ func inspectLogs(t *testing.T, cli *client.Client, ID string) string {
t.Fatal(err) t.Fatal(err)
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
// Each output line has a header, which needs to be removed // Each output line has a header, which needs to be removed
_, err = stdcopy.StdCopy(buf, buf, reader) _, err = stdcopy.StdCopy(buf, buf, reader)
if err != nil { if err != nil {
@@ -890,6 +891,30 @@ func getMQVersion(t *testing.T, cli *client.Client) (string, error) {
return version, nil return version, nil
} }
// scanForExcludedEntries scans for default excluded messages
func scanForExcludedEntries(msg string) bool {
if strings.Contains(msg, "AMQ5041I") || strings.Contains(msg, "AMQ5052I") ||
strings.Contains(msg, "AMQ5051I") || strings.Contains(msg, "AMQ5037I") ||
strings.Contains(msg, "AMQ5975I") {
return true
}
return false
}
// checkLogForValidJSON checks if the message is in Json format
func checkLogForValidJSON(jsonLogs string) bool {
scanner := bufio.NewScanner(strings.NewReader(jsonLogs))
for scanner.Scan() {
var obj map[string]interface{}
s := scanner.Text()
err := json.Unmarshal([]byte(s), &obj)
if err != nil {
return false
}
}
return true
}
// runContainerWithAllConfig creates and starts a container, using the supplied ContainerConfig, HostConfig, // runContainerWithAllConfig creates and starts a container, using the supplied ContainerConfig, HostConfig,
// NetworkingConfig, and container name (or the value of t.Name if containerName=""). // NetworkingConfig, and container name (or the value of t.Name if containerName="").
func runContainerWithAllConfigError(t *testing.T, cli *client.Client, containerConfig *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (string, error) { func runContainerWithAllConfigError(t *testing.T, cli *client.Client, containerConfig *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (string, error) {