1 //===--- Logger.h - Logger interface for clangd ------------------*- C++-*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_LOGGER_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_LOGGER_H
12 #include "llvm/Support/Debug.h"
13 #include "llvm/Support/Error.h"
14 #include "llvm/Support/FormatAdapters.h"
15 #include "llvm/Support/FormatVariadic.h"
21 /// Interface to allow custom logging in clangd.
24 virtual ~Logger() = default;
26 /// The significance or severity of this message.
27 /// Typically used to filter the output to an interesting level.
28 enum Level
: unsigned char { Debug
, Verbose
, Info
, Error
};
29 static char indicator(Level L
) { return "DVIE"[L
]; }
31 /// Implementations of this method must be thread-safe.
32 virtual void log(Level
, const char *Fmt
,
33 const llvm::formatv_object_base
&Message
) = 0;
37 const char *debugType(const char *Filename
);
38 void logImpl(Logger::Level
, const char *Fmt
, const llvm::formatv_object_base
&);
40 // We often want to consume llvm::Errors by value when passing them to log().
41 // We automatically wrap them in llvm::fmt_consume() as formatv requires.
42 template <typename T
> T
&&wrap(T
&&V
) { return std::forward
<T
>(V
); }
43 inline decltype(fmt_consume(llvm::Error::success())) wrap(llvm::Error
&&V
) {
44 return fmt_consume(std::move(V
));
46 template <typename
... Ts
>
47 void log(Logger::Level L
, const char *Fmt
, Ts
&&... Vals
) {
48 detail::logImpl(L
, Fmt
,
49 llvm::formatv(Fmt
, detail::wrap(std::forward
<Ts
>(Vals
))...));
52 llvm::Error
error(std::error_code
, std::string
&&);
55 // Clangd logging functions write to a global logger set by LoggingSession.
56 // If no logger is registered, writes to llvm::errs().
57 // All accept llvm::formatv()-style arguments, e.g. log("Text={0}", Text).
59 // elog() is used for "loud" errors and warnings.
60 // This level is often visible to users.
61 template <typename
... Ts
> void elog(const char *Fmt
, Ts
&&... Vals
) {
62 detail::log(Logger::Error
, Fmt
, std::forward
<Ts
>(Vals
)...);
64 // log() is used for information important to understand a clangd session.
65 // e.g. the names of LSP messages sent are logged at this level.
66 // This level could be enabled in production builds to allow later inspection.
67 template <typename
... Ts
> void log(const char *Fmt
, Ts
&&... Vals
) {
68 detail::log(Logger::Info
, Fmt
, std::forward
<Ts
>(Vals
)...);
70 // vlog() is used for details often needed for debugging clangd sessions.
71 // This level would typically be enabled for clangd developers.
72 template <typename
... Ts
> void vlog(const char *Fmt
, Ts
&&... Vals
) {
73 detail::log(Logger::Verbose
, Fmt
, std::forward
<Ts
>(Vals
)...);
75 // error() constructs an llvm::Error object, using formatv()-style arguments.
76 // It is not automatically logged! (This function is a little out of place).
77 // The error simply embeds the message string.
78 template <typename
... Ts
>
79 llvm::Error
error(std::error_code EC
, const char *Fmt
, Ts
&&... Vals
) {
80 // We must render the formatv_object eagerly, while references are valid.
82 EC
, llvm::formatv(Fmt
, detail::wrap(std::forward
<Ts
>(Vals
))...).str());
84 // Overload with no error_code conversion, the error will be inconvertible.
85 template <typename
... Ts
> llvm::Error
error(const char *Fmt
, Ts
&&... Vals
) {
87 llvm::inconvertibleErrorCode(),
88 llvm::formatv(Fmt
, detail::wrap(std::forward
<Ts
>(Vals
))...).str());
90 // Overload to avoid formatv complexity for simple strings.
91 inline llvm::Error
error(std::error_code EC
, std::string Msg
) {
92 return detail::error(EC
, std::move(Msg
));
94 // Overload for simple strings with no error_code conversion.
95 inline llvm::Error
error(std::string Msg
) {
96 return detail::error(llvm::inconvertibleErrorCode(), std::move(Msg
));
99 // dlog only logs if --debug was passed, or --debug_only=Basename.
100 // This level would be enabled in a targeted way when debugging.
102 DEBUG_WITH_TYPE(::clang::clangd::detail::debugType(__FILE__), \
103 ::clang::clangd::detail::log(Logger::Debug, __VA_ARGS__))
105 /// Only one LoggingSession can be active at a time.
106 class LoggingSession
{
108 LoggingSession(clangd::Logger
&Instance
);
111 LoggingSession(LoggingSession
&&) = delete;
112 LoggingSession
&operator=(LoggingSession
&&) = delete;
114 LoggingSession(LoggingSession
const &) = delete;
115 LoggingSession
&operator=(LoggingSession
const &) = delete;
118 // Logs to an output stream, such as stderr.
119 class StreamLogger
: public Logger
{
121 StreamLogger(llvm::raw_ostream
&Logs
, Logger::Level MinLevel
)
122 : MinLevel(MinLevel
), Logs(Logs
) {}
124 /// Write a line to the logging stream.
125 void log(Level
, const char *Fmt
,
126 const llvm::formatv_object_base
&Message
) override
;
129 Logger::Level MinLevel
;
130 llvm::raw_ostream
&Logs
;
132 std::mutex StreamMutex
;
135 } // namespace clangd