1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "google/cloud/log.h"
16 #include "google/cloud/internal/getenv.h"
17 #include "absl/time/time.h"
18 #include <array>
19 
20 namespace google {
21 namespace cloud {
22 inline namespace GOOGLE_CLOUD_CPP_NS {
23 
24 static_assert(sizeof(Severity) <= sizeof(int),
25               "Expected Severity to fit in an integer");
26 
27 static_assert(static_cast<int>(Severity::GCP_LS_LOWEST) <
28                   static_cast<int>(Severity::GCP_LS_HIGHEST),
29               "Expect LOWEST severity to be smaller than HIGHEST severity");
30 
31 namespace {
32 struct Timestamp {
Timestampgoogle::cloud::GOOGLE_CLOUD_CPP_NS::__anon3b3e4ebc0111::Timestamp33   explicit Timestamp(std::chrono::system_clock::time_point const& tp)
34       : t(absl::FromChrono(tp)) {}
35   absl::Time t;
36 };
37 
operator <<(std::ostream & os,Timestamp const & ts)38 std::ostream& operator<<(std::ostream& os, Timestamp const& ts) {
39   auto constexpr kFormat = "%E4Y-%m-%dT%H:%M:%E9SZ";
40   return os << absl::FormatTime(kFormat, ts.t, absl::UTCTimeZone());
41 }
42 
43 auto constexpr kSeverityCount = static_cast<int>(Severity::GCP_LS_HIGHEST) + 1;
44 
45 // Double braces needed to workaround a clang-3.8 bug.
46 std::array<char const*, kSeverityCount> constexpr kSeverityNames{{
47     "TRACE",
48     "DEBUG",
49     "INFO",
50     "NOTICE",
51     "WARNING",
52     "ERROR",
53     "CRITICAL",
54     "ALERT",
55     "FATAL",
56 }};
57 }  // namespace
58 
operator <<(std::ostream & os,Severity x)59 std::ostream& operator<<(std::ostream& os, Severity x) {
60   auto index = static_cast<int>(x);
61   return os << kSeverityNames[index];
62 }
63 
operator <<(std::ostream & os,LogRecord const & rhs)64 std::ostream& operator<<(std::ostream& os, LogRecord const& rhs) {
65   return os << Timestamp{rhs.timestamp} << " [" << rhs.severity << "] "
66             << rhs.message << " (" << rhs.filename << ':' << rhs.lineno << ')';
67 }
68 
LogSink()69 LogSink::LogSink()
70     : empty_(true),
71       minimum_severity_(static_cast<int>(Severity::GCP_LS_LOWEST_ENABLED)),
72       next_id_(0),
73       clog_backend_id_(0) {}
74 
Instance()75 LogSink& LogSink::Instance() {
76   static auto* const kInstance = [] {
77     auto* p = new LogSink;
78     if (internal::GetEnv("GOOGLE_CLOUD_CPP_ENABLE_CLOG").has_value()) {
79       p->EnableStdClogImpl();
80     }
81     return p;
82   }();
83   return *kInstance;
84 }
85 
86 // NOLINTNEXTLINE(google-runtime-int)
AddBackend(std::shared_ptr<LogBackend> backend)87 long LogSink::AddBackend(std::shared_ptr<LogBackend> backend) {
88   std::unique_lock<std::mutex> lk(mu_);
89   return AddBackendImpl(std::move(backend));
90 }
91 
92 // NOLINTNEXTLINE(google-runtime-int)
RemoveBackend(long id)93 void LogSink::RemoveBackend(long id) {
94   std::unique_lock<std::mutex> lk(mu_);
95   RemoveBackendImpl(id);
96 }
97 
ClearBackends()98 void LogSink::ClearBackends() {
99   std::unique_lock<std::mutex> lk(mu_);
100   backends_.clear();
101   clog_backend_id_ = 0;
102   empty_.store(backends_.empty());
103 }
104 
BackendCount() const105 std::size_t LogSink::BackendCount() const {
106   std::unique_lock<std::mutex> lk(mu_);
107   return backends_.size();
108 }
109 
Log(LogRecord log_record)110 void LogSink::Log(LogRecord log_record) {
111   // Make a copy of the backends because calling user-defined functions while
112   // holding a lock is a bad idea: the application may change the backends while
113   // we are holding this lock, and soon deadlock occurs.
114   auto copy = [this]() {
115     std::unique_lock<std::mutex> lk(mu_);
116     return backends_;
117   }();
118   if (copy.empty()) {
119     return;
120   }
121   // In general, we just give each backend a const-reference and the backends
122   // must make a copy if needed.  But if there is only one backend we can give
123   // the backend an opportunity to optimize things by transferring ownership of
124   // the LogRecord to it.
125   if (copy.size() == 1) {
126     copy.begin()->second->ProcessWithOwnership(std::move(log_record));
127     return;
128   }
129   for (auto& kv : copy) {
130     kv.second->Process(log_record);
131   }
132 }
133 
134 namespace {
135 class StdClogBackend : public LogBackend {
136  public:
137   StdClogBackend() = default;
138 
Process(LogRecord const & lr)139   void Process(LogRecord const& lr) override {
140     std::clog << lr << "\n";
141     if (lr.severity >= Severity::GCP_LS_WARNING) {
142       std::clog << std::flush;
143     }
144   }
ProcessWithOwnership(LogRecord lr)145   void ProcessWithOwnership(LogRecord lr) override { Process(lr); }
146 };
147 }  // namespace
148 
EnableStdClogImpl()149 void LogSink::EnableStdClogImpl() {
150   std::unique_lock<std::mutex> lk(mu_);
151   if (clog_backend_id_ != 0) {
152     return;
153   }
154   clog_backend_id_ = AddBackendImpl(std::make_shared<StdClogBackend>());
155 }
156 
DisableStdClogImpl()157 void LogSink::DisableStdClogImpl() {
158   std::unique_lock<std::mutex> lk(mu_);
159   if (clog_backend_id_ == 0) {
160     return;
161   }
162   RemoveBackendImpl(clog_backend_id_);
163   clog_backend_id_ = 0;
164 }
165 
166 // NOLINTNEXTLINE(google-runtime-int)
AddBackendImpl(std::shared_ptr<LogBackend> backend)167 long LogSink::AddBackendImpl(std::shared_ptr<LogBackend> backend) {
168   auto const id = ++next_id_;
169   backends_.emplace(id, std::move(backend));
170   empty_.store(backends_.empty());
171   return id;
172 }
173 
174 // NOLINTNEXTLINE(google-runtime-int)
RemoveBackendImpl(long id)175 void LogSink::RemoveBackendImpl(long id) {
176   auto it = backends_.find(id);
177   if (backends_.end() == it) {
178     return;
179   }
180   backends_.erase(it);
181   empty_.store(backends_.empty());
182 }
183 
184 }  // namespace GOOGLE_CLOUD_CPP_NS
185 }  // namespace cloud
186 }  // namespace google
187