From 19fcec84d8d7d21e796c7624e521b60d28ee21ed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:45:59 +0200 Subject: Adding upstream version 16.2.11+ds. Signed-off-by: Daniel Baumann --- src/log/LogClock.h | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 src/log/LogClock.h (limited to 'src/log/LogClock.h') diff --git a/src/log/LogClock.h b/src/log/LogClock.h new file mode 100644 index 000000000..07531c8e5 --- /dev/null +++ b/src/log/LogClock.h @@ -0,0 +1,168 @@ +// -*- 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 +#include +#include +#include + +#include "include/ceph_assert.h" +#include "common/ceph_time.h" + +#ifndef suseconds_t +typedef long suseconds_t; +#endif + +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; + // The second template parameter defaults to the clock's duration + // type. + using time_point = std::chrono::time_point; + 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); + #ifndef _WIN32 + return { static_cast(std::chrono::duration_cast(ts).count()), + static_cast(std::chrono::duration_cast( + ts % std::chrono::seconds(1)).count()) }; + #else + return { static_cast(std::chrono::duration_cast(ts).count()), + static_cast(std::chrono::duration_cast( + ts % std::chrono::seconds(1)).count()) }; + #endif + } +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; + time_t t_sec = tv.tv_sec; + localtime_r(&t_sec, &bdt); + char tz[32] = { 0 }; + strftime(tz, sizeof(tz), "%z", &bdt); + + int r; + if (coarse) { + r = std::snprintf(out, outlen, "%04d-%02d-%02dT%02d:%02d:%02d.%03ld%s", + bdt.tm_year + 1900, bdt.tm_mon + 1, bdt.tm_mday, + bdt.tm_hour, bdt.tm_min, bdt.tm_sec, + static_cast(tv.tv_usec / 1000), tz); + } else { + r = std::snprintf(out, outlen, "%04d-%02d-%02dT%02d:%02d:%02d.%06ld%s", + bdt.tm_year + 1900, bdt.tm_mon + 1, bdt.tm_mday, + bdt.tm_hour, bdt.tm_min, bdt.tm_sec, + static_cast(tv.tv_usec), tz); + } + // Since our caller just adds the return value to something without + // checking it… + ceph_assert(r >= 0); + return r; +} +} +} + +#endif -- cgit v1.2.3