//===--- Logger.h - Logger interface for clangd ------------------*- C++-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_LOGGER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_LOGGER_H #include "llvm/ADT/Twine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Error.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" #include namespace clang { namespace clangd { /// Interface to allow custom logging in clangd. class Logger { public: virtual ~Logger() = default; enum Level { Debug, Verbose, Info, Error }; static char indicator(Level L) { return "DVIE"[L]; } /// Implementations of this method must be thread-safe. virtual void log(Level, const llvm::formatv_object_base &Message) = 0; }; namespace detail { const char *debugType(const char *Filename); void log(Logger::Level, const llvm::formatv_object_base &); // We often want to consume llvm::Errors by value when passing them to log(). // We automatically wrap them in llvm::fmt_consume() as formatv requires. template T &&wrap(T &&V) { return std::forward(V); } inline decltype(fmt_consume(llvm::Error::success())) wrap(llvm::Error &&V) { return fmt_consume(std::move(V)); } template void log(Logger::Level L, const char *Fmt, Ts &&... Vals) { detail::log(L, llvm::formatv(Fmt, detail::wrap(std::forward(Vals))...)); } } // namespace detail // Clangd logging functions write to a global logger set by LoggingSession. // If no logger is registered, writes to llvm::errs(). // All accept llvm::formatv()-style arguments, e.g. log("Text={0}", Text). // elog() is used for "loud" errors and warnings. // This level is often visible to users. template void elog(const char *Fmt, Ts &&... Vals) { detail::log(Logger::Error, Fmt, std::forward(Vals)...); } // log() is used for information important to understand a clangd session. // e.g. the names of LSP messages sent are logged at this level. // This level could be enabled in production builds to allow later inspection. template void log(const char *Fmt, Ts &&... Vals) { detail::log(Logger::Info, Fmt, std::forward(Vals)...); } // vlog() is used for details often needed for debugging clangd sessions. // This level would typically be enabled for clangd developers. template void vlog(const char *Fmt, Ts &&... Vals) { detail::log(Logger::Verbose, Fmt, std::forward(Vals)...); } // dlog only logs if --debug was passed, or --debug_only=Basename. // This level would be enabled in a targeted way when debugging. #define dlog(...) \ DEBUG_WITH_TYPE(::clang::clangd::detail::debugType(__FILE__), \ ::clang::clangd::detail::log(Logger::Debug, __VA_ARGS__)) /// Only one LoggingSession can be active at a time. class LoggingSession { public: LoggingSession(clangd::Logger &Instance); ~LoggingSession(); LoggingSession(LoggingSession &&) = delete; LoggingSession &operator=(LoggingSession &&) = delete; LoggingSession(LoggingSession const &) = delete; LoggingSession &operator=(LoggingSession const &) = delete; }; // Logs to an output stream, such as stderr. class StreamLogger : public Logger { public: StreamLogger(llvm::raw_ostream &Logs, Logger::Level MinLevel) : MinLevel(MinLevel), Logs(Logs) {} /// Write a line to the logging stream. void log(Level, const llvm::formatv_object_base &Message) override; private: Logger::Level MinLevel; llvm::raw_ostream &Logs; std::mutex StreamMutex; }; } // namespace clangd } // namespace clang #endif