diff options
Diffstat (limited to 'src/log/LogClock.h')
-rw-r--r-- | src/log/LogClock.h | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/src/log/LogClock.h b/src/log/LogClock.h new file mode 100644 index 00000000..5cd4e77d --- /dev/null +++ b/src/log/LogClock.h @@ -0,0 +1,155 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LOG_CLOCK_H +#define CEPH_LOG_CLOCK_H + +#include <cstdio> +#include <chrono> +#include <ctime> +#include <sys/time.h> + +#include "include/ceph_assert.h" +#include "common/ceph_time.h" + +namespace ceph { +namespace logging { +namespace _logclock { +// Because the underlying representations of a duration can be any +// arithmetic type we wish, slipping a coarseness tag there is the +// least hacky way to tag them. I'd also considered doing bit-stealing +// and just setting the low bit of the representation unconditionally +// to mark it as fine, BUT that would cut our nanosecond precision in +// half which sort of obviates the point of 'fine'…admittedly real +// computers probably don't care. More to the point it wouldn't be +// durable under arithmetic unless we wrote a whole class to support +// it /anyway/, and if I'm going to do that I may as well add a bool. + +// (Yes I know we don't do arithmetic on log timestamps, but I don't +// want everything to suddenly break because someone did something +// that the std::chrono::timepoint contract actually supports.) +struct taggedrep { + uint64_t count; + bool coarse; + + explicit taggedrep(uint64_t count) : count(count), coarse(true) {} + taggedrep(uint64_t count, bool coarse) : count(count), coarse(coarse) {} + + explicit operator uint64_t() { + return count; + } +}; + +// Proper significant figure support would be a bit excessive. Also +// we'd have to know the precision of the clocks on Linux and FreeBSD +// and whatever else we want to support. +inline taggedrep operator +(const taggedrep& l, const taggedrep& r) { + return { l.count + r.count, l.coarse || r.coarse }; +} +inline taggedrep operator -(const taggedrep& l, const taggedrep& r) { + return { l.count - r.count, l.coarse || r.coarse }; +} +inline taggedrep operator *(const taggedrep& l, const taggedrep& r) { + return { l.count * r.count, l.coarse || r.coarse }; +} +inline taggedrep operator /(const taggedrep& l, const taggedrep& r) { + return { l.count / r.count, l.coarse || r.coarse }; +} +inline taggedrep operator %(const taggedrep& l, const taggedrep& r) { + return { l.count % r.count, l.coarse || r.coarse }; +} + +// You can compare coarse and fine time. You shouldn't do so in any +// case where ordering actually MATTERS but in practice people won't +// actually ping-pong their logs back and forth between them. +inline bool operator ==(const taggedrep& l, const taggedrep& r) { + return l.count == r.count; +} +inline bool operator !=(const taggedrep& l, const taggedrep& r) { + return l.count != r.count; +} +inline bool operator <(const taggedrep& l, const taggedrep& r) { + return l.count < r.count; +} +inline bool operator <=(const taggedrep& l, const taggedrep& r) { + return l.count <= r.count; +} +inline bool operator >=(const taggedrep& l, const taggedrep& r) { + return l.count >= r.count; +} +inline bool operator >(const taggedrep& l, const taggedrep& r) { + return l.count > r.count; +} +} +class log_clock { +public: + using rep = _logclock::taggedrep; + using period = std::nano; + using duration = std::chrono::duration<rep, period>; + // The second template parameter defaults to the clock's duration + // type. + using time_point = std::chrono::time_point<log_clock>; + static constexpr const bool is_steady = false; + + time_point now() noexcept { + return appropriate_now(); + } + + void coarsen() { + appropriate_now = coarse_now; + } + + void refine() { + appropriate_now = fine_now; + } + + // Since our formatting is done in microseconds and we're using it + // anyway, we may as well keep this one + static timeval to_timeval(time_point t) { + auto rep = t.time_since_epoch().count(); + timespan ts(rep.count); + return { static_cast<time_t>(std::chrono::duration_cast<std::chrono::seconds>(ts).count()), + static_cast<suseconds_t>(std::chrono::duration_cast<std::chrono::microseconds>( + ts % std::chrono::seconds(1)).count()) }; + } +private: + static time_point coarse_now() { + return time_point( + duration(_logclock::taggedrep(coarse_real_clock::now() + .time_since_epoch().count(), true))); + } + static time_point fine_now() { + return time_point( + duration(_logclock::taggedrep(real_clock::now() + .time_since_epoch().count(), false))); + } + time_point(*appropriate_now)() = coarse_now; +}; +using log_time = log_clock::time_point; +inline int append_time(const log_time& t, char *out, int outlen) { + bool coarse = t.time_since_epoch().count().coarse; + auto tv = log_clock::to_timeval(t); + std::tm bdt; + localtime_r(&tv.tv_sec, &bdt); + + int r; + if (coarse) { + r = std::snprintf(out, outlen, "%04d-%02d-%02d %02d:%02d:%02d.%03ld", + bdt.tm_year + 1900, bdt.tm_mon + 1, bdt.tm_mday, + bdt.tm_hour, bdt.tm_min, bdt.tm_sec, + static_cast<long>(tv.tv_usec / 1000)); + } else { + r = std::snprintf(out, outlen, "%04d-%02d-%02d %02d:%02d:%02d.%06ld", + bdt.tm_year + 1900, bdt.tm_mon + 1, bdt.tm_mday, + bdt.tm_hour, bdt.tm_min, bdt.tm_sec, + static_cast<long>(tv.tv_usec)); + } + // Since our caller just adds the return value to something without + // checking it… + ceph_assert(r >= 0); + return r; +} +} +} + +#endif |