1/*
2 * Copyright 2015-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#pragma once
17
18#include <chrono>
19#include <string>
20#include <type_traits>
21
22#include <folly/Conv.h>
23#include <folly/Format.h>
24#include <folly/Optional.h>
25#include <folly/String.h>
26#include <glog/logging.h>
27
28namespace folly {
29
30// Default logger
31enum class GoogleLoggerStyle { SECONDS, PRETTY };
32template <GoogleLoggerStyle>
33struct GoogleLogger;
34
35/**
36 * Automatically times a block of code, printing a specified log message on
37 * destruction or whenever the log() method is called. For example:
38 *
39 * AutoTimer t("Foo() completed");
40 * doWork();
41 * t.log("Do work finished");
42 * doMoreWork();
43 *
44 * This would print something like:
45 * "Do work finished in 1.2 seconds"
46 * "Foo() completed in 4.3 seconds"
47 *
48 * You can customize what you use as the logger and clock. The logger needs
49 * to have an operator()(StringPiece, std::chrono::duration<double>) that
50 * gets a message and a duration. The clock needs to model Clock from
51 * std::chrono.
52 *
53 * The default logger logs usings glog. It only logs if the message is
54 * non-empty, so you can also just use this class for timing, e.g.:
55 *
56 * AutoTimer t;
57 * doWork()
58 * const auto how_long = t.log();
59 */
60template <
61 class Logger = GoogleLogger<GoogleLoggerStyle::PRETTY>,
62 class Clock = std::chrono::high_resolution_clock>
63class AutoTimer final {
64 public:
65 using DoubleSeconds = std::chrono::duration<double>;
66
67 explicit AutoTimer(
68 std::string&& msg = "",
69 const DoubleSeconds& minTimetoLog = DoubleSeconds::zero(),
70 Logger&& logger = Logger())
71 : destructionMessage_(std::move(msg)),
72 minTimeToLog_(minTimetoLog),
73 logger_(std::move(logger)) {}
74
75 // It doesn't really make sense to copy AutoTimer
76 // Movable to make sure the helper method for creating an AutoTimer works.
77 AutoTimer(const AutoTimer&) = delete;
78 AutoTimer(AutoTimer&&) = default;
79 AutoTimer& operator=(const AutoTimer&) = delete;
80 AutoTimer& operator=(AutoTimer&&) = default;
81
82 ~AutoTimer() {
83 if (destructionMessage_) {
84 log(destructionMessage_.value());
85 }
86 }
87
88 DoubleSeconds log(StringPiece msg = "") {
89 return logImpl(Clock::now(), msg);
90 }
91
92 template <typename... Args>
93 DoubleSeconds log(Args&&... args) {
94 auto now = Clock::now();
95 return logImpl(now, to<std::string>(std::forward<Args>(args)...));
96 }
97
98 template <typename... Args>
99 DoubleSeconds logFormat(Args&&... args) {
100 auto now = Clock::now();
101 return logImpl(now, format(std::forward<Args>(args)...).str());
102 }
103
104 private:
105 // We take in the current time so that we don't measure time to call
106 // to<std::string> or format() in the duration.
107 DoubleSeconds logImpl(std::chrono::time_point<Clock> now, StringPiece msg) {
108 auto duration = now - start_;
109 if (duration >= minTimeToLog_) {
110 logger_(msg, duration);
111 }
112 start_ = Clock::now(); // Don't measure logging time
113 return duration;
114 }
115
116 Optional<std::string> destructionMessage_;
117 std::chrono::time_point<Clock> start_ = Clock::now();
118 DoubleSeconds minTimeToLog_;
119 Logger logger_;
120};
121
122template <
123 class Logger = GoogleLogger<GoogleLoggerStyle::PRETTY>,
124 class Clock = std::chrono::high_resolution_clock>
125auto makeAutoTimer(
126 std::string&& msg = "",
127 const std::chrono::duration<double>& minTimeToLog =
128 std::chrono::duration<double>::zero(),
129 Logger&& logger = Logger()) {
130 return AutoTimer<Logger, Clock>(
131 std::move(msg), minTimeToLog, std::move(logger));
132}
133
134template <GoogleLoggerStyle Style>
135struct GoogleLogger final {
136 void operator()(StringPiece msg, const std::chrono::duration<double>& sec)
137 const {
138 if (msg.empty()) {
139 return;
140 }
141 if (Style == GoogleLoggerStyle::PRETTY) {
142 LOG(INFO) << msg << " in "
143 << prettyPrint(sec.count(), PrettyType::PRETTY_TIME);
144 } else {
145 LOG(INFO) << msg << " in " << sec.count() << " seconds";
146 }
147 }
148};
149} // namespace folly
150