cpp/writer/TraceFileHelpers.cpp (113 lines of code) (raw):

/** * Copyright 2004-present, Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "TraceFileHelpers.h" #include <errno.h> #include <profilo/util/common.h> #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <zlib.h> #include <zstr/zstr.hpp> #include <sstream> #include <system_error> namespace facebook { namespace profilo { namespace writer { namespace { std::string getTraceIDAsString(int64_t trace_id) { const char* kBase64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; const size_t kTraceIdStringLen = 11; if (trace_id < 0) { throw std::invalid_argument("trace_id must be non-negative"); } char result[kTraceIdStringLen + 1]{}; for (ssize_t idx = kTraceIdStringLen - 1; idx >= 0; --idx) { result[idx] = kBase64Alphabet[trace_id % 64]; trace_id /= 64; } return std::string(result); } std::string getTraceFilename( const std::string& trace_prefix, const std::string& trace_id) { std::stringstream filename; filename << trace_prefix << "-" << getpid() << "-"; auto now = time(nullptr); struct tm localnow {}; if (localtime_r(&now, &localnow) == nullptr) { throw std::runtime_error("Could not localtime_r(3)"); } filename << (1900 + localnow.tm_year) << "-" << (1 + localnow.tm_mon) << "-" << localnow.tm_mday << "T" << localnow.tm_hour << "-" << localnow.tm_min << "-" << localnow.tm_sec; filename << "-" << trace_id << ".tmp"; return filename.str(); } std::string sanitize(std::string input) { for (size_t idx = 0; idx < input.size(); ++idx) { char ch = input[idx]; bool is_valid = (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '_' || ch == '.'; if (!is_valid) { input[idx] = '_'; } } return input; } } // namespace void TraceFileHelpers::writeHeaders( std::ostream& output, int64_t trace_id, std::vector<std::pair<std::string, std::string>> const& trace_headers) { output << "dt\n" << "ver|" << kTraceFormatVersion << "\n" << "id|" << getTraceIDAsString(trace_id) << "\n" << "prec|" << kTimestampPrecision << "\n"; for (auto const& header : trace_headers) { output << header.first << '|' << header.second << '\n'; } output << '\n'; } std::string TraceFileHelpers::getTraceFilePath( int64_t trace_id, std::string const& prefix, std::string const& folder) { std::stringstream path_stream{}; const std::string trace_id_string = getTraceIDAsString(trace_id); path_stream << folder << '/' << sanitize(getTraceFilename(prefix, trace_id_string)); return path_stream.str(); } void TraceFileHelpers::ensureFolder(const std::string& folder) { struct stat stat_out {}; if (stat(folder.c_str(), &stat_out)) { if (errno != ENOENT) { std::string error = std::string("Could not stat() folder ") + folder; throw std::system_error(errno, std::system_category(), error); } // errno == ENOENT, folder needs creating mkdirs(folder.c_str()); } } std::unique_ptr<std::ofstream> TraceFileHelpers::openCompressedStream( int64_t trace_id, std::string const& trace_folder, std::string const& trace_prefix) { ensureFolder(trace_folder.c_str()); std::string trace_file = TraceFileHelpers::getTraceFilePath(trace_id, trace_prefix, trace_folder); auto output = std::make_unique<std::ofstream>( trace_file, std::ofstream::out | std::ofstream::binary); output->exceptions(std::ofstream::badbit | std::ofstream::failbit); zstr::ostreambuf* output_buf = new zstr::ostreambuf( output->rdbuf(), // wrap the ofstream buffer 512 * 1024, // input and output buffers 3 // compression level ); // Disable ofstream buffering output->rdbuf()->pubsetbuf(nullptr, 0); // Replace ofstream buffer with the compressed one output->basic_ios<char>::rdbuf(output_buf); return output; } } // namespace writer } // namespace profilo } // namespace facebook