prod/native/libcommon/code/Logger.cpp (167 lines of code) (raw):

/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you 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 "Logger.h" #include <sys/syscall.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <syslog.h> namespace elasticapm::php { using namespace std::string_literals; void Logger::setLogFeatures(std::unordered_map<elasticapm::php::LogFeature, LogLevel> features) { features_ = std::move(features); } bool Logger::doesFeatureMeetsLevelCondition(LogLevel level, LogFeature feature) const { if (features_.empty()) { return doesMeetsLevelCondition(level); } { auto found = features_.find(feature); if (found != std::end(features_)) { return level <= found->second; } } auto found = features_.find(LogFeature::ALL); if (found != std::end(features_)) { return level <= found->second; } return false; } LogLevel Logger::getMaxLogLevel() const { auto maxLevel = LogLevel::logLevel_off; for (auto const &sink : sinks_) { maxLevel = std::max(sink->getLevel(), maxLevel); } return maxLevel; } bool Logger::doesMeetsLevelCondition(LogLevel level) const { auto maxLevel = LogLevel::logLevel_off; for (auto const &sink : sinks_) { maxLevel = std::max(sink->getLevel(), maxLevel); } return level <= maxLevel; } void Logger::attachSink(std::shared_ptr<LoggerSinkInterface> sink) { std::lock_guard<SpinLock> lock(spinLock_); sinks_.emplace_back(std::move(sink)); } void Logger::printf(LogLevel level, const char *format, ...) const { va_list args; va_start(args, format); auto msg = elasticapm::utils::stringVPrintf(format, args); va_end(args); std::string output = "[EDOT] "s; output.append(getFormattedTime()); output.append(" "); size_t indexOfProcessData = output.length(); output.append(getFormattedProcessData()); size_t indexOfLogLevel = output.length(); output.append(" ["); output.append(getLogLevelName(level)); output.append("] "); size_t indexOfMessage = output.length(); output.append(std::move(msg)); output.push_back('\n'); auto outputSv = std::string_view{output}; auto msgSv = outputSv.substr(indexOfMessage, output.length() - indexOfMessage - 1); auto timeSv = outputSv.substr(0, indexOfProcessData - 1); auto processSv = outputSv.substr(indexOfProcessData, indexOfLogLevel - indexOfProcessData); auto levelSv = outputSv.substr(indexOfLogLevel + 1, indexOfMessage - indexOfLogLevel - 2); std::lock_guard<SpinLock> lock(spinLock_); for (auto const &sink : sinks_) { if (sink->getLevel() < level) { continue; } sink->writeLog(output, msgSv, timeSv, levelSv, processSv); } } void Logger::log(LogLevel level, const std::string &message) const { std::string output = "[EDOT] "s; output.append(getFormattedTime()); output.append(" "); size_t indexOfProcessData = output.length(); output.append(getFormattedProcessData()); size_t indexOfLogLevel = output.length(); output.append(" ["); output.append(getLogLevelName(level)); output.append("] "); size_t indexOfMessage = output.length(); output.append(message); output.push_back('\n'); auto outputSv = std::string_view{output}; auto msgSv = outputSv.substr(indexOfMessage, output.length() - indexOfMessage - 1); auto timeSv = outputSv.substr(0, indexOfProcessData - 1); auto processSv = outputSv.substr(indexOfProcessData, indexOfLogLevel - indexOfProcessData); auto levelSv = outputSv.substr(indexOfLogLevel + 1, indexOfMessage - indexOfLogLevel - 2); std::lock_guard<SpinLock> lock(spinLock_); for (auto const &sink : sinks_) { if (sink->getLevel() < level) { continue; } sink->writeLog(output, msgSv, timeSv, levelSv, processSv); } } std::string Logger::getFormattedTime() const { const auto now = std::chrono::system_clock::now(); const std::time_t nowTime = std::chrono::system_clock::to_time_t(now); auto seconds = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()); int64_t mircros = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch() - seconds).count(); struct tm buf; gmtime_r(&nowTime, &buf); return elasticapm::utils::stringPrintf("[%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d.%6.6d UTC]", buf.tm_year + 1900, buf.tm_mon + 1, buf.tm_mday, buf.tm_hour, buf.tm_min, buf.tm_sec, mircros); } std::string Logger::getFormattedProcessData() const { std::string data; data.append("["); data.append(std::to_string(getpid())); data.append("/"); auto tid = syscall( SYS_gettid ); data.append(std::to_string(tid)); data.append("]"); return data; } LogLevel LoggerSinkStdErr::getLevel() const { return level_; } void LoggerSinkStdErr::setLevel(LogLevel level) { level_ = level; } void LoggerSinkStdErr::writeLog(std::string const &formattedOutput, std::string_view message, std::string_view time, std::string_view level, std::string_view process) const { // mt-safe (multithread) [[maybe_unused]] auto rv = ::write(STDERR_FILENO, formattedOutput.c_str(), formattedOutput.length()); } LogLevel LoggerSinkSysLog::getLevel() const { return level_; } void LoggerSinkSysLog::setLevel(LogLevel level) { level_ = level; } void LoggerSinkSysLog::writeLog(std::string const &formattedOutput, std::string_view message, std::string_view time, std::string_view level, std::string_view process) const { // mt-safe (multithread) ::syslog(LOG_ALERT, PRsv " " PRsv, PRsvArg(formattedOutput), PRsvArg(formattedOutput)); } LogLevel LoggerSinkFile::getLevel() const { return level_; } void LoggerSinkFile::setLevel(LogLevel level) { level_ = level; } void LoggerSinkFile::writeLog(std::string const &formattedOutput, std::string_view message, std::string_view time, std::string_view level, std::string_view process) const { if (fd_ < 0) { return; } // mt-safe (multithread) [[maybe_unused]] auto rv = ::write(fd_, formattedOutput.c_str(), formattedOutput.length()); } //TODO note, in case of fork each worker can have different fd (and different log name) opened. Consided shm sync. bool LoggerSinkFile::reopen(std::string fileName) { std::lock_guard<SpinLock> lock(spinLock_); if (openedFilePath_ == fileName) { return true; } if (fd_ >= 0) { close(fd_); openedFilePath_.clear(); } fd_ = open(fileName.c_str(), O_APPEND | O_CREAT | O_WRONLY, S_IRWXU | S_IRWXG); if (fd_ >=0) { openedFilePath_ = std::move(fileName); return true; } return false; } }