summaryrefslogtreecommitdiffstats
path: root/src/log/LogClock.h
blob: 07531c8e51a9f8223b60045314d8f73e0fdcf596 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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 <cstdio>
#include <chrono>
#include <ctime>
#include <sys/time.h>

#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<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);
    #ifndef _WIN32
    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()) };
    #else
    return { static_cast<long>(std::chrono::duration_cast<std::chrono::seconds>(ts).count()),
             static_cast<long>(std::chrono::duration_cast<std::chrono::microseconds>(
               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<long>(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<long>(tv.tv_usec), tz);
  }
  // Since our caller just adds the return value to something without
  // checking it…
  ceph_assert(r >= 0);
  return r;
}
}
}

#endif