summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarkus Mohrhard <markus.mohrhard@googlemail.com>2016-12-19 12:54:34 +0100
committerThorsten Behrens <Thorsten.Behrens@CIB.de>2017-02-20 01:38:37 +0100
commit4ff75747c530452fc671d8584ee1a4e26effd25a (patch)
tree651d6433e057457f5869d996bc8a35e1634a485f
parent3e6476c3508aec3058d2b61e8b45a09c9aa8a354 (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.cxx189
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
}