mirror of
https://github.com/ceph/ceph
synced 2025-01-20 10:01:45 +00:00
log: Mark timestamps for coarseness and print appropriately
Have each timestamp know whether it's coarse or fine. Print six figures for fine stamps and three for coarse. Signed-off-by: Adam C. Emerson <aemerson@redhat.com>
This commit is contained in:
parent
0f8606b4e0
commit
afaf48a263
@ -84,25 +84,6 @@ namespace ceph {
|
||||
|
||||
}
|
||||
|
||||
namespace logging {
|
||||
void log_clock::to_ceph_timespec(const time_point& t,
|
||||
struct ceph_timespec& ts) {
|
||||
ts.tv_sec = to_time_t(t);
|
||||
ts.tv_nsec = (t.time_since_epoch() %std::chrono::seconds(1)).count();
|
||||
}
|
||||
struct ceph_timespec log_clock::to_ceph_timespec(
|
||||
const time_point& t) {
|
||||
struct ceph_timespec ts;
|
||||
to_ceph_timespec(t, ts);
|
||||
return ts;
|
||||
}
|
||||
log_clock::time_point log_clock::from_ceph_timespec(
|
||||
const struct ceph_timespec& ts) {
|
||||
return time_point(std::chrono::seconds(ts.tv_sec) +
|
||||
std::chrono::nanoseconds(ts.tv_nsec));
|
||||
}
|
||||
}
|
||||
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::seconds;
|
||||
using std::chrono::microseconds;
|
||||
@ -153,6 +134,4 @@ namespace ceph {
|
||||
operator<< <coarse_mono_clock>(std::ostream& m, const coarse_mono_time& t);
|
||||
template std::ostream&
|
||||
operator<< <coarse_real_clock>(std::ostream& m, const coarse_real_time& t);
|
||||
template std::ostream&
|
||||
operator<< <logging::log_clock>(std::ostream& m, const logging::log_time& t);
|
||||
}
|
||||
|
@ -14,16 +14,87 @@
|
||||
|
||||
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 duration = timespan;
|
||||
using rep = duration::rep;
|
||||
using period = duration::period;
|
||||
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;
|
||||
}
|
||||
@ -32,99 +103,45 @@ public:
|
||||
appropriate_now = fine_now;
|
||||
}
|
||||
|
||||
time_point now() noexcept {
|
||||
return appropriate_now();
|
||||
}
|
||||
|
||||
static bool is_zero(const time_point& t) {
|
||||
return (t == time_point::min());
|
||||
}
|
||||
|
||||
// Allow conversion to/from any clock with the same interface as
|
||||
// std::chrono::system_clock)
|
||||
template<typename Clock, typename Duration>
|
||||
static time_point to_system_time_point(
|
||||
const std::chrono::time_point<Clock, Duration>& t) {
|
||||
return time_point(seconds(Clock::to_time_t(t)) +
|
||||
std::chrono::duration_cast<duration>(
|
||||
t.time_since_epoch() % std::chrono::seconds(1)));
|
||||
}
|
||||
template<typename Clock, typename Duration>
|
||||
static std::chrono::time_point<Clock, Duration> to_system_time_point(
|
||||
const time_point& t) {
|
||||
return (Clock::from_time_t(to_time_t(t)) +
|
||||
std::chrono::duration_cast<Duration>(t.time_since_epoch() %
|
||||
std::chrono::seconds(1)));
|
||||
}
|
||||
|
||||
static time_t to_time_t(const time_point& t) noexcept {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||
t.time_since_epoch()).count();
|
||||
}
|
||||
static time_point from_time_t(const time_t& t) noexcept {
|
||||
return time_point(std::chrono::seconds(t));
|
||||
}
|
||||
|
||||
static void to_timespec(const time_point& t, struct timespec& ts) {
|
||||
ts.tv_sec = to_time_t(t);
|
||||
ts.tv_nsec = (t.time_since_epoch() % std::chrono::seconds(1)).count();
|
||||
}
|
||||
static struct timespec to_timespec(const time_point& t) {
|
||||
struct timespec ts;
|
||||
to_timespec(t, ts);
|
||||
return ts;
|
||||
}
|
||||
static time_point from_timespec(const struct timespec& ts) {
|
||||
return time_point(std::chrono::seconds(ts.tv_sec) +
|
||||
std::chrono::nanoseconds(ts.tv_nsec));
|
||||
}
|
||||
|
||||
static void to_ceph_timespec(const time_point& t,
|
||||
struct ceph_timespec& ts);
|
||||
static struct ceph_timespec to_ceph_timespec(const time_point& t);
|
||||
static time_point from_ceph_timespec(const struct ceph_timespec& ts);
|
||||
|
||||
static void to_timeval(const time_point& t, struct timeval& tv) {
|
||||
tv.tv_sec = to_time_t(t);
|
||||
tv.tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
t.time_since_epoch() % std::chrono::seconds(1)).count();
|
||||
}
|
||||
static struct timeval to_timeval(const time_point& t) {
|
||||
struct timeval tv;
|
||||
to_timeval(t, tv);
|
||||
return tv;
|
||||
}
|
||||
static time_point from_timeval(const struct timeval& tv) {
|
||||
return time_point(std::chrono::seconds(tv.tv_sec) +
|
||||
std::chrono::microseconds(tv.tv_usec));
|
||||
}
|
||||
|
||||
static double to_double(const time_point& t) {
|
||||
return std::chrono::duration<double>(t.time_since_epoch()).count();
|
||||
}
|
||||
static time_point from_double(const double d) {
|
||||
return time_point(std::chrono::duration_cast<duration>(
|
||||
std::chrono::duration<double>(d)));
|
||||
// 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 { std::chrono::duration_cast<std::chrono::seconds>(ts).count(),
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
ts % std::chrono::seconds(1)).count() };
|
||||
}
|
||||
private:
|
||||
static time_point coarse_now() {
|
||||
return time_point(coarse_real_clock::now().time_since_epoch());
|
||||
return time_point(
|
||||
duration(_logclock::taggedrep(coarse_real_clock::now()
|
||||
.time_since_epoch().count(), true)));
|
||||
}
|
||||
static time_point fine_now() {
|
||||
return time_point(real_clock::now().time_since_epoch());
|
||||
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);
|
||||
|
||||
auto 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, tv.tv_usec);
|
||||
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, 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, tv.tv_usec);
|
||||
}
|
||||
// Since our caller just adds the return value to something without
|
||||
// checking it…
|
||||
ceph_assert(r >= 0);
|
||||
|
@ -251,3 +251,26 @@ TEST(Log, TimeSwitch)
|
||||
log.flush();
|
||||
log.stop();
|
||||
}
|
||||
|
||||
TEST(Log, TimeFormat)
|
||||
{
|
||||
static constexpr auto buflen = 128u;
|
||||
char buf[buflen];
|
||||
ceph::logging::log_clock clock;
|
||||
{
|
||||
clock.coarsen();
|
||||
auto t = clock.now();
|
||||
ceph::logging::append_time(t, buf, buflen);
|
||||
auto c = std::strrchr(buf, '.');
|
||||
ASSERT_NE(c, nullptr);
|
||||
ASSERT_EQ(strlen(c + 1), 3);
|
||||
}
|
||||
{
|
||||
clock.refine();
|
||||
auto t = clock.now();
|
||||
ceph::logging::append_time(t, buf, buflen);
|
||||
auto c = std::strrchr(buf, '.');
|
||||
ASSERT_NE(c, nullptr);
|
||||
ASSERT_EQ(std::strlen(c + 1), 6);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user