]> git.proxmox.com Git - ceph.git/blob - ceph/src/log/LogClock.h
import 15.2.0 Octopus source
[ceph.git] / ceph / src / log / LogClock.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #ifndef CEPH_LOG_CLOCK_H
5 #define CEPH_LOG_CLOCK_H
6
7 #include <cstdio>
8 #include <chrono>
9 #include <ctime>
10 #include <sys/time.h>
11
12 #include "include/ceph_assert.h"
13 #include "common/ceph_time.h"
14
15 namespace ceph {
16 namespace logging {
17 namespace _logclock {
18 // Because the underlying representations of a duration can be any
19 // arithmetic type we wish, slipping a coarseness tag there is the
20 // least hacky way to tag them. I'd also considered doing bit-stealing
21 // and just setting the low bit of the representation unconditionally
22 // to mark it as fine, BUT that would cut our nanosecond precision in
23 // half which sort of obviates the point of 'fine'…admittedly real
24 // computers probably don't care. More to the point it wouldn't be
25 // durable under arithmetic unless we wrote a whole class to support
26 // it /anyway/, and if I'm going to do that I may as well add a bool.
27
28 // (Yes I know we don't do arithmetic on log timestamps, but I don't
29 // want everything to suddenly break because someone did something
30 // that the std::chrono::timepoint contract actually supports.)
31 struct taggedrep {
32 uint64_t count;
33 bool coarse;
34
35 explicit taggedrep(uint64_t count) : count(count), coarse(true) {}
36 taggedrep(uint64_t count, bool coarse) : count(count), coarse(coarse) {}
37
38 explicit operator uint64_t() {
39 return count;
40 }
41 };
42
43 // Proper significant figure support would be a bit excessive. Also
44 // we'd have to know the precision of the clocks on Linux and FreeBSD
45 // and whatever else we want to support.
46 inline taggedrep operator +(const taggedrep& l, const taggedrep& r) {
47 return { l.count + r.count, l.coarse || r.coarse };
48 }
49 inline taggedrep operator -(const taggedrep& l, const taggedrep& r) {
50 return { l.count - r.count, l.coarse || r.coarse };
51 }
52 inline taggedrep operator *(const taggedrep& l, const taggedrep& r) {
53 return { l.count * r.count, l.coarse || r.coarse };
54 }
55 inline taggedrep operator /(const taggedrep& l, const taggedrep& r) {
56 return { l.count / r.count, l.coarse || r.coarse };
57 }
58 inline taggedrep operator %(const taggedrep& l, const taggedrep& r) {
59 return { l.count % r.count, l.coarse || r.coarse };
60 }
61
62 // You can compare coarse and fine time. You shouldn't do so in any
63 // case where ordering actually MATTERS but in practice people won't
64 // actually ping-pong their logs back and forth between them.
65 inline bool operator ==(const taggedrep& l, const taggedrep& r) {
66 return l.count == r.count;
67 }
68 inline bool operator !=(const taggedrep& l, const taggedrep& r) {
69 return l.count != r.count;
70 }
71 inline bool operator <(const taggedrep& l, const taggedrep& r) {
72 return l.count < r.count;
73 }
74 inline bool operator <=(const taggedrep& l, const taggedrep& r) {
75 return l.count <= r.count;
76 }
77 inline bool operator >=(const taggedrep& l, const taggedrep& r) {
78 return l.count >= r.count;
79 }
80 inline bool operator >(const taggedrep& l, const taggedrep& r) {
81 return l.count > r.count;
82 }
83 }
84 class log_clock {
85 public:
86 using rep = _logclock::taggedrep;
87 using period = std::nano;
88 using duration = std::chrono::duration<rep, period>;
89 // The second template parameter defaults to the clock's duration
90 // type.
91 using time_point = std::chrono::time_point<log_clock>;
92 static constexpr const bool is_steady = false;
93
94 time_point now() noexcept {
95 return appropriate_now();
96 }
97
98 void coarsen() {
99 appropriate_now = coarse_now;
100 }
101
102 void refine() {
103 appropriate_now = fine_now;
104 }
105
106 // Since our formatting is done in microseconds and we're using it
107 // anyway, we may as well keep this one
108 static timeval to_timeval(time_point t) {
109 auto rep = t.time_since_epoch().count();
110 timespan ts(rep.count);
111 return { static_cast<time_t>(std::chrono::duration_cast<std::chrono::seconds>(ts).count()),
112 static_cast<suseconds_t>(std::chrono::duration_cast<std::chrono::microseconds>(
113 ts % std::chrono::seconds(1)).count()) };
114 }
115 private:
116 static time_point coarse_now() {
117 return time_point(
118 duration(_logclock::taggedrep(coarse_real_clock::now()
119 .time_since_epoch().count(), true)));
120 }
121 static time_point fine_now() {
122 return time_point(
123 duration(_logclock::taggedrep(real_clock::now()
124 .time_since_epoch().count(), false)));
125 }
126 time_point(*appropriate_now)() = coarse_now;
127 };
128 using log_time = log_clock::time_point;
129 inline int append_time(const log_time& t, char *out, int outlen) {
130 bool coarse = t.time_since_epoch().count().coarse;
131 auto tv = log_clock::to_timeval(t);
132 std::tm bdt;
133 localtime_r(&tv.tv_sec, &bdt);
134 char tz[32] = { 0 };
135 strftime(tz, sizeof(tz), "%z", &bdt);
136
137 int r;
138 if (coarse) {
139 r = std::snprintf(out, outlen, "%04d-%02d-%02dT%02d:%02d:%02d.%03ld%s",
140 bdt.tm_year + 1900, bdt.tm_mon + 1, bdt.tm_mday,
141 bdt.tm_hour, bdt.tm_min, bdt.tm_sec,
142 static_cast<long>(tv.tv_usec / 1000), tz);
143 } else {
144 r = std::snprintf(out, outlen, "%04d-%02d-%02dT%02d:%02d:%02d.%06ld%s",
145 bdt.tm_year + 1900, bdt.tm_mon + 1, bdt.tm_mday,
146 bdt.tm_hour, bdt.tm_min, bdt.tm_sec,
147 static_cast<long>(tv.tv_usec), tz);
148 }
149 // Since our caller just adds the return value to something without
150 // checking it…
151 ceph_assert(r >= 0);
152 return r;
153 }
154 }
155 }
156
157 #endif