| // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
| // This source code is licensed under both the GPLv2 (found in the |
| // COPYING file in the root directory) and Apache 2.0 License |
| // (found in the LICENSE.Apache file in the root directory). |
| |
| #pragma once |
| |
| #include <memory> |
| #include <sstream> |
| #include <string> |
| #include <chrono> |
| |
| #include "rocksdb/env.h" |
| #include "util/log_buffer.h" |
| |
| namespace rocksdb { |
| |
| class JSONWriter { |
| public: |
| JSONWriter() : state_(kExpectKey), first_element_(true), in_array_(false) { |
| stream_ << "{"; |
| } |
| |
| void AddKey(const std::string& key) { |
| assert(state_ == kExpectKey); |
| if (!first_element_) { |
| stream_ << ", "; |
| } |
| stream_ << "\"" << key << "\": "; |
| state_ = kExpectValue; |
| first_element_ = false; |
| } |
| |
| void AddValue(const char* value) { |
| assert(state_ == kExpectValue || state_ == kInArray); |
| if (state_ == kInArray && !first_element_) { |
| stream_ << ", "; |
| } |
| stream_ << "\"" << value << "\""; |
| if (state_ != kInArray) { |
| state_ = kExpectKey; |
| } |
| first_element_ = false; |
| } |
| |
| template <typename T> |
| void AddValue(const T& value) { |
| assert(state_ == kExpectValue || state_ == kInArray); |
| if (state_ == kInArray && !first_element_) { |
| stream_ << ", "; |
| } |
| stream_ << value; |
| if (state_ != kInArray) { |
| state_ = kExpectKey; |
| } |
| first_element_ = false; |
| } |
| |
| void StartArray() { |
| assert(state_ == kExpectValue); |
| state_ = kInArray; |
| in_array_ = true; |
| stream_ << "["; |
| first_element_ = true; |
| } |
| |
| void EndArray() { |
| assert(state_ == kInArray); |
| state_ = kExpectKey; |
| in_array_ = false; |
| stream_ << "]"; |
| first_element_ = false; |
| } |
| |
| void StartObject() { |
| assert(state_ == kExpectValue); |
| state_ = kExpectKey; |
| stream_ << "{"; |
| first_element_ = true; |
| } |
| |
| void EndObject() { |
| assert(state_ == kExpectKey); |
| stream_ << "}"; |
| first_element_ = false; |
| } |
| |
| void StartArrayedObject() { |
| assert(state_ == kInArray && in_array_); |
| state_ = kExpectValue; |
| if (!first_element_) { |
| stream_ << ", "; |
| } |
| StartObject(); |
| } |
| |
| void EndArrayedObject() { |
| assert(in_array_); |
| EndObject(); |
| state_ = kInArray; |
| } |
| |
| std::string Get() const { return stream_.str(); } |
| |
| JSONWriter& operator<<(const char* val) { |
| if (state_ == kExpectKey) { |
| AddKey(val); |
| } else { |
| AddValue(val); |
| } |
| return *this; |
| } |
| |
| JSONWriter& operator<<(const std::string& val) { |
| return *this << val.c_str(); |
| } |
| |
| template <typename T> |
| JSONWriter& operator<<(const T& val) { |
| assert(state_ != kExpectKey); |
| AddValue(val); |
| return *this; |
| } |
| |
| private: |
| enum JSONWriterState { |
| kExpectKey, |
| kExpectValue, |
| kInArray, |
| kInArrayedObject, |
| }; |
| JSONWriterState state_; |
| bool first_element_; |
| bool in_array_; |
| std::ostringstream stream_; |
| }; |
| |
| class EventLoggerStream { |
| public: |
| template <typename T> |
| EventLoggerStream& operator<<(const T& val) { |
| MakeStream(); |
| *json_writer_ << val; |
| return *this; |
| } |
| |
| void StartArray() { json_writer_->StartArray(); } |
| void EndArray() { json_writer_->EndArray(); } |
| void StartObject() { json_writer_->StartObject(); } |
| void EndObject() { json_writer_->EndObject(); } |
| |
| ~EventLoggerStream(); |
| |
| private: |
| void MakeStream() { |
| if (!json_writer_) { |
| json_writer_ = new JSONWriter(); |
| *this << "time_micros" |
| << std::chrono::duration_cast<std::chrono::microseconds>( |
| std::chrono::system_clock::now().time_since_epoch()).count(); |
| } |
| } |
| friend class EventLogger; |
| explicit EventLoggerStream(Logger* logger); |
| explicit EventLoggerStream(LogBuffer* log_buffer); |
| // exactly one is non-nullptr |
| Logger* const logger_; |
| LogBuffer* const log_buffer_; |
| // ownership |
| JSONWriter* json_writer_; |
| }; |
| |
| // here is an example of the output that will show up in the LOG: |
| // 2015/01/15-14:13:25.788019 1105ef000 EVENT_LOG_v1 {"time_micros": |
| // 1421360005788015, "event": "table_file_creation", "file_number": 12, |
| // "file_size": 1909699} |
| class EventLogger { |
| public: |
| static const char* Prefix() { |
| return "EVENT_LOG_v1"; |
| } |
| |
| explicit EventLogger(Logger* logger) : logger_(logger) {} |
| EventLoggerStream Log() { return EventLoggerStream(logger_); } |
| EventLoggerStream LogToBuffer(LogBuffer* log_buffer) { |
| return EventLoggerStream(log_buffer); |
| } |
| void Log(const JSONWriter& jwriter); |
| static void Log(Logger* logger, const JSONWriter& jwriter); |
| static void LogToBuffer(LogBuffer* log_buffer, const JSONWriter& jwriter); |
| |
| private: |
| Logger* logger_; |
| }; |
| |
| } // namespace rocksdb |