// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#include "logging.hpp"
+#include "Logging.hpp"
#include "Config.hpp"
#include "File.hpp"
# include <tchar.h>
#endif
+using nonstd::string_view;
+
namespace {
// Destination for g_config.log_file.
// Buffer used for logs in debug mode.
std::string debug_log_buffer;
+// Whether debug logging is enabled via configuration or environment variable.
bool debug_log_enabled = false;
-} // namespace
-
-// Initialize logging. Call only once.
-void
-init_log(const Config& config)
+// Print error message to stderr about failure writing to the log file and exit
+// with failure.
+[[noreturn]] void
+print_fatal_error_and_exit()
{
- debug_log_enabled = config.debug();
-
-#ifdef HAVE_SYSLOG
- if (config.log_file() == "syslog") {
- use_syslog = true;
- openlog("ccache", LOG_PID, LOG_USER);
- return; // Don't open logfile
- }
-#endif
-
- logfile_path = config.log_file();
- logfile.open(logfile_path, "a");
-#ifndef _WIN32
- if (logfile) {
- Util::set_cloexec_flag(fileno(*logfile));
- }
-#endif
+ // Note: Can't call fatal() since that would lead to recursion.
+ fprintf(stderr,
+ "ccache: error: Failed to write to %s: %s\n",
+ logfile_path.c_str(),
+ strerror(errno));
+ exit(EXIT_FAILURE);
}
-static void
-log_prefix(bool log_updated_time)
+void
+do_log(string_view message, bool bulk)
{
static char prefix[200];
-#ifdef HAVE_GETTIMEOFDAY
- if (log_updated_time) {
+
+ if (!bulk) {
char timestamp[100];
struct timeval tv;
gettimeofday(&tv, nullptr);
static_cast<int>(tv.tv_usec),
static_cast<int>(getpid()));
}
-#else
- snprintf(prefix, sizeof(prefix), "[%-5d] ", static_cast<int>(getpid()));
-#endif
- if (logfile) {
- fputs(prefix, *logfile);
+
+ if (logfile
+ && (fputs(prefix, *logfile) == EOF
+ || fwrite(message.data(), message.length(), 1, *logfile) != 1
+ || fputc('\n', *logfile) == EOF
+ || (!bulk && fflush(*logfile) == EOF))) {
+ print_fatal_error_and_exit();
}
#ifdef HAVE_SYSLOG
if (use_syslog) {
- // prefix information will be added by syslog
+ // Note: No log prefix since syslog will add a prefix of its own.
+ syslog(
+ LOG_DEBUG, "%.*s", static_cast<int>(message.length()), message.data());
+ // Note: No trailing newline.
}
#endif
if (debug_log_enabled) {
debug_log_buffer += prefix;
+ debug_log_buffer.append(message.data(), message.length());
+ debug_log_buffer += '\n';
}
}
-static void warn_log_fail() ATTR_NORETURN;
+} // namespace
-// Warn about failure writing to the log file and then exit.
-static void
-warn_log_fail()
-{
- // Note: Can't call fatal() since that would lead to recursion.
- fprintf(stderr,
- "ccache: error: Failed to write to %s: %s\n",
- logfile_path.c_str(),
- strerror(errno));
- exit(EXIT_FAILURE);
-}
+namespace Logging {
-static void
-vlog(const char* format, va_list ap, bool log_updated_time)
+// Initialize logging. Call only once.
+void
+init(const Config& config)
{
- if (!(debug_log_enabled || logfile || use_syslog)) {
- return;
- }
+ debug_log_enabled = config.debug();
- va_list aq;
- va_copy(aq, ap);
- log_prefix(log_updated_time);
- if (logfile) {
- int rc1 = vfprintf(*logfile, format, ap);
- int rc2 = fprintf(*logfile, "\n");
- if (rc1 < 0 || rc2 < 0) {
- warn_log_fail();
- }
- }
#ifdef HAVE_SYSLOG
- if (use_syslog) {
- vsyslog(LOG_DEBUG, format, ap);
+ if (config.log_file() == "syslog") {
+ use_syslog = true;
+ openlog("ccache", LOG_PID, LOG_USER);
+ return; // Don't open logfile
}
#endif
- if (debug_log_enabled) {
- char buf[8192];
- int len = vsnprintf(buf, sizeof(buf), format, aq);
- if (len >= 0) {
- debug_log_buffer.append(
- buf, std::min(static_cast<size_t>(len), sizeof(buf) - 1));
- debug_log_buffer += "\n";
+
+ if (!config.log_file().empty()) {
+ logfile_path = config.log_file();
+ logfile.open(logfile_path, "a");
+ if (logfile) {
+ Util::set_cloexec_flag(fileno(*logfile));
+ } else {
+ print_fatal_error_and_exit();
}
}
- va_end(aq);
}
-// Write a message to the log file (adding a newline) and flush.
-void
-cc_log(const char* format, ...)
+bool
+enabled()
{
- va_list ap;
- va_start(ap, format);
- vlog(format, ap, true);
- va_end(ap);
- if (logfile) {
- fflush(*logfile);
- }
+ return debug_log_enabled || logfile || use_syslog;
}
-// Write a message to the log file (adding a newline) without flushing and with
-// a reused timestamp.
void
-cc_bulklog(const char* format, ...)
+log(string_view message)
{
- va_list ap;
- va_start(ap, format);
- vlog(format, ap, false);
- va_end(ap);
+ if (!enabled()) {
+ return;
+ }
+ do_log(message, false);
}
-// Log an executed command to the CCACHE_LOGFILE location.
void
-cc_log_argv(const char* prefix, const char* const* argv)
+bulk_log(string_view message)
{
- if (!(debug_log_enabled || logfile || use_syslog)) {
+ if (!enabled()) {
return;
}
-
- std::string argv_as_string = Util::format_argv_for_logging(argv);
-
- log_prefix(true);
- if (logfile) {
- fputs(prefix, *logfile);
- fwrite(argv_as_string.data(), argv_as_string.length(), 1, *logfile);
- fputc('\n', *logfile);
- int rc = fflush(*logfile);
- if (rc) {
- warn_log_fail();
- }
- }
-#ifdef HAVE_SYSLOG
- if (use_syslog) {
- syslog(LOG_DEBUG, "%s", Util::format_argv_for_logging(argv).c_str());
- }
-#endif
- if (debug_log_enabled) {
- debug_log_buffer += prefix;
- debug_log_buffer += argv_as_string;
- debug_log_buffer += '\n';
- }
+ do_log(message, true);
}
-// Copy the current log memory buffer to an output file.
void
-cc_dump_debug_log_buffer(const char* path)
+dump_log(const std::string& path)
{
- if (!debug_log_enabled) {
+ if (!enabled()) {
return;
}
File file(path, "w");
if (file) {
- (void)fwrite(debug_log_buffer.data(), 1, debug_log_buffer.length(), *file);
+ (void)fwrite(debug_log_buffer.data(), debug_log_buffer.length(), 1, *file);
} else {
- cc_log("Failed to open %s: %s", path, strerror(errno));
+ log("Failed to open {}: {}", path, strerror(errno));
+ }
+}
+
+} // namespace Logging
+
+void
+cc_log(const char* format, ...)
+{
+ if (!Logging::enabled()) {
+ return;
}
+
+ va_list ap;
+ va_start(ap, format);
+
+ char buffer[16384];
+ int size = vsnprintf(buffer, sizeof(buffer), format, ap);
+ Logging::log(string_view(buffer, size));
+
+ va_end(ap);
}
--- /dev/null
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#include "system.hpp"
+
+#include "FormatNonstdStringView.hpp"
+
+#include "third_party/fmt/core.h"
+#include "third_party/nonstd/optional.hpp"
+#include "third_party/nonstd/string_view.hpp"
+
+#include <string>
+#include <utility>
+
+class Config;
+
+namespace Logging {
+
+// Initialize global logging state. Must be called once before using the other
+// logging functions.
+void init(const Config& config);
+
+// Return whether logging is enabled to at least one destination.
+bool enabled();
+
+// Log `message` (plus a newline character).
+void log(nonstd::string_view message);
+
+// Log `message` (plus a newline character) without flushing and with a reused
+// timestamp.
+void bulk_log(nonstd::string_view message);
+
+// Write the current log memory buffer `path`.
+void dump_log(const std::string& path);
+
+// Log a message (plus a newline character). `args` are forwarded to
+// `fmt::format`.
+template<typename... T>
+inline void
+log(T&&... args)
+{
+ if (!enabled()) {
+ return;
+ }
+ log(nonstd::string_view(fmt::format(std::forward<T>(args)...)));
+}
+
+// Log a message (plus a newline character) without flushing and with a reused
+// timestamp. `args` are forwarded to `fmt::format`.
+template<typename... T>
+inline void
+bulk_log(T&&... args)
+{
+ if (!enabled()) {
+ return;
+ }
+ bulk_log(nonstd::string_view(fmt::format(std::forward<T>(args)...)));
+}
+
+} // namespace Logging
+
+// Legacy API.
+void cc_log(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
#include "File.hpp"
#include "FormatNonstdStringView.hpp"
#include "Hash.hpp"
+#include "Logging.hpp"
#include "MiniTrace.hpp"
#include "ProgressBar.hpp"
#include "Result.hpp"
#include "execute.hpp"
#include "hashutil.hpp"
#include "language.hpp"
-#include "logging.hpp"
#include "manifest.hpp"
#include "stats.hpp"
#include <algorithm>
#include <limits>
+using Logging::log;
using nonstd::nullopt;
using nonstd::optional;
using nonstd::string_view;
{
bool primary_config_exists = set_up_config(ctx.config);
set_up_context(ctx, argc, argv);
- init_log(ctx.config);
+ Logging::init(ctx.config);
// Set default umask for all files created by ccache from now on (if
// configured to). This is intentionally done after calling init_log so that
const std::string& value,
const std::string& origin)
{
- cc_bulklog(
- "Config: (%s) %s = %s", origin.c_str(), key.c_str(), value.c_str());
+ Logging::bulk_log("Config: ({}) {} = {}", origin, key, value);
}
static void
Args saved_orig_args(std::move(ctx->orig_args));
auto execv_argv = saved_orig_args.to_argv();
- cc_log_argv("Executing ", execv_argv.data());
+ log("Executing {}", Util::format_argv_for_logging(execv_argv.data()));
ctx.reset(); // Dump debug logs last thing before executing.
execv(execv_argv[0], const_cast<char* const*>(execv_argv.data()));
fatal("execv of {} failed: {}", execv_argv[0], strerror(errno));
set_up_uncached_err();
MTR_END("main", "set_up_uncached_err");
- cc_log_argv("Command line: ", argv);
+ log("Command line: {}", Util::format_argv_for_logging(argv));
cc_log("Hostname: %s", Util::get_hostname());
cc_log("Working directory: %s", ctx.actual_cwd.c_str());
if (ctx.apparent_cwd != ctx.actual_cwd) {