Skip to content

Add MQ_QMGR_PRIMARY_LOGFILES, MQ_QMGR_SECONDARY_LOGFILES env variables for configuring logfiles #595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Change log

## XX (2025-XX)
* New environment variables: MQ_QMGR_PRIMARY_LOGFILES, MQ_QMGR_SECONDARY_LOGFILES. These are meant to be set the number of logfiles when the default ones are not enough.

## 9.4.2.0 (2025-02)

* Updated to MQ version 9.4.2.0
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Note that in order to use the image, it is necessary to accept the terms of the
- **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_LOG_FILE_PAGES** - Set this to control the value for LogFilePages passed to the "crtmqm" command. Cannot be changed after queue manager creation.
- **MQ_QMGR_PRIMARY_LOGFILES** - Set this to control the value for LogPrimaryFiles passed to the "crtmqm" command. Cannot be changed after queue manager creation.
- **MQ_QMGR_SECONDARY_LOGFILES** - Set this to control the value for LogSecondaryFiles passed to the "crtmqm" command. Cannot be changed after queue manager creation.
- **MQ_LOGGING_CONSOLE_SOURCE** - Specifies a comma-separated list of sources for logs which are mirrored to the container's stdout. The valid values are "qmgr", "web" and "mqsc". Defaults to "qmgr,web".
- **MQ_LOGGING_CONSOLE_FORMAT** - Changes 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 "basic" to use a simple human-readable format. Defaults to "basic".
- **MQ_LOGGING_CONSOLE_EXCLUDE_ID** - Excludes log messages with the specified ID. The log messages still appear in the log file on disk, but are excluded from the container's stdout. Defaults to "AMQ5041I,AMQ5052I,AMQ5051I,AMQ5037I,AMQ5975I".
Expand Down
54 changes: 54 additions & 0 deletions cmd/runmqserver/qmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,30 @@ func createQueueManager(name string, devMode bool) (bool, error) {
log.Println("Warning: the value of MQ_QMGR_LOG_FILE_PAGES does not match the value of 'LogFilePages' in the qm.ini. This setting cannot be altered after Queue Manager creation.")
}
}
// Check if MQ_QMGR_PRIMARY_LOGFILES matches the value set in qm.ini
plf := os.Getenv("MQ_QMGR_PRIMARY_LOGFILES")
if plf != "" {
qmIniBytes, err := readQMIni(dataDir)
if err != nil {
log.Printf("Error reading qm.ini : %v", err)
return false, err
}
if !validatePrimaryLogFileSetting(qmIniBytes, plf) {
log.Println("Warning: the value of MQ_QMGR_PRIMARY_LOGFILES does not match the value of 'LogPrimaryFiles' in the qm.ini. This setting cannot be altered after Queue Manager creation.")
}
}
// Check if MQ_QMGR_SECONDARY_LOGFILES matches the value set in qm.ini
slf := os.Getenv("MQ_QMGR_SECONDARY_LOGFILES")
if slf != "" {
qmIniBytes, err := readQMIni(dataDir)
if err != nil {
log.Printf("Error reading qm.ini : %v", err)
return false, err
}
if !validateSecondaryLogFileSetting(qmIniBytes, slf) {
log.Println("Warning: the value of MQ_QMGR_SECONDARY_LOGFILES does not match the value of 'LogSecondaryFiles' in the qm.ini. This setting cannot be altered after Queue Manager creation.")
}
}
return false, nil
}

Expand Down Expand Up @@ -131,6 +155,20 @@ func validateLogFilePageSetting(iniFileBytes []byte, logFilePages string) bool {
return strings.Contains(qminiConfigStr, lfpString)
}

// validatePrimaryLogFileSetting validates if the specified primaryLogFiles number is equal to the existing value in the qm.ini
func validatePrimaryLogFileSetting(iniFileBytes []byte, primaryLogFiles string) bool {
plfString := "LogPrimaryFiles=" + primaryLogFiles
qminiConfigStr := string(iniFileBytes)
return strings.Contains(qminiConfigStr, plfString)
}

// validateSecondaryLogFileSetting validates if the specified secondaryLogFiles number is equal to the existing value in the qm.ini
func validateSecondaryLogFileSetting(iniFileBytes []byte, secondaryLogFiles string) bool {
slfString := "LogSecondaryFiles=" + secondaryLogFiles
qminiConfigStr := string(iniFileBytes)
return strings.Contains(qminiConfigStr, slfString)
}

func updateCommandLevel() error {
level, ok := os.LookupEnv("MQ_CMDLEVEL")
if ok && level != "" {
Expand Down Expand Up @@ -286,6 +324,22 @@ func getCreateQueueManagerArgs(mounts map[string]string, name string, devMode bo
args = append(args, "-lf", os.Getenv("MQ_QMGR_LOG_FILE_PAGES"))
}
}
if os.Getenv("MQ_QMGR_PRIMARY_LOGFILES") != "" {
_, err = strconv.Atoi(os.Getenv("MQ_QMGR_PRIMARY_LOGFILES"))
if err != nil {
log.Printf("Error processing MQ_QMGR_PRIMARY_LOGFILES, the default value for LogPrimaryFiles will be used. Err: %v", err)
} else {
args = append(args, "-lp", os.Getenv("MQ_QMGR_PRIMARY_LOGFILES"))
}
}
if os.Getenv("MQ_QMGR_SECONDARY_LOGFILES") != "" {
_, err = strconv.Atoi(os.Getenv("MQ_QMGR_SECONDARY_LOGFILES"))
if err != nil {
log.Printf("Error processing MQ_QMGR_SECONDARY_LOGFILES, the default value for LogSecondaryFiles will be used. Err: %v", err)
} else {
args = append(args, "-ls", os.Getenv("MQ_QMGR_SECONDARY_LOGFILES"))
}
}
args = append(args, name)
return args
}
Expand Down
130 changes: 130 additions & 0 deletions cmd/runmqserver/qmgr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,136 @@ func Test_validateLogFilePageSetting(t *testing.T) {
}
}

func Test_validatePrimaryLogFileSetting(t *testing.T) {
type args struct {
iniFilePath string
isValid bool
primaryLogFilesValue string
}
tests := []struct {
name string
args args
}{
{
name: "TestPrimaryLogFile1",
args: args{
iniFilePath: "./test-files/testvalidateLogPrimaryFiles_1.ini",
isValid: true,
primaryLogFilesValue: "3",
},
},
{
name: "TestPrimaryLogFile2",
args: args{
iniFilePath: "./test-files/testvalidateLogPrimaryFiles_2.ini",
isValid: true,
primaryLogFilesValue: "3",
},
},
{
name: "TestPrimaryLogFile3",
args: args{
iniFilePath: "./test-files/testvalidateLogPrimaryFiles_3.ini",
isValid: false,
primaryLogFilesValue: "10",
},
},
{
name: "TestPrimaryLogFile4",
args: args{
iniFilePath: "./test-files/testvalidateLogPrimaryFiles_4.ini",
isValid: false,
primaryLogFilesValue: "3",
},
},
{
name: "TestPrimaryLogFile5",
args: args{
iniFilePath: "./test-files/testvalidateLogPrimaryFiles_5.ini",
isValid: false,
primaryLogFilesValue: "1235",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
iniFileBytes, err := os.ReadFile(tt.args.iniFilePath)
if err != nil {
t.Fatal(err)
}
validate := validatePrimaryLogFileSetting(iniFileBytes, tt.args.primaryLogFilesValue)
if validate != tt.args.isValid {
t.Fatalf("Expected ini file validation output to be %v got %v", tt.args.isValid, validate)
}
})
}
}

func Test_validateSecondaryLogFileSetting(t *testing.T) {
type args struct {
iniFilePath string
isValid bool
secondaryLogFilesValue string
}
tests := []struct {
name string
args args
}{
{
name: "TestSecondaryLogFile1",
args: args{
iniFilePath: "./test-files/testvalidateLogSecondaryFiles_1.ini",
isValid: true,
secondaryLogFilesValue: "2",
},
},
{
name: "TestSecondaryLogFile2",
args: args{
iniFilePath: "./test-files/testvalidateLogSecondaryFiles_2.ini",
isValid: true,
secondaryLogFilesValue: "2",
},
},
{
name: "TestSecondaryLogFile3",
args: args{
iniFilePath: "./test-files/testvalidateLogSecondaryFiles_3.ini",
isValid: false,
secondaryLogFilesValue: "10",
},
},
{
name: "TestSecondaryLogFile4",
args: args{
iniFilePath: "./test-files/testvalidateLogSecondaryFiles_4.ini",
isValid: false,
secondaryLogFilesValue: "2",
},
},
{
name: "TestSecondaryLogFile5",
args: args{
iniFilePath: "./test-files/testvalidateLogSecondaryFiles_5.ini",
isValid: false,
secondaryLogFilesValue: "1235",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
iniFileBytes, err := os.ReadFile(tt.args.iniFilePath)
if err != nil {
t.Fatal(err)
}
validate := validateSecondaryLogFileSetting(iniFileBytes, tt.args.secondaryLogFilesValue)
if validate != tt.args.isValid {
t.Fatalf("Expected ini file validation output to be %v got %v", tt.args.isValid, validate)
}
})
}
}

// Unit test for special character in queue manager names
func Test_SpecialCharInQMNameReplacements(t *testing.T) {
type qmNames struct {
Expand Down
9 changes: 9 additions & 0 deletions cmd/runmqserver/test-files/testvalidateLogPrimaryFiles_1.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ExitPath:
ExitsDefaultPath=/mnt/mqm/data/exits
ExitsDefaultPath64=/mnt/mqm/data/exits64
Log:
LogPrimaryFiles=3
LogSecondaryFiles=2
LogFilePages=1235
LogBufferPages=0
LogWriteIntegrity=TripleWrite
9 changes: 9 additions & 0 deletions cmd/runmqserver/test-files/testvalidateLogPrimaryFiles_2.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ExitPath:
ExitsDefaultPath=/mnt/mqm/data/exits
ExitsDefaultPath64=/mnt/mqm/data/exits64
Log:
LogPrimaryFiles=3
LogSecondaryFiles=2
LogFilePages=2224
LogBufferPages=0
LogWriteIntegrity=TripleWrite
9 changes: 9 additions & 0 deletions cmd/runmqserver/test-files/testvalidateLogPrimaryFiles_3.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ExitPath:
ExitsDefaultPath=/mnt/mqm/data/exits
ExitsDefaultPath64=/mnt/mqm/data/exits64
Log:
LogPrimaryFiles=3
LogSecondaryFiles=2
LogFilePages=6002
LogBufferPages=0
LogWriteIntegrity=TripleWrite
7 changes: 7 additions & 0 deletions cmd/runmqserver/test-files/testvalidateLogPrimaryFiles_4.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ExitPath:
ExitsDefaultPath=/mnt/mqm/data/exits
ExitsDefaultPath64=/mnt/mqm/data/exits64
Log:
LogSecondaryFiles=2
LogBufferPages=0
LogWriteIntegrity=TripleWrite
7 changes: 7 additions & 0 deletions cmd/runmqserver/test-files/testvalidateLogPrimaryFiles_5.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ExitPath:
ExitsDefaultPath=/mnt/mqm/data/exits
ExitsDefaultPath64=/mnt/mqm/data/exits64
Log:
LogSecondaryFiles=2
LogBufferPages=3
LogWriteIntegrity=TripleWrite
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ExitPath:
ExitsDefaultPath=/mnt/mqm/data/exits
ExitsDefaultPath64=/mnt/mqm/data/exits64
Log:
LogPrimaryFiles=3
LogSecondaryFiles=2
LogFilePages=1235
LogBufferPages=0
LogWriteIntegrity=TripleWrite
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ExitPath:
ExitsDefaultPath=/mnt/mqm/data/exits
ExitsDefaultPath64=/mnt/mqm/data/exits64
Log:
LogPrimaryFiles=3
LogSecondaryFiles=2
LogFilePages=2224
LogBufferPages=0
LogWriteIntegrity=TripleWrite
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ExitPath:
ExitsDefaultPath=/mnt/mqm/data/exits
ExitsDefaultPath64=/mnt/mqm/data/exits64
Log:
LogPrimaryFiles=3
LogSecondaryFiles=2
LogFilePages=6002
LogBufferPages=0
LogWriteIntegrity=TripleWrite
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ExitPath:
ExitsDefaultPath=/mnt/mqm/data/exits
ExitsDefaultPath64=/mnt/mqm/data/exits64
Log:
LogBufferPages=0
LogWriteIntegrity=TripleWrite
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ExitPath:
ExitsDefaultPath=/mnt/mqm/data/exits
ExitsDefaultPath64=/mnt/mqm/data/exits64
Log:
LogBufferPages=12345
LogWriteIntegrity=TripleWrite
40 changes: 40 additions & 0 deletions test/container/docker_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ func goldenPath(t *testing.T, metrics bool) {
t.Run("Validate Default LogFilePages", func(t *testing.T) {
testLogFilePages(t, cli, id, "qm1", "4096")
})

t.Run("Validate Default LogPrimaryFiles", func(t *testing.T) {
testPrimaryLogFiles(t, cli, id, "qm1", "3")
})

t.Run("Validate Default LogSecondaryFiles", func(t *testing.T) {
testSecondaryLogFiles(t, cli, id, "qm1", "2")
})
// Stop the container cleanly
stopContainer(t, cli, id)
}
Expand Down Expand Up @@ -1407,6 +1415,38 @@ func TestCustomLogFilePages(t *testing.T) {
testLogFilePages(t, cli, id, "qmlfp", "8192")
}

// TestCustomPrimaryLogFiles starts a qmgr with a custom number of LogPrimaryFiles set.
// Check that the number of LogPrimaryFiles matches.
func TestCustomPrimaryLogFiles(t *testing.T) {
t.Parallel()
cli := ce.NewContainerClient(ce.WithTestCommandLogger(t))
containerConfig := ce.ContainerConfig{
Env: []string{"LICENSE=accept", "MQ_QMGR_PRIMARY_LOGFILES=16", "MQ_QMGR_NAME=qmlfp"},
}

id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id, false)
waitForReady(t, cli, id)

testPrimaryLogFiles(t, cli, id, "qmlfp", "16")
}

// TestCustomSecondaryLogFiles starts a qmgr with a custom number of LogSecondaryFiles set.
// Check that the number of LogSecondaryFiles matches.
func TestCustomSecondaryLogFiles(t *testing.T) {
t.Parallel()
cli := ce.NewContainerClient(ce.WithTestCommandLogger(t))
containerConfig := ce.ContainerConfig{
Env: []string{"LICENSE=accept", "MQ_QMGR_SECONDARY_LOGFILES=8", "MQ_QMGR_NAME=qmlfp"},
}

id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id, false)
waitForReady(t, cli, id)

testSecondaryLogFiles(t, cli, id, "qmlfp", "8")
}

// TestLoggingConsoleSource tests default behavior which is
// MQ_LOGGING_CONSOLE_SOURCE set to qmgr,web
func TestLoggingConsoleSource(t *testing.T) {
Expand Down
Loading