diff options
-rw-r--r-- | jstests/core/apitest_db_profile_level.js | 46 | ||||
-rw-r--r-- | jstests/libs/log.js | 37 | ||||
-rw-r--r-- | src/mongo/db/commands/profile_common.cpp | 27 |
3 files changed, 99 insertions, 11 deletions
diff --git a/jstests/core/apitest_db_profile_level.js b/jstests/core/apitest_db_profile_level.js index 2172b4ed1cb..20f6ff55df9 100644 --- a/jstests/core/apitest_db_profile_level.js +++ b/jstests/core/apitest_db_profile_level.js @@ -1,11 +1,13 @@ /** * Tests for setting of profile levels - * @tags: [does_not_support_stepdowns, requires_profiling] + * @tags: [does_not_support_stepdowns, requires_profiling, requires_fcv_40] */ (function() { 'use strict'; + load("jstests/libs/log.js"); // For findMatchingLogLine, findMatchingLogLines. + /* * be sure the public collection API is complete */ @@ -16,25 +18,51 @@ // other tests that modify profiler level, when run in parallel. var profileLevelDB = db.getSiblingDB("apitest_db_profile_level"); + // Checks for the log that was expected to be created when profile level changed. + function profilerChangeWasLogged({from, to} = {}) { + const globalLog = assert.commandWorked(profileLevelDB.adminCommand({getLog: 'global'})); + + const fieldMatcher = {msg: "Profiler settings changed"}; + if (from && to) { + const lines = [...findMatchingLogLines(globalLog.log, fieldMatcher)]; + return lines.find(line => line.match(new RegExp(/from:\{ /.source + from.source)) && + line.match(new RegExp(/to:\{ /.source + to.source))); + } else { + return findMatchingLogLine(globalLog.log, fieldMatcher); + } + } + + profileLevelDB.getProfilingLevel(); + assert(!profilerChangeWasLogged({from: /level: 0/, to: /level: -1/}), + "Didn't expect anything to be logged"); + + assert.throws(() => { + profileLevelDB.setProfilingLevel(-1); + }); + profileLevelDB.setProfilingLevel(0); assert(profileLevelDB.getProfilingLevel() == 0, "prof level 0"); + assert(profilerChangeWasLogged({from: /level: 0/, to: /level: 0/}), + "Didn't find expected log line"); profileLevelDB.setProfilingLevel(1); assert(profileLevelDB.getProfilingLevel() == 1, "p1"); + assert(profilerChangeWasLogged({from: /level: 0/, to: /level: 1/}), + "Didn't find expected log line"); profileLevelDB.setProfilingLevel(2); assert(profileLevelDB.getProfilingLevel() == 2, "p2"); + assert(profilerChangeWasLogged({from: /level: 1/, to: /level: 2/}), + "Didn't find expected log line"); profileLevelDB.setProfilingLevel(0); assert(profileLevelDB.getProfilingLevel() == 0, "prof level 0"); + assert(profilerChangeWasLogged({from: /level: 2/, to: /level: 0/}), + "Didn't find expected log line"); - var asserted = false; - try { + assert.throws(() => { profileLevelDB.setProfilingLevel(10); - assert(false); - } catch (e) { - asserted = true; - assert(e.dbSetProfilingException); - } - assert(asserted, "should have asserted"); + }); + // Check that didn't log an invalid profile level change. + assert(!profilerChangeWasLogged({from: /level: 0/, to: /level: 10/}), "Didn't expect log line"); })(); diff --git a/jstests/libs/log.js b/jstests/libs/log.js new file mode 100644 index 00000000000..299a9ab8e0c --- /dev/null +++ b/jstests/libs/log.js @@ -0,0 +1,37 @@ +// Yields every logline that contains the specified fields. The regex escape function used here is +// drawn from the following: +// https://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript +// https://github.com/ljharb/regexp.escape +function * findMatchingLogLines(logLines, fields, ignoreFields) { + ignoreFields = ignoreFields || []; + function escapeRegex(input) { + return (typeof input === "string" ? input.replace(/[\^\$\\\.\*\+\?\(\)\[\]\{\}]/g, '\\$&') + : input); + } + function lineMatches(line, fields, ignoreFields) { + const fieldNames = + Object.keys(fields).filter((fieldName) => !ignoreFields.includes(fieldName)); + return fieldNames.every((fieldName) => { + const fieldValue = fields[fieldName]; + let regex = escapeRegex(fieldName) + ":? ?(" + + escapeRegex(checkLog.formatAsLogLine(fieldValue)) + "|" + + escapeRegex(checkLog.formatAsLogLine(fieldValue, true)) + ")"; + const match = line.match(regex); + return match && match[0]; + }); + } + + for (let line of logLines) { + if (lineMatches(line, fields, ignoreFields)) { + yield line; + } + } +} +// Finds and returns a logline containing all the specified fields, or null if no such logline +// was found. +function findMatchingLogLine(logLines, fields, ignoreFields) { + for (let line of findMatchingLogLines(logLines, fields, ignoreFields)) { + return line; + } + return null; +} diff --git a/src/mongo/db/commands/profile_common.cpp b/src/mongo/db/commands/profile_common.cpp index c3ed936d753..d29ddf948e5 100644 --- a/src/mongo/db/commands/profile_common.cpp +++ b/src/mongo/db/commands/profile_common.cpp @@ -27,13 +27,16 @@ * exception statement from all source files in the program, then also delete * it in the license file. */ +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand #include "mongo/platform/basic.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/commands/profile_common.h" #include "mongo/db/commands/profile_gen.h" +#include "mongo/db/jsobj.h" #include "mongo/idl/idl_parser.h" +#include "mongo/util/log.h" namespace mongo { @@ -70,10 +73,12 @@ bool ProfileCmdBase::run(OperationContext* opCtx, // Delegate to _applyProfilingLevel to set the profiling level appropriately whether we are on // mongoD or mongoS. int oldLevel = _applyProfilingLevel(opCtx, dbName, profilingLevel); + auto oldSlowMS = serverGlobalParams.slowMS; + auto oldSampleRate = serverGlobalParams.sampleRate; result.append("was", oldLevel); - result.append("slowms", serverGlobalParams.slowMS); - result.append("sampleRate", serverGlobalParams.sampleRate); + result.append("slowms", oldSlowMS); + result.append("sampleRate", oldSampleRate); if (auto slowms = request.getSlowms()) { serverGlobalParams.slowMS = *slowms; @@ -85,6 +90,24 @@ bool ProfileCmdBase::run(OperationContext* opCtx, serverGlobalParams.sampleRate = *sampleRate; } + // Log the change made to server's profiling settings, unless the request was to get the current + // value. + if (profilingLevel != -1) { + BSONObjBuilder oldState; + BSONObjBuilder newState; + + oldState.append("level"_sd, oldLevel); + oldState.append("slowms"_sd, oldSlowMS); + oldState.append("sampleRate"_sd, oldSampleRate); + + newState.append("level"_sd, profilingLevel); + newState.append("slowms"_sd, serverGlobalParams.slowMS); + newState.append("sampleRate"_sd, serverGlobalParams.sampleRate); + + LOG(0) << "{msg: \"Profiler settings changed\", from:" << oldState.obj() + << ", to:" << newState.obj() << "}"; + } + return true; } } // namespace mongo |