Skip to content
This repository was archived by the owner on Dec 8, 2021. It is now read-only.

Commit d785659

Browse files
authored
feat: add timestamps in default log backend (#266)
Timestamps are always rendered in "Zulu" time, and with nanosecond resolution (no matter what the actual resolution of a platform's `system_clock::time_point`). Fixes #261.
1 parent 13b7a15 commit d785659

File tree

3 files changed

+74
-19
lines changed

3 files changed

+74
-19
lines changed

google/cloud/log.cc

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,39 +15,77 @@
1515
#include "google/cloud/log.h"
1616
#include "google/cloud/internal/getenv.h"
1717
#include <array>
18+
#include <cstdint>
19+
#include <ctime>
20+
#include <iomanip>
1821

1922
namespace google {
2023
namespace cloud {
2124
inline namespace GOOGLE_CLOUD_CPP_NS {
25+
2226
static_assert(sizeof(Severity) <= sizeof(int),
2327
"Expected Severity to fit in an integer");
2428

2529
static_assert(static_cast<int>(Severity::GCP_LS_LOWEST) <
2630
static_cast<int>(Severity::GCP_LS_HIGHEST),
2731
"Expect LOWEST severity to be smaller than HIGHEST severity");
2832

33+
namespace {
34+
struct Timestamp {
35+
explicit Timestamp(std::chrono::system_clock::time_point const& tp) {
36+
auto const tt = std::chrono::system_clock::to_time_t(tp);
37+
#if defined(_WIN32)
38+
gmtime_s(&tm, &tt);
39+
#else
40+
gmtime_r(&tt, &tm);
41+
#endif
42+
auto const ss = tp - std::chrono::system_clock::from_time_t(tt);
43+
nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(ss).count();
44+
}
45+
std::tm tm;
46+
std::int32_t nanos;
47+
};
48+
49+
std::ostream& operator<<(std::ostream& os, Timestamp const& ts) {
50+
auto const prev = os.fill(' ');
51+
auto constexpr kTmYearOffset = 1900;
52+
os << std::setw(4) << ts.tm.tm_year + kTmYearOffset;
53+
os.fill('0');
54+
os << '-' << std::setw(2) << ts.tm.tm_mon + 1;
55+
os << '-' << std::setw(2) << ts.tm.tm_mday;
56+
os << 'T' << std::setw(2) << ts.tm.tm_hour;
57+
os << ':' << std::setw(2) << ts.tm.tm_min;
58+
os << ':' << std::setw(2) << ts.tm.tm_sec;
59+
// NOLINTNEXTLINE(readability-magic-numbers)
60+
os << '.' << std::setw(9) << ts.nanos << 'Z';
61+
os.fill(prev);
62+
return os;
63+
}
64+
65+
auto constexpr kSeverityCount = static_cast<int>(Severity::GCP_LS_HIGHEST) + 1;
66+
67+
// Double braces needed to workaround a clang-3.8 bug.
68+
std::array<char const*, kSeverityCount> constexpr kSeverityNames{{
69+
"TRACE",
70+
"DEBUG",
71+
"INFO",
72+
"NOTICE",
73+
"WARNING",
74+
"ERROR",
75+
"CRITICAL",
76+
"ALERT",
77+
"FATAL",
78+
}};
79+
} // namespace
80+
2981
std::ostream& operator<<(std::ostream& os, Severity x) {
30-
auto constexpr kSeverityCount =
31-
static_cast<int>(Severity::GCP_LS_HIGHEST) + 1;
32-
// Double braces needed to workaround a clang-3.8 bug.
33-
std::array<char const*, kSeverityCount> names{{
34-
"TRACE",
35-
"DEBUG",
36-
"INFO",
37-
"NOTICE",
38-
"WARNING",
39-
"ERROR",
40-
"CRITICAL",
41-
"ALERT",
42-
"FATAL",
43-
}};
4482
auto index = static_cast<int>(x);
45-
return os << names[index];
83+
return os << kSeverityNames[index];
4684
}
4785

4886
std::ostream& operator<<(std::ostream& os, LogRecord const& rhs) {
49-
return os << '[' << rhs.severity << "] " << rhs.message << " ("
50-
<< rhs.filename << ':' << rhs.lineno << ')';
87+
return os << Timestamp{rhs.timestamp} << " [" << rhs.severity << "] "
88+
<< rhs.message << " (" << rhs.filename << ':' << rhs.lineno << ')';
5189
}
5290

5391
LogSink::LogSink()

google/cloud/log.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_LOG_H
1616
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_LOG_H
17+
1718
/**
1819
* @file log.h
1920
*
@@ -222,7 +223,7 @@ struct LogRecord {
222223
std::ostream& operator<<(std::ostream& os, LogRecord const& rhs);
223224

224225
/**
225-
* A sink to receive log records.
226+
* The logging backend interface.
226227
*/
227228
class LogBackend {
228229
public:
@@ -233,7 +234,7 @@ class LogBackend {
233234
};
234235

235236
/**
236-
*
237+
* A sink to receive log records.
237238
*/
238239
class LogSink {
239240
public:

google/cloud/log_test.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "google/cloud/log.h"
1616
#include "google/cloud/testing_util/scoped_environment.h"
1717
#include <gmock/gmock.h>
18+
#include <chrono>
1819

1920
namespace google {
2021
namespace cloud {
@@ -32,6 +33,21 @@ TEST(LogSeverityTest, Streaming) {
3233
EXPECT_EQ("TRACE", os.str());
3334
}
3435

36+
TEST(LogRecordTest, Streaming) {
37+
std::ostringstream os;
38+
LogRecord lr;
39+
lr.severity = Severity::GCP_LS_INFO;
40+
lr.function = "Func";
41+
lr.filename = "filename.cc";
42+
lr.lineno = 123;
43+
lr.timestamp = std::chrono::system_clock::from_time_t(1585112316) +
44+
std::chrono::microseconds(123456);
45+
lr.message = "message";
46+
os << lr;
47+
EXPECT_EQ("2020-03-25T04:58:36.123456000Z [INFO] message (filename.cc:123)",
48+
os.str());
49+
}
50+
3551
TEST(LogSinkTest, CompileTimeEnabled) {
3652
EXPECT_TRUE(LogSink::CompileTimeEnabled(Severity::GCP_LS_CRITICAL));
3753
if (Severity::GCP_LS_LOWEST_ENABLED >= Severity::GCP_LS_TRACE) {

0 commit comments

Comments
 (0)