diff options
author | Markus Mohrhard <markus.mohrhard@googlemail.com> | 2016-12-19 12:54:34 +0100 |
---|---|---|
committer | Thorsten Behrens <Thorsten.Behrens@CIB.de> | 2017-02-20 01:38:37 +0100 |
commit | 4ff75747c530452fc671d8584ee1a4e26effd25a (patch) | |
tree | 651d6433e057457f5869d996bc8a35e1634a485f | |
parent | 3e6476c3508aec3058d2b61e8b45a09c9aa8a354 (diff) |
Add logging to file and log config for Windows
Patch set comprised of:
add an option to pipe log output to file
The target file and the option are controlled by SAL_LOG_FILE. The
variable should contain the target file in a system dependent notation.
Change-Id: Ie1ce9c39740c8b7a989e5af222be21a3e3142084
Reviewed-on: https://gerrit.libreoffice.org/32176
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Markus Mohrhard <markus.mohrhard@googlemail.com>
(cherry picked from commit fb22a4f0d92d1a7a371fab21c965b26b9c0a5b42)
Allow to set log level/path in file
Expects a file logging.txt in the program/ directory
with the keys LogFilePath and LogLevel, e.g.:
LogFilePath=C:\\log.txt
LogLevel=info
Change-Id: I35c730469e4079139da908aa287b989dc98e0f9d
(cherry picked from commit e06a339e37929af1132cd454e2fbd412ad55c3a5)
sal: don't reopen logfile on every log line
Change-Id: Iac34fc87a99e98ca5e41eb621d55bf356c371e97
(cherry picked from commit 45b33aa06fc9a9d866b086777cee209a23b92e2e)
sal: log windows trace output to debugger console
Change-Id: Ic8fea70fd3b0b2d4881cd30e3616f5bbf7c0c533
(cherry picked from commit c249b51db18302e02b86b9b13270ca21eecdcf36)
Optionally include timestamp in the log
Change-Id: Ia9f162b704b6e16c84f259e9548e91b7bcd6a378
(cherry picked from commit 11536e5cde942692e90df33ff74f53df2b7e2548)
This code wouldn't otherwise build on 5.0.7@Linux
Change-Id: I106c1a286ff5ad710a54cabc873cc5356f0a3123
(cherry picked from commit 7a0a072224ecc46fab96b6074df33cd6a308721f)
Fix crash when logging.ini does not exist
Change-Id: I1ed82fdcce1eb15548e699ffbf93ae39664612b3
(cherry picked from commit d7ecdc3901cca29d37fe597744dffb66c46e0ee6)
sal: flush log-to-file, remove extra linefeeds otherwise
syslog and windows debugger console don't need explicit line feeds.
For ofstream, use endl which implies a flush.
Change-Id: Id94e07aa1ae042557052fab6483e5db75aada89d
(cherry picked from commit c7f35d079fd364f236ce1df6ee1f32bb80d1cd22)
-rw-r--r-- | sal/osl/all/log.cxx | 189 |
1 files changed, 175 insertions, 14 deletions
diff --git a/sal/osl/all/log.cxx b/sal/osl/all/log.cxx index 67e384cb6fcd..c7ea7970c195 100644 --- a/sal/osl/all/log.cxx +++ b/sal/osl/all/log.cxx @@ -15,13 +15,17 @@ #include <cstdio> #include <cstdlib> #include <cstring> +#include <ctime> #include <sstream> #include <stdio.h> #include <string.h> +#include <fstream> #include "osl/thread.hxx" +#include "osl/process.h" #include "rtl/string.h" +#include "rtl/ustring.hxx" #include "sal/detail/log.h" #include "sal/log.hxx" #include "sal/types.h" @@ -32,6 +36,7 @@ #include <android/log.h> #elif defined WNT #include <process.h> +#include <windows.h> #define OSL_DETAIL_GETPID _getpid() #else #include <unistd.h> @@ -80,16 +85,16 @@ char const * toString(sal_detail_LogLevel level) { // the process is running": #if defined ANDROID -char const * getEnvironmentVariable() { +char const * getLogLevel() { return std::getenv("SAL_LOG"); } #else -char const * getEnvironmentVariable_() { - char const * p1 = std::getenv("SAL_LOG"); - if (p1 == 0) { - return 0; +char const * getEnvironmentVariable_(const char* env) { + char const * p1 = std::getenv(env); + if (p1 == nullptr) { + return nullptr; } char const * p2 = strdup(p1); // leaked if (p2 == 0) { @@ -98,9 +103,146 @@ char const * getEnvironmentVariable_() { return p2; } -char const * getEnvironmentVariable() { - static char const * env = getEnvironmentVariable_(); - return env; +bool getValueFromLoggingIniFile(const char* key, char* value) { + rtl::OUString programDirectoryURL; + rtl::OUString programDirectoryPath; + osl_getProcessWorkingDir(&(programDirectoryURL.pData)); + osl_getSystemPathFromFileURL(programDirectoryURL.pData, &programDirectoryPath.pData); + rtl::OUString aLogFile(programDirectoryPath + "/" + "logging.ini"); + std::ifstream logFileStream(rtl::OUStringToOString( aLogFile, RTL_TEXTENCODING_ASCII_US).getStr()); + if (!logFileStream.good()) + return false; + + std::size_t n; + std::string aKey; + std::string aValue; + std::string sWantedKey(key); + std::string sLine; + while (std::getline(logFileStream, sLine)) { + if (sLine.find('#') == 0) + continue; + if ( ( n = sLine.find('=') ) != std::string::npos) { + aKey = sLine.substr(0, n); + if (aKey != sWantedKey) + continue; + aValue = sLine.substr(n+1, sLine.length()); + sprintf(value, "%s", aValue.c_str()); + return true; + } + } + return false; +} + +char const * getLogLevel() { + // First check the environment variable, then the setting in logging.ini + static char const * env = getEnvironmentVariable_("SAL_LOG"); + if (env != nullptr) + return env; + + static char logLevel[1024]; + if (getValueFromLoggingIniFile("LogLevel", logLevel)) { + return logLevel; + } + + return nullptr; +} + +char const * getLogFilePath() { + // First check the environment variable, then the setting in logging.ini + static char const * logFile = getEnvironmentVariable_("SAL_LOG_FILE"); + if (logFile != nullptr) + return logFile; + + static char logFilePath[1024]; + if (getValueFromLoggingIniFile("LogFilePath", logFilePath)) { + return logFilePath; + } + + return nullptr; +} + +std::ofstream * getLogFile() { + static char const * logFilePath = getLogFilePath(); + if (logFilePath == nullptr) + return nullptr; + + static std::ofstream file(logFilePath, std::ios::app | std::ios::out); + return &file; +} + +void maybeOutputTimestamp(std::ostringstream &s) { + char const * env = getLogLevel(); + if (env == nullptr) + return; + bool outputTimestamp = false; + bool outputRelativeTimer = false; + for (char const * p = env;;) { + switch (*p++) { + case '\0': + if (outputTimestamp) { + char ts[100]; + TimeValue systemTime; + osl_getSystemTime(&systemTime); + TimeValue localTime; + osl_getLocalTimeFromSystemTime(&systemTime, &localTime); + oslDateTime dateTime; + osl_getDateTimeFromTimeValue(&localTime, &dateTime); + struct tm tm; + tm.tm_sec = dateTime.Seconds; + tm.tm_min = dateTime.Minutes; + tm.tm_hour = dateTime.Hours; + tm.tm_mday = dateTime.Day; + tm.tm_mon = dateTime.Month - 1; + tm.tm_year = dateTime.Year - 1900; + strftime(ts, sizeof(ts), "%Y-%m-%d:%H:%M:%S", &tm); + char milliSecs[10]; + sprintf(milliSecs, "%03d", static_cast<int>(dateTime.NanoSeconds/1000000)); + s << ts << '.' << milliSecs << ':'; + } + if (outputRelativeTimer) { + static bool beenHere = false; + static TimeValue first; + if (!beenHere) { + osl_getSystemTime(&first); + beenHere = true; + } + TimeValue now; + osl_getSystemTime(&now); + int seconds = now.Seconds - first.Seconds; + int milliSeconds; + if (now.Nanosec < first.Nanosec) { + seconds--; + milliSeconds = 1000-(first.Nanosec-now.Nanosec)/1000000; + } + else + milliSeconds = (now.Nanosec-first.Nanosec)/1000000; + char relativeTimestamp[100]; + sprintf(relativeTimestamp, "%d.%03d", seconds, milliSeconds); + s << relativeTimestamp << ':'; + } + return; + case '+': + { + char const * p1 = p; + while (*p1 != '.' && *p1 != '+' && *p1 != '-' && *p1 != '\0') { + ++p1; + } + if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("TIMESTAMP"))) + outputTimestamp = true; + else if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("RELATIVETIMER"))) + outputRelativeTimer = true; + char const * p2 = p1; + while (*p2 != '+' && *p2 != '-' && *p2 != '\0') { + ++p2; + } + p = p2; + } + break; + default: + ; // nothing + } + } + return; } #endif @@ -109,8 +251,8 @@ bool report(sal_detail_LogLevel level, char const * area) { if (level == SAL_DETAIL_LOG_LEVEL_DEBUG) return true; assert(area != 0); - char const * env = getEnvironmentVariable(); - if (env == 0) { + static char const * env = getLogLevel(); + if (env == 0 || strcmp(env, "+TIMESTAMP") == 0) { env = "+WARN"; } std::size_t areaLen = std::strlen(area); @@ -147,6 +289,10 @@ bool report(sal_detail_LogLevel level, char const * area) { } else if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("WARN"))) { match = level == SAL_DETAIL_LOG_LEVEL_WARN; + } else if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("TIMESTAMP"))) + { + // handled later + match = false; } else { return true; // upon an illegal SAL_LOG value, everything is considered @@ -181,8 +327,9 @@ void log( std::ostringstream s; #if !defined ANDROID // On Android, the area will be used as the "tag," and log info already - // contains the PID + // contains timestamp and PID. if (!sal_use_syslog) { + maybeOutputTimestamp(s); s << toString(level) << ':'; } if (level != SAL_DETAIL_LOG_LEVEL_DEBUG) { @@ -199,7 +346,8 @@ void log( + (std::strncmp(where, SRCDIR "/", nStrLen) == 0 ? nStrLen : 0)); } - s << message << '\n'; + s << message; + #if defined ANDROID int android_log_level; switch (level) { @@ -240,8 +388,21 @@ void log( syslog(prio, "%s", s.str().c_str()); #endif } else { - std::fputs(s.str().c_str(), stderr); - std::fflush(stderr); + static std::ofstream * logFile = getLogFile(); + if (logFile) { + *logFile << s.str() << std::endl; + } +#if defined WNT + else { + OutputDebugString(s.str().c_str()); + } +#else + else { + s << '\n'; + std::fputs(s.str().c_str(), stderr); + std::fflush(stderr); + } +#endif } #endif } |