1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 
6 #pragma once
7 
8 #include <memory>
9 #include <sstream>
10 #include <string>
11 #include <chrono>
12 
13 #include "logging/log_buffer.h"
14 #include "rocksdb/env.h"
15 
16 namespace ROCKSDB_NAMESPACE {
17 
18 class JSONWriter {
19  public:
JSONWriter()20   JSONWriter() : state_(kExpectKey), first_element_(true), in_array_(false) {
21     stream_ << "{";
22   }
23 
AddKey(const std::string & key)24   void AddKey(const std::string& key) {
25     assert(state_ == kExpectKey);
26     if (!first_element_) {
27       stream_ << ", ";
28     }
29     stream_ << "\"" << key << "\": ";
30     state_ = kExpectValue;
31     first_element_ = false;
32   }
33 
AddValue(const char * value)34   void AddValue(const char* value) {
35     assert(state_ == kExpectValue || state_ == kInArray);
36     if (state_ == kInArray && !first_element_) {
37       stream_ << ", ";
38     }
39     stream_ << "\"" << value << "\"";
40     if (state_ != kInArray) {
41       state_ = kExpectKey;
42     }
43     first_element_ = false;
44   }
45 
46   template <typename T>
AddValue(const T & value)47   void AddValue(const T& value) {
48     assert(state_ == kExpectValue || state_ == kInArray);
49     if (state_ == kInArray && !first_element_) {
50       stream_ << ", ";
51     }
52     stream_ << value;
53     if (state_ != kInArray) {
54       state_ = kExpectKey;
55     }
56     first_element_ = false;
57   }
58 
StartArray()59   void StartArray() {
60     assert(state_ == kExpectValue);
61     state_ = kInArray;
62     in_array_ = true;
63     stream_ << "[";
64     first_element_ = true;
65   }
66 
EndArray()67   void EndArray() {
68     assert(state_ == kInArray);
69     state_ = kExpectKey;
70     in_array_ = false;
71     stream_ << "]";
72     first_element_ = false;
73   }
74 
StartObject()75   void StartObject() {
76     assert(state_ == kExpectValue);
77     state_ = kExpectKey;
78     stream_ << "{";
79     first_element_ = true;
80   }
81 
EndObject()82   void EndObject() {
83     assert(state_ == kExpectKey);
84     stream_ << "}";
85     first_element_ = false;
86   }
87 
StartArrayedObject()88   void StartArrayedObject() {
89     assert(state_ == kInArray && in_array_);
90     state_ = kExpectValue;
91     if (!first_element_) {
92       stream_ << ", ";
93     }
94     StartObject();
95   }
96 
EndArrayedObject()97   void EndArrayedObject() {
98     assert(in_array_);
99     EndObject();
100     state_ = kInArray;
101   }
102 
Get()103   std::string Get() const { return stream_.str(); }
104 
105   JSONWriter& operator<<(const char* val) {
106     if (state_ == kExpectKey) {
107       AddKey(val);
108     } else {
109       AddValue(val);
110     }
111     return *this;
112   }
113 
114   JSONWriter& operator<<(const std::string& val) {
115     return *this << val.c_str();
116   }
117 
118   template <typename T>
119   JSONWriter& operator<<(const T& val) {
120     assert(state_ != kExpectKey);
121     AddValue(val);
122     return *this;
123   }
124 
125  private:
126   enum JSONWriterState {
127     kExpectKey,
128     kExpectValue,
129     kInArray,
130     kInArrayedObject,
131   };
132   JSONWriterState state_;
133   bool first_element_;
134   bool in_array_;
135   std::ostringstream stream_;
136 };
137 
138 class EventLoggerStream {
139  public:
140   template <typename T>
141   EventLoggerStream& operator<<(const T& val) {
142     MakeStream();
143     *json_writer_ << val;
144     return *this;
145   }
146 
StartArray()147   void StartArray() { json_writer_->StartArray(); }
EndArray()148   void EndArray() { json_writer_->EndArray(); }
StartObject()149   void StartObject() { json_writer_->StartObject(); }
EndObject()150   void EndObject() { json_writer_->EndObject(); }
151 
152   ~EventLoggerStream();
153 
154  private:
MakeStream()155   void MakeStream() {
156     if (!json_writer_) {
157       json_writer_ = new JSONWriter();
158       *this << "time_micros"
159             << std::chrono::duration_cast<std::chrono::microseconds>(
160                    std::chrono::system_clock::now().time_since_epoch()).count();
161     }
162   }
163   friend class EventLogger;
164   explicit EventLoggerStream(Logger* logger);
165   explicit EventLoggerStream(LogBuffer* log_buffer, const size_t max_log_size);
166   // exactly one is non-nullptr
167   Logger* const logger_;
168   LogBuffer* const log_buffer_;
169   const size_t max_log_size_;  // used only for log_buffer_
170   // ownership
171   JSONWriter* json_writer_;
172 };
173 
174 // here is an example of the output that will show up in the LOG:
175 // 2015/01/15-14:13:25.788019 1105ef000 EVENT_LOG_v1 {"time_micros":
176 // 1421360005788015, "event": "table_file_creation", "file_number": 12,
177 // "file_size": 1909699}
178 class EventLogger {
179  public:
Prefix()180   static const char* Prefix() {
181     return "EVENT_LOG_v1";
182   }
183 
EventLogger(Logger * logger)184   explicit EventLogger(Logger* logger) : logger_(logger) {}
Log()185   EventLoggerStream Log() { return EventLoggerStream(logger_); }
LogToBuffer(LogBuffer * log_buffer)186   EventLoggerStream LogToBuffer(LogBuffer* log_buffer) {
187     return EventLoggerStream(log_buffer, LogBuffer::kDefaultMaxLogSize);
188   }
LogToBuffer(LogBuffer * log_buffer,const size_t max_log_size)189   EventLoggerStream LogToBuffer(LogBuffer* log_buffer,
190                                 const size_t max_log_size) {
191     return EventLoggerStream(log_buffer, max_log_size);
192   }
193   void Log(const JSONWriter& jwriter);
194   static void Log(Logger* logger, const JSONWriter& jwriter);
195   static void LogToBuffer(
196       LogBuffer* log_buffer, const JSONWriter& jwriter,
197       const size_t max_log_size = LogBuffer::kDefaultMaxLogSize);
198 
199  private:
200   Logger* logger_;
201 };
202 
203 }  // namespace ROCKSDB_NAMESPACE
204