diff --git a/CHANGELOG b/CHANGELOG index e6f5d4a71a..222ad05fac 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,23 @@ v1.4 ---- +* renamed command-line option `--log.filter` to `--log.source-filter` to avoid + misunderstandings + +* introduced new command-line option `--log.content-filter` to optionally restrict + logging to just specific log messages (containing the filter string, case-sensitive). + + For example, to filter on just log entries which contain `ArangoDB`, use: + + --log.content-filter "ArangoDB" + +* added optional command-line option `--log.requests-file` to log incoming HTTP + requests to a file. + + When used, all HTTP requests will be logged to the specified file, containing the + client IP address, HTTP method, requests URL, HTTP response code, and size of the + response body. + * added a signal handler for SIGUSR1 signal: when ArangoDB receives this signal, it will respond all further incoming requests diff --git a/Documentation/UserManual/CommandLine.md b/Documentation/UserManual/CommandLine.md index f32a99dcfd..e6fa9a94f8 100644 --- a/Documentation/UserManual/CommandLine.md +++ b/Documentation/UserManual/CommandLine.md @@ -230,6 +230,10 @@ General Logging Options {#CommandLineLoggingGeneral} @anchor CommandLineLoggingLogFile @copydetails triagens::rest::ApplicationServer::_logFile +@CLEARPAGE +@anchor CommandLineLoggingLogRequestsFile +@copydetails triagens::rest::ApplicationServer::_logRequestsFile + @CLEARPAGE @anchor CommandLineLoggingLogSeverity @copydetails triagens::rest::ApplicationServer::_logSeverity @@ -258,8 +262,12 @@ Human Readable Logging {#CommandLineLoggingHuman} @copydetails triagens::rest::ApplicationServer::_logThreadId @CLEARPAGE -@anchor CommandLineLoggingLogFilter -@copydetails triagens::rest::ApplicationServer::_logFilter +@anchor CommandLineLoggingLogSourceFilter +@copydetails triagens::rest::ApplicationServer::_logSourceFilter + +@CLEARPAGE +@anchor CommandLineLoggingLogContentFilter +@copydetails triagens::rest::ApplicationServer::_logContentFilter @CLEARPAGE Machine Readable Logging {#CommandLineLoggingMachine} diff --git a/Documentation/UserManual/CommandLineTOC.md b/Documentation/UserManual/CommandLineTOC.md index f65a189fe1..184c2de0b9 100644 --- a/Documentation/UserManual/CommandLineTOC.md +++ b/Documentation/UserManual/CommandLineTOC.md @@ -41,6 +41,7 @@ TOC {#CommandLineTOC} - @ref CommandLineLogging - @ref CommandLineLoggingGeneral - @ref CommandLineLoggingLogFile "log.file" + - @ref CommandLineLoggingLogRequestsFile "log.requests-file" - @ref CommandLineLoggingLogSeverity "log.severity" - @ref CommandLineLoggingLogSyslog "log.syslog" - @ref CommandLineLoggingHuman @@ -48,7 +49,8 @@ TOC {#CommandLineTOC} - @ref CommandLineLoggingLogLineNumber "log.line-number" - @ref CommandLineLoggingLogPrefix "log.prefix" - @ref CommandLineLoggingLogThread "log.thread" - - @ref CommandLineLoggingLogFilter "log.filter" + - @ref CommandLineLoggingLogSourceFilter "log.source-filter" + - @ref CommandLineLoggingLogContentFilter "log.content-filter" - @ref CommandLineLoggingMachine - @ref CommandLineLoggingLogApplication "log.application" - @ref CommandLineLoggingLogFacility "log.facility" diff --git a/arangosh/ArangoShell/ArangoClient.cpp b/arangosh/ArangoShell/ArangoClient.cpp index b5f5eda85f..4c4103785b 100644 --- a/arangosh/ArangoShell/ArangoClient.cpp +++ b/arangosh/ArangoShell/ArangoClient.cpp @@ -292,7 +292,7 @@ void ArangoClient::parse (ProgramOptions& options, // setup the logging TRI_SetLogLevelLogging(_logLevel.c_str()); - TRI_CreateLogAppenderFile("-"); + TRI_CreateLogAppenderFile("-", 0, TRI_LOG_SEVERITY_UNKNOWN, false); TRI_SetLineNumberLogging(false); TRI_SetThreadIdentifierLogging(false); diff --git a/js/client/modules/org/arangodb/api/graph.js b/js/client/modules/org/arangodb/api/graph.js index e1830f042d..35318b5372 100644 --- a/js/client/modules/org/arangodb/api/graph.js +++ b/js/client/modules/org/arangodb/api/graph.js @@ -28,7 +28,12 @@ /// @author Copyright 2011-2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// -exports.GraphAPI = { +var GraphAPI, + arangodb = require("org/arangodb"), + arangosh = require("org/arangodb/arangosh"); + + +GraphAPI = { send: function (method, graphKey, path, data) { var results = arangodb.arango[method]("/_api/graph/" + encodeURIComponent(graphKey) + @@ -171,3 +176,5 @@ exports.GraphAPI = { return new ArangoQueryCursor(database, results); } }; + +exports.GraphAPI = GraphAPI; diff --git a/lib/ApplicationServer/ApplicationServer.cpp b/lib/ApplicationServer/ApplicationServer.cpp index 6ee18ab313..64ad723b0f 100644 --- a/lib/ApplicationServer/ApplicationServer.cpp +++ b/lib/ApplicationServer/ApplicationServer.cpp @@ -145,10 +145,13 @@ ApplicationServer::ApplicationServer (std::string const& name, std::string const _logFormat(), _logSeverity("human"), _logFile("+"), + _logRequestsFile(""), _logPrefix(), _logSyslog(), _logThreadId(false), _logLineNumber(false), + _logSourceFilter(), + _logContentFilter(), _randomGenerator(5) { } @@ -183,10 +186,13 @@ ApplicationServer::ApplicationServer (std::string const& name, std::string const _logFormat(), _logSeverity("human"), _logFile("+"), + _logRequestsFile(""), _logPrefix(), _logSyslog(), _logThreadId(false), _logLineNumber(false), + _logSourceFilter(), + _logContentFilter(), _randomGenerator(3) { storeRealPrivileges(); } @@ -279,6 +285,11 @@ void ApplicationServer::setupLogging (bool threaded, bool daemon) { if (_options.has("log.line-number")) { _logLineNumber = true; } + + if (! _logRequestsFile.empty()) { + // add this so the user does not need to think about it + _logSeverity += ",usage"; + } TRI_SetLineNumberLogging(_logLineNumber); @@ -287,30 +298,64 @@ void ApplicationServer::setupLogging (bool threaded, bool daemon) { TRI_SetPrefixLogging(_logPrefix); TRI_SetThreadIdentifierLogging(_logThreadId); - for (vector::iterator i = _logFilter.begin(); i != _logFilter.end(); ++i) { + for (vector::iterator i = _logSourceFilter.begin(); i != _logSourceFilter.end(); ++i) { TRI_SetFileToLog(i->c_str()); } -#ifdef TRI_ENABLE_SYSLOG - if (_logSyslog != "") { - TRI_CreateLogAppenderSyslog(_logPrefix.c_str(), _logSyslog.c_str()); - } -#endif + char const* contentFilter = 0; - if (_logFile.length() > 0) { + if (_options.has("log.content-filter")) { + contentFilter = _logContentFilter.c_str(); + } + + // requests log (must come before the regular logs) + if (! _logRequestsFile.empty()) { + string filename = _logRequestsFile; + + if (daemon && filename != "+" && filename != "-") { + filename = filename + ".daemon"; + } + + // this appender consumes all usage log messages, so they are not propagated to any others + struct TRI_log_appender_s* appender = TRI_CreateLogAppenderFile(filename.c_str(), + 0, + TRI_LOG_SEVERITY_USAGE, + true); + + // the user specified a requests log file to use but it could not be created. bail out + if (appender == 0) { + LOGGER_FATAL_AND_EXIT("failed to create requests logfile '" << filename << "'. Please check the path and permissions."); + } + } + + // regular log file + if (! _logFile.empty()) { string filename = _logFile; if (daemon && filename != "+" && filename != "-") { filename = filename + ".daemon"; } - struct TRI_log_appender_s* appender = TRI_CreateLogAppenderFile(filename.c_str()); + struct TRI_log_appender_s* appender = TRI_CreateLogAppenderFile(filename.c_str(), + contentFilter, + TRI_LOG_SEVERITY_UNKNOWN, + false); // the user specified a log file to use but it could not be created. bail out if (appender == 0) { LOGGER_FATAL_AND_EXIT("failed to create logfile '" << filename << "'. Please check the path and permissions."); } } + +#ifdef TRI_ENABLE_SYSLOG + if (_logSyslog != "") { + TRI_CreateLogAppenderSyslog(_logPrefix.c_str(), + _logSyslog.c_str(), + contentFilter, + TRI_LOG_SEVERITY_UNKNOWN, + false); + } +#endif } //////////////////////////////////////////////////////////////////////////////// @@ -791,13 +836,15 @@ void ApplicationServer::setupOptions (map& op options[OPTIONS_LOGGER] ("log.file", &_logFile, "log to file") + ("log.requests-file", &_logRequestsFile, "log requests to file") ("log.level,l", &_logLevel, "log level for severity 'human'") ; options[OPTIONS_LOGGER + ":help-log"] ("log.application", &_logApplicationName, "application name for syslog") ("log.facility", &_logFacility, "facility name for syslog") - ("log.filter", &_logFilter, "only debug and trace messages originated by specific C source file") + ("log.source-filter", &_logSourceFilter, "only debug and trace messages originated by specific C source file") + ("log.content-filter", &_logContentFilter, "only log message containing the specified string (case-sensitive)") ("log.format", &_logFormat, "log format") ("log.hostname", &_logHostName, "host name") ("log.line-number", "always log file and line number") diff --git a/lib/ApplicationServer/ApplicationServer.h b/lib/ApplicationServer/ApplicationServer.h index d35c41b229..c681d88e8e 100644 --- a/lib/ApplicationServer/ApplicationServer.h +++ b/lib/ApplicationServer/ApplicationServer.h @@ -705,11 +705,11 @@ namespace triagens { string _logSeverity; //////////////////////////////////////////////////////////////////////////////// -/// @brief log level +/// @brief log file /// /// @CMDOPT{\--log.file @CA{filename}} /// -/// This option allows the user to specify the name of a file to which +/// This option allows the user to specify the name of a file to which /// information is logged. By default, if no log file is specified, the standard /// output is used. Note that if the file named by @CA{filename} does not /// exist, it will be created. If the file cannot be created (e.g. due to @@ -722,6 +722,24 @@ namespace triagens { string _logFile; +//////////////////////////////////////////////////////////////////////////////// +/// @brief log file for requests +/// +/// @CMDOPT{\--log.requests-file @CA{filename}} +/// +/// This option allows the user to specify the name of a file to which +/// requests are logged. By default, no log file is used and requests are +/// not logged. Note that if the file named by @CA{filename} does not +/// exist, it will be created. If the file cannot be created (e.g. due to +/// missing file privileges), the server will refuse to start. If the specified +/// file already exists, output is appended to that file. +/// +/// Use @LIT{+} to log to standard error. Use @LIT{-} to log to standard output. +/// Use @LIT{""} to disable request logging altogether. +//////////////////////////////////////////////////////////////////////////////// + + string _logRequestsFile; + //////////////////////////////////////////////////////////////////////////////// /// @brief log prefix /// @@ -787,15 +805,25 @@ namespace triagens { bool _logLineNumber; //////////////////////////////////////////////////////////////////////////////// -/// @brief log filter +/// @brief log source filter /// -/// @CMDOPT{\--log.filter @CA{arg}} +/// @CMDOPT{\--log.source-filter @CA{arg}} /// /// For debug and trace messages, only log those messages originated from the /// C source file @CA{arg}. The argument can be used multiple times. //////////////////////////////////////////////////////////////////////////////// - vector _logFilter; + vector _logSourceFilter; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief log content filter +/// +/// @CMDOPT{\--log.content-filter @CA{arg}} +/// +/// Only log message containing the specified string @CA{arg}. +//////////////////////////////////////////////////////////////////////////////// + + string _logContentFilter; //////////////////////////////////////////////////////////////////////////////// /// @brief random number generator to use diff --git a/lib/BasicsC/logging.c b/lib/BasicsC/logging.c index 95651e5994..de75c9852b 100644 --- a/lib/BasicsC/logging.c +++ b/lib/BasicsC/logging.c @@ -66,6 +66,9 @@ typedef struct TRI_log_appender_s { void (*log) (struct TRI_log_appender_s*, TRI_log_level_e, TRI_log_severity_e, char const* msg, size_t length); void (*reopen) (struct TRI_log_appender_s*); void (*close) (struct TRI_log_appender_s*); + char* _contentFilter; // an optional content filter for log messages + TRI_log_severity_e _severityFilter; // appender will care only about message with a specific severity. set to TRI_LOG_SEVERITY_UNKNOWN to catch all + bool _consume; // whether or not the appender will consume the message (true) or let it through to other appenders (false) } TRI_log_appender_t; @@ -172,6 +175,12 @@ static TRI_thread_t LoggingThread; static sig_atomic_t LoggingThreadActive = 0; +//////////////////////////////////////////////////////////////////////////////// +/// @brief usage logging +//////////////////////////////////////////////////////////////////////////////// + +static sig_atomic_t IsUsage = 0; + //////////////////////////////////////////////////////////////////////////////// /// @brief human readable logging //////////////////////////////////////////////////////////////////////////////// @@ -455,10 +464,17 @@ static int GenerateMessage (char* buffer, // ............................................................................. if (ShowThreadIdentifier) { - n = snprintf(buffer + m, size - m, "[%llu-%llu] ", (unsigned long long) currentProcessId, (unsigned long long) currentThreadId); + n = snprintf(buffer + m, + size - m, + "[%llu-%llu] ", + (unsigned long long) currentProcessId, + (unsigned long long) currentThreadId); } else { - n = snprintf(buffer + m, size - m, "[%llu] ", (unsigned long long) currentProcessId); + n = snprintf(buffer + m, + size - m, + "[%llu] ", + (unsigned long long) currentProcessId); } if (n < 0) { @@ -477,12 +493,12 @@ static int GenerateMessage (char* buffer, ll = "UNKNOWN"; switch (level) { - case TRI_LOG_LEVEL_FATAL: ll = "FATAL"; break; - case TRI_LOG_LEVEL_ERROR: ll = "ERROR"; break; + case TRI_LOG_LEVEL_FATAL: ll = "FATAL"; break; + case TRI_LOG_LEVEL_ERROR: ll = "ERROR"; break; case TRI_LOG_LEVEL_WARNING: ll = "WARNING"; break; - case TRI_LOG_LEVEL_INFO: ll = "INFO"; break; - case TRI_LOG_LEVEL_DEBUG: ll = "DEBUG"; break; - case TRI_LOG_LEVEL_TRACE: ll = "TRACE"; break; + case TRI_LOG_LEVEL_INFO: ll = "INFO"; break; + case TRI_LOG_LEVEL_DEBUG: ll = "DEBUG"; break; + case TRI_LOG_LEVEL_TRACE: ll = "TRACE"; break; } n = snprintf(buffer + m, size - m, "%s ", ll); @@ -597,7 +613,8 @@ static void OutputMessage (TRI_log_level_e level, return; } - + + // copy message to ring buffer of recent log messages if (severity == TRI_LOG_SEVERITY_HUMAN) { // we start copying the message from the given offset to skip any irrelevant // or redundant message parts such as date, info etc. The offset might be 0 though. @@ -645,7 +662,27 @@ static void OutputMessage (TRI_log_level_e level, TRI_log_appender_t* appender; appender = Appenders._buffer[i]; + + assert(appender != NULL); + + // apply severity filter + if (appender->_severityFilter != TRI_LOG_SEVERITY_UNKNOWN && + appender->_severityFilter != severity) { + continue; + } + + // apply content filter on log message + if (appender->_contentFilter != NULL) { + if (! TRI_IsContainedString(message, appender->_contentFilter)) { + continue; + } + } + appender->log(appender, level, severity, message, length); + + if (appender->_consume) { + break; + } } TRI_UnlockSpin(&AppendersLock); @@ -715,7 +752,27 @@ static void MessageQueueWorker (void* data) { TRI_log_appender_t* appender; appender = Appenders._buffer[i]; + + assert(appender != NULL); + + // apply severity filter + if (appender->_severityFilter != TRI_LOG_SEVERITY_UNKNOWN && + appender->_severityFilter != msg->_severity) { + continue; + } + + // apply content filter on log message + if (appender->_contentFilter != NULL) { + if (! TRI_IsContainedString(msg->_message, appender->_contentFilter)) { + continue; + } + } + appender->log(appender, msg->_level, msg->_severity, msg->_message, msg->_length); + + if (appender->_consume) { + break; + } } TRI_UnlockSpin(&AppendersLock); @@ -983,6 +1040,7 @@ void TRI_SetLogSeverityLogging (char const* severities) { IsTechnical = 0; IsFunctional = 0; IsDevelopment = 0; + IsUsage = 0; IsHuman = 0; n = split._length; @@ -1002,6 +1060,9 @@ void TRI_SetLogSeverityLogging (char const* severities) { else if (TRI_CaseEqualString(type, "development")) { IsDevelopment = 1; } + else if (TRI_CaseEqualString(type, "usage")) { + IsUsage = 1; + } else if (TRI_CaseEqualString(type, "human")) { IsHuman = 1; } @@ -1010,6 +1071,7 @@ void TRI_SetLogSeverityLogging (char const* severities) { IsTechnical = 1; IsFunctional = 1; IsDevelopment = 1; + IsUsage = 1; IsHuman = 1; } else if (TRI_CaseEqualString(type, "non-human")) { @@ -1017,6 +1079,7 @@ void TRI_SetLogSeverityLogging (char const* severities) { IsTechnical = 1; IsFunctional = 1; IsDevelopment = 1; + IsUsage = 1; } } @@ -1072,6 +1135,14 @@ void TRI_SetFileToLog (char const* file) { FilesToLog[TRI_FnvHashString(file) % FilesToLogSize] = true; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks if usage logging is enabled +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_IsUsageLogging () { + return IsUsage != 0; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief checks if human logging is enabled //////////////////////////////////////////////////////////////////////////////// @@ -1458,6 +1529,10 @@ static void LogAppenderFile_Close (TRI_log_appender_t* appender) { TRI_FreeString(TRI_CORE_MEM_ZONE, self->_filename); } + if (self->base._contentFilter != NULL) { + TRI_FreeString(TRI_CORE_MEM_ZONE, self->base._contentFilter); + } + TRI_DestroySpin(&self->_lock); TRI_Free(TRI_CORE_MEM_ZONE, self); @@ -1480,7 +1555,10 @@ static void LogAppenderFile_Close (TRI_log_appender_t* appender) { /// @brief creates a log appender for file output //////////////////////////////////////////////////////////////////////////////// -TRI_log_appender_t* TRI_CreateLogAppenderFile (char const* filename) { +TRI_log_appender_t* TRI_CreateLogAppenderFile (char const* filename, + char const* contentFilter, + TRI_log_severity_e severityFilter, + bool consume) { log_appender_file_t* appender; // no logging @@ -1496,6 +1574,19 @@ TRI_log_appender_t* TRI_CreateLogAppenderFile (char const* filename) { return NULL; } + + appender->base._contentFilter = NULL; + appender->base._severityFilter = severityFilter; + appender->base._consume = consume; + + if (contentFilter != NULL) { + if (NULL == (appender->base._contentFilter = TRI_DuplicateStringZ(TRI_CORE_MEM_ZONE, contentFilter))) { + TRI_Free(TRI_CORE_MEM_ZONE, appender); + TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); + + return NULL; + } + } // logging to stdout if (TRI_EqualString(filename, "+")) { @@ -1521,6 +1612,7 @@ TRI_log_appender_t* TRI_CreateLogAppenderFile (char const* filename) { TRI_SetCloseOnExitFile(appender->_fd); appender->_filename = TRI_DuplicateString(filename); + if (appender->_filename == NULL) { TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); TRI_Free(TRI_CORE_MEM_ZONE, appender); @@ -1530,9 +1622,9 @@ TRI_log_appender_t* TRI_CreateLogAppenderFile (char const* filename) { } // set methods - appender->base.log = LogAppenderFile_Log; + appender->base.log = LogAppenderFile_Log; appender->base.reopen = LogAppenderFile_Reopen; - appender->base.close = LogAppenderFile_Close; + appender->base.close = LogAppenderFile_Close; // create lock TRI_InitSpin(&appender->_lock); @@ -1607,6 +1699,7 @@ static void LogAppenderSyslog_Log (TRI_log_appender_t* appender, switch (severity) { case TRI_LOG_SEVERITY_EXCEPTION: priority = LOG_CRIT; break; case TRI_LOG_SEVERITY_FUNCTIONAL: priority = LOG_NOTICE; break; + case TRI_LOG_SEVERITY_USAGE: priority = LOG_INFO; break; case TRI_LOG_SEVERITY_TECHNICAL: priority = LOG_INFO; break; case TRI_LOG_SEVERITY_DEVELOPMENT: priority = LOG_DEBUG; break; default: priority = LOG_DEBUG; break; @@ -1653,12 +1746,16 @@ static void LogAppenderSyslog_Close (TRI_log_appender_t* appender) { log_appender_syslog_t* self; self = (log_appender_syslog_t*) appender; - + TRI_LockMutex(&self->_mutex); closelog(); TRI_UnlockMutex(&self->_mutex); TRI_DestroyMutex(&self->_mutex); + + if (self->base._contentFilter != NULL) { + TRI_FreeString(TRI_CORE_MEM_ZONE, self->base._contentFilter); + } TRI_Free(TRI_CORE_MEM_ZONE, self); } @@ -1684,7 +1781,11 @@ static void LogAppenderSyslog_Close (TRI_log_appender_t* appender) { #ifdef TRI_ENABLE_SYSLOG -TRI_log_appender_t* TRI_CreateLogAppenderSyslog (char const* name, char const* facility) { +TRI_log_appender_t* TRI_CreateLogAppenderSyslog (char const* name, + char const* facility, + char const* contentFilter, + TRI_log_severity_e severityFilter, + bool consume) { log_appender_syslog_t* appender; int value; @@ -1699,10 +1800,27 @@ TRI_log_appender_t* TRI_CreateLogAppenderSyslog (char const* name, char const* f // allocate space appender = (log_appender_syslog_t*) TRI_Allocate(TRI_CORE_MEM_ZONE, sizeof(log_appender_syslog_t), false); + if (appender == NULL) { + return NULL; + } + + appender->base._contentFilter = NULL; + appender->base._severityFilter = severityFilter; + appender->base._consume = consume; + // set methods - appender->base.log = LogAppenderSyslog_Log; - appender->base.reopen = LogAppenderSyslog_Reopen; - appender->base.close = LogAppenderSyslog_Close; + appender->base.log = LogAppenderSyslog_Log; + appender->base.reopen = LogAppenderSyslog_Reopen; + appender->base.close = LogAppenderSyslog_Close; + + if (contentFilter != NULL) { + if (NULL == (appender->base._contentFilter = TRI_DuplicateStringZ(TRI_CORE_MEM_ZONE, contentFilter))) { + TRI_Free(TRI_CORE_MEM_ZONE, appender); + TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); + + return NULL; + } + } TRI_InitMutex(&appender->_mutex); diff --git a/lib/BasicsC/logging.h b/lib/BasicsC/logging.h index 829705f72c..e5905a7c67 100644 --- a/lib/BasicsC/logging.h +++ b/lib/BasicsC/logging.h @@ -54,12 +54,12 @@ extern "C" { //////////////////////////////////////////////////////////////////////////////// typedef enum { - TRI_LOG_LEVEL_FATAL = 1, - TRI_LOG_LEVEL_ERROR = 2, + TRI_LOG_LEVEL_FATAL = 1, + TRI_LOG_LEVEL_ERROR = 2, TRI_LOG_LEVEL_WARNING = 3, - TRI_LOG_LEVEL_INFO = 4, - TRI_LOG_LEVEL_DEBUG = 5, - TRI_LOG_LEVEL_TRACE = 6 + TRI_LOG_LEVEL_INFO = 4, + TRI_LOG_LEVEL_DEBUG = 5, + TRI_LOG_LEVEL_TRACE = 6 } TRI_log_level_e; @@ -68,12 +68,13 @@ TRI_log_level_e; //////////////////////////////////////////////////////////////////////////////// typedef enum { - TRI_LOG_SEVERITY_EXCEPTION = 1, - TRI_LOG_SEVERITY_TECHNICAL = 2, - TRI_LOG_SEVERITY_FUNCTIONAL = 3, + TRI_LOG_SEVERITY_EXCEPTION = 1, + TRI_LOG_SEVERITY_TECHNICAL = 2, + TRI_LOG_SEVERITY_FUNCTIONAL = 3, TRI_LOG_SEVERITY_DEVELOPMENT = 4, - TRI_LOG_SEVERITY_HUMAN = 5, - TRI_LOG_SEVERITY_UNKNOWN = 6 + TRI_LOG_SEVERITY_USAGE = 5, + TRI_LOG_SEVERITY_HUMAN = 6, + TRI_LOG_SEVERITY_UNKNOWN = 7 } TRI_log_severity_e; @@ -84,9 +85,9 @@ TRI_log_severity_e; typedef enum { // exceptions - TRI_LOG_CATEGORY_FATAL = 1000, - TRI_LOG_CATEGORY_ERROR = 1001, - TRI_LOG_CATEGORY_WARNING = 1002, + TRI_LOG_CATEGORY_FATAL = 1000, + TRI_LOG_CATEGORY_ERROR = 1001, + TRI_LOG_CATEGORY_WARNING = 1002, // technical TRI_LOG_CATEGORY_HEARTBEAT = 2000, @@ -179,6 +180,12 @@ void TRI_SetFunctionLogging (bool show); void TRI_SetFileToLog (char const* file); +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks if usage logging is enabled +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_IsUsageLogging (void); + //////////////////////////////////////////////////////////////////////////////// /// @brief checks if human logging is enabled //////////////////////////////////////////////////////////////////////////////// @@ -433,6 +440,28 @@ void CLEANUP_LOGGING_AND_EXIT_ON_FATAL_ERROR (void); #endif +//////////////////////////////////////////////////////////////////////////////// +/// @brief logs usage messages +//////////////////////////////////////////////////////////////////////////////// + +#undef LOG_USAGE + +#ifdef TRI_ENABLE_LOGGER + +#define LOG_USAGE(...) \ + do { \ + LOG_ARG_CHECK(__VA_ARGS__) \ + if (TRI_IsUsageLogging()) { \ + TRI_Log(__FUNCTION__, __FILE__, __LINE__, TRI_LOG_LEVEL_INFO, TRI_LOG_SEVERITY_USAGE, __VA_ARGS__); \ + } \ + } while (0) + +#else + +#define LOG_USAGE(...) while (0) + +#endif + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// @@ -473,14 +502,21 @@ struct TRI_log_appender_s; /// @brief creates a log append for file output //////////////////////////////////////////////////////////////////////////////// -struct TRI_log_appender_s* TRI_CreateLogAppenderFile (char const* filename); +struct TRI_log_appender_s* TRI_CreateLogAppenderFile (char const*, + char const*, + TRI_log_severity_e, + bool); //////////////////////////////////////////////////////////////////////////////// /// @brief creates a log append for syslog //////////////////////////////////////////////////////////////////////////////// #ifdef TRI_ENABLE_SYSLOG -struct TRI_log_appender_s* TRI_CreateLogAppenderSyslog (char const* name, char const* facility); +struct TRI_log_appender_s* TRI_CreateLogAppenderSyslog (char const*, + char const*, + char const*, + TRI_log_severity_e, + bool); #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/BasicsC/tri-strings.c b/lib/BasicsC/tri-strings.c index 4a96d1f15f..fa90e92004 100644 --- a/lib/BasicsC/tri-strings.c +++ b/lib/BasicsC/tri-strings.c @@ -524,6 +524,14 @@ bool TRI_IsPrefixString (char const* full, char const* prefix) { return strncmp(full, prefix, strlen(prefix)) == 0; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief tests if second string is contained in the first +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_IsContainedString (char const* full, char const* part) { + return strstr(full, part) != NULL; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief duplicates a string, without using a memory zone //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/BasicsC/tri-strings.h b/lib/BasicsC/tri-strings.h index e1773263d4..348aa89642 100644 --- a/lib/BasicsC/tri-strings.h +++ b/lib/BasicsC/tri-strings.h @@ -111,6 +111,12 @@ bool TRI_CaseEqualString2 (char const* left, char const* right, size_t n); bool TRI_IsPrefixString (char const* full, char const* prefix); +//////////////////////////////////////////////////////////////////////////////// +/// @brief tests if second string is contained in the first +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_IsContainedString (char const* full, char const* part); + //////////////////////////////////////////////////////////////////////////////// /// @brief duplicates a string, without using a memory zone /// diff --git a/lib/HttpServer/HttpCommTask.h b/lib/HttpServer/HttpCommTask.h index 9ea77884ab..4fdc27fb6b 100644 --- a/lib/HttpServer/HttpCommTask.h +++ b/lib/HttpServer/HttpCommTask.h @@ -140,6 +140,10 @@ namespace triagens { this->_bodyLength = 0; this->_readRequestBody = false; this->_requestPending = false; + + this->_httpVersion = HttpRequest::HTTP_UNKNOWN; + this->_requestType = HttpRequest::HTTP_REQUEST_ILLEGAL; + this->_fullUrl = ""; } //////////////////////////////////////////////////////////////////////////////// @@ -223,8 +227,11 @@ namespace triagens { return true; } - // check HTTP protocol - if (! this->_request->isHttp10() && ! this->_request->isHttp11()) { + // check HTTP protocol version + _httpVersion = this->_request->httpVersion(); + + if (_httpVersion != HttpRequest::HTTP_1_0 && + _httpVersion != HttpRequest::HTTP_1_1) { HttpResponse response(HttpResponse::HTTP_VERSION_NOT_SUPPORTED); this->handleResponse(&response); this->resetState(); @@ -233,7 +240,8 @@ namespace triagens { } // check max URL length - if (this->_request->fullUrl().length() > 16384) { + _fullUrl = this->_request->fullUrl(); + if (_fullUrl.size() > 16384) { HttpResponse response(HttpResponse::REQUEST_URI_TOO_LONG); this->handleResponse(&response); this->resetState(); @@ -306,15 +314,6 @@ namespace triagens { } } - - // enable the following statement for excessive logging of incoming requests - // LOGGER_INFO(this->_connectionInfo.serverAddress << "," << this->_connectionInfo.serverPort << "," << - // this->_connectionInfo.clientAddress << "," << - // HttpRequest::translateMethod(this->_request->requestType()) << "," << - // HttpRequest::translateVersion(this->_request->httpVersion()) << "," << - // this->_bodyLength << "," << - // this->_request->fullUrl()); - // ............................................................................. // check if server is active // ............................................................................. @@ -530,15 +529,13 @@ namespace triagens { // not authenticated else { - string realm = "basic realm=\"" + this->_server->getHandlerFactory()->authenticationRealm(this->_request) + "\""; - - delete this->_request; - this->_request = 0; + const string realm = "basic realm=\"" + this->_server->getHandlerFactory()->authenticationRealm(this->_request) + "\""; HttpResponse response(HttpResponse::UNAUTHORIZED); response.setHeader("www-authenticate", strlen("www-authenticate"), realm.c_str()); this->handleResponse(&response); + this->resetState(); } return processRead(); @@ -606,6 +603,14 @@ namespace triagens { LOGGER_TRACE("HTTP WRITE FOR " << static_cast(this) << ":\n" << buffer->c_str()); + // disable the following statement to prevent excessive logging of incoming requests + LOGGER_USAGE(this->_connectionInfo.clientAddress << " \"" << + HttpRequest::translateMethod(this->_requestType) << " " << + this->_fullUrl << " " << + HttpRequest::translateVersion(this->_httpVersion) << "\" " << + response->responseCode() << " " << + response->body().length()); + // clear body response->body().clear(); @@ -670,12 +675,24 @@ namespace triagens { private: +//////////////////////////////////////////////////////////////////////////////// +/// @brief http version number used +//////////////////////////////////////////////////////////////////////////////// + + HttpRequest::HttpVersion _httpVersion; + //////////////////////////////////////////////////////////////////////////////// /// @brief type of request (GET, POST, ...) //////////////////////////////////////////////////////////////////////////////// HttpRequest::HttpRequestType _requestType; +//////////////////////////////////////////////////////////////////////////////// +/// @brief value of requested URL +//////////////////////////////////////////////////////////////////////////////// + + std::string _fullUrl; + //////////////////////////////////////////////////////////////////////////////// /// @brief value of the HTTP origin header the client sent (if any). /// this is only used for CORS diff --git a/lib/Logger/Logger.cpp b/lib/Logger/Logger.cpp index 950547e94f..ef781d07d1 100644 --- a/lib/Logger/Logger.cpp +++ b/lib/Logger/Logger.cpp @@ -104,6 +104,12 @@ void TRI_SetFileToLog (string const& file) { static string LoggerFormat = "%Z;1;%S;%C;%H;%p-%t;%F;%A;%f;%m;%K;%f:%l;%x;%P;%u;%V;%U;%E"; +//////////////////////////////////////////////////////////////////////////////// +/// @brief logger format for raw logging +//////////////////////////////////////////////////////////////////////////////// + +static string LoggerRawFormat = "%Z %x"; + //////////////////////////////////////////////////////////////////////////////// /// @brief special characters which must be escaped //////////////////////////////////////////////////////////////////////////////// @@ -128,8 +134,18 @@ static string const SpecialCharacters = ";%\r\t\n"; //////////////////////////////////////////////////////////////////////////////// static void OutputMachine (string const& text, LoggerData::Info const& info) { - char const* format = LoggerFormat.c_str(); - char const* end = format + LoggerFormat.size(); + char const* format; + char const* end; + + if (info._severity == TRI_LOG_SEVERITY_USAGE) { + format = LoggerRawFormat.c_str(); + end = format + LoggerRawFormat.size(); + } + else { + format = LoggerFormat.c_str(); + end = format + LoggerFormat.size(); + } + time_t tt = time(0); StringBuffer line(TRI_CORE_MEM_ZONE); @@ -298,6 +314,7 @@ static void OutputMachine (string const& text, LoggerData::Info const& info) { switch (info._severity) { case TRI_LOG_SEVERITY_EXCEPTION: line.appendInteger(2); break; case TRI_LOG_SEVERITY_FUNCTIONAL: line.appendInteger(5); break; + case TRI_LOG_SEVERITY_USAGE: line.appendInteger(5); break; case TRI_LOG_SEVERITY_TECHNICAL: line.appendInteger(6); break; case TRI_LOG_SEVERITY_DEVELOPMENT: line.appendInteger(7); break; default: line.appendInteger(7); break; @@ -534,7 +551,6 @@ Logger::~Logger () { //////////////////////////////////////////////////////////////////////////////// void Logger::output (string const& text, LoggerData::Info const& info) { - // human readable if (info._severity == TRI_LOG_SEVERITY_HUMAN) { if (! TRI_IsHumanLogging()) { @@ -547,12 +563,18 @@ void Logger::output (string const& text, LoggerData::Info const& info) { info._level, info._severity, "%s", text.c_str()); + + return; + } + // usage logging + else if (info._severity == TRI_LOG_SEVERITY_USAGE) { + if (! TRI_IsUsageLogging()) { + return; + } } // machine readable logging - else { - OutputMachine(text, info); - } + OutputMachine(text, info); } //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/Logger/Logger.h b/lib/Logger/Logger.h index f4387d7409..a42c5a0e94 100644 --- a/lib/Logger/Logger.h +++ b/lib/Logger/Logger.h @@ -358,6 +358,31 @@ #endif +//////////////////////////////////////////////////////////////////////////////// +/// @brief logs usage messages +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_LOGGER + +#define LOGGER_USAGE(a) \ + do { \ + if (TRI_IsUsageLogging()) { \ + triagens::basics::Logger::_singleton \ + << TRI_LOG_LEVEL_INFO \ + << TRI_LOG_SEVERITY_USAGE \ + << triagens::basics::LoggerData::Position(__FUNCTION__, __FILE__, __LINE__) \ + << a; \ + } \ + } \ + while (0) + +#else + +#define LOGGER_USAGE(a) \ + while (0) + +#endif + //////////////////////////////////////////////////////////////////////////////// /// @brief logs non-human messsages ////////////////////////////////////////////////////////////////////////////////