/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include namespace folly { // Default logger enum class GoogleLoggerStyle { SECONDS, PRETTY }; template struct GoogleLogger; /** * Automatically times a block of code, printing a specified log message on * destruction or whenever the log() method is called. For example: * * AutoTimer t("Foo() completed"); * doWork(); * t.log("Do work finished"); * doMoreWork(); * * This would print something like: * "Do work finished in 1.2 seconds" * "Foo() completed in 4.3 seconds" * * You can customize what you use as the logger and clock. The logger needs * to have an operator()(StringPiece, std::chrono::duration) that * gets a message and a duration. The clock needs to model Clock from * std::chrono. * * The default logger logs usings glog. It only logs if the message is * non-empty, so you can also just use this class for timing, e.g.: * * AutoTimer t; * doWork() * const auto how_long = t.log(); */ template < class Logger = GoogleLogger, class Clock = std::chrono::high_resolution_clock> class AutoTimer final { public: using DoubleSeconds = std::chrono::duration; explicit AutoTimer( std::string&& msg = "", const DoubleSeconds& minTimetoLog = DoubleSeconds::zero(), Logger&& logger = Logger()) : destructionMessage_(std::move(msg)), minTimeToLog_(minTimetoLog), logger_(std::move(logger)) {} // It doesn't really make sense to copy AutoTimer // Movable to make sure the helper method for creating an AutoTimer works. AutoTimer(const AutoTimer&) = delete; AutoTimer(AutoTimer&&) = default; AutoTimer& operator=(const AutoTimer&) = delete; AutoTimer& operator=(AutoTimer&&) = default; ~AutoTimer() { if (destructionMessage_) { log(destructionMessage_.value()); } } DoubleSeconds log(StringPiece msg = "") { return logImpl(Clock::now(), msg); } template DoubleSeconds log(Args&&... args) { auto now = Clock::now(); return logImpl(now, to(std::forward(args)...)); } template DoubleSeconds logFormat(Args&&... args) { auto now = Clock::now(); return logImpl(now, format(std::forward(args)...).str()); } private: // We take in the current time so that we don't measure time to call // to or format() in the duration. DoubleSeconds logImpl(std::chrono::time_point now, StringPiece msg) { auto duration = now - start_; if (duration >= minTimeToLog_) { logger_(msg, duration); } start_ = Clock::now(); // Don't measure logging time return duration; } Optional destructionMessage_; std::chrono::time_point start_ = Clock::now(); DoubleSeconds minTimeToLog_; Logger logger_; }; template < class Logger = GoogleLogger, class Clock = std::chrono::high_resolution_clock> auto makeAutoTimer( std::string&& msg = "", const std::chrono::duration& minTimeToLog = std::chrono::duration::zero(), Logger&& logger = Logger()) { return AutoTimer( std::move(msg), minTimeToLog, std::move(logger)); } template struct GoogleLogger final { void operator()(StringPiece msg, const std::chrono::duration& sec) const { if (msg.empty()) { return; } if (Style == GoogleLoggerStyle::PRETTY) { LOG(INFO) << msg << " in " << prettyPrint(sec.count(), PrettyType::PRETTY_TIME); } else { LOG(INFO) << msg << " in " << sec.count() << " seconds"; } } }; } // namespace folly