| /** |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF 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. |
| */ |
| /** |
| * @file Logger.cc |
| * @warning log rolling doesn't work correctly in 3.2.x see: |
| * https://issues.apache.org/jira/browse/TS-1813 |
| * Apply the patch in TS-1813 to correct log rolling in 3.2.x |
| */ |
| |
| #include "tscpp/api/Logger.h" |
| #include <cstdarg> |
| #include <vector> |
| #include <cstdio> |
| #include <string> |
| #include <cstring> |
| #include "ts/ts.h" |
| #include "tscpp/api/noncopyable.h" |
| #include "logging_internal.h" |
| |
| using std::vector; |
| using std::string; |
| |
| using atscppapi::Logger; |
| |
| /** |
| * @private |
| */ |
| struct atscppapi::LoggerState : noncopyable { |
| std::string filename_; |
| bool add_timestamp_; |
| bool rename_file_; |
| Logger::LogLevel level_; |
| bool rolling_enabled_; |
| int rolling_interval_seconds_; |
| TSTextLogObject text_log_obj_; |
| bool initialized_; |
| |
| LoggerState() |
| : add_timestamp_(false), |
| rename_file_(false), |
| level_(Logger::LOG_LEVEL_NO_LOG), |
| rolling_enabled_(false), |
| rolling_interval_seconds_(-1), |
| text_log_obj_(nullptr), |
| initialized_(false){}; |
| ~LoggerState(){}; |
| }; |
| |
| namespace |
| { |
| // Since the TSTextLog API doesn't support override the log file sizes (I will add this to TS api at some point) |
| // we will use the roll size specified by default in records.config. |
| const int ROLL_ON_TIME = 1; // See RollingEnabledValues in LogConfig.h |
| } // namespace |
| |
| /* |
| * These have default values specified for add_timestamp and rename_file in Logger.h |
| */ |
| Logger::Logger() |
| { |
| state_ = new LoggerState(); |
| } |
| |
| Logger::~Logger() |
| { |
| if (state_->initialized_ && state_->text_log_obj_) { |
| TSTextLogObjectDestroy(state_->text_log_obj_); |
| } |
| |
| delete state_; |
| } |
| |
| /* |
| * These have default values specified for rolling_enabled and rolling_interval_seconds in Logger.h |
| */ |
| bool |
| Logger::init(const string &file, bool add_timestamp, bool rename_file, LogLevel level, bool rolling_enabled, |
| int rolling_interval_seconds) |
| { |
| if (state_->initialized_) { |
| LOG_ERROR("Attempt to reinitialize a logger named '%s' that's already been initialized to '%s'.", file.c_str(), |
| state_->filename_.c_str()); |
| return false; |
| } |
| state_->filename_ = file; |
| state_->add_timestamp_ = add_timestamp; |
| state_->rename_file_ = rename_file; |
| state_->level_ = level; |
| state_->rolling_enabled_ = rolling_enabled; |
| state_->rolling_interval_seconds_ = rolling_interval_seconds; |
| state_->initialized_ = true; // set this to true always - we are not providing re-init() after a failed init() |
| |
| int mode = 0; |
| if (state_->add_timestamp_) { |
| mode |= TS_LOG_MODE_ADD_TIMESTAMP; |
| } |
| |
| if (!state_->rename_file_) { |
| mode |= TS_LOG_MODE_DO_NOT_RENAME; |
| } |
| |
| TSReturnCode result = TSTextLogObjectCreate(state_->filename_.c_str(), mode, &state_->text_log_obj_); |
| |
| if (result == TS_SUCCESS) { |
| TSTextLogObjectRollingEnabledSet(state_->text_log_obj_, state_->rolling_enabled_ ? ROLL_ON_TIME : 0); |
| TSTextLogObjectRollingIntervalSecSet(state_->text_log_obj_, state_->rolling_interval_seconds_); |
| LOG_DEBUG("Initialized log [%s]", state_->filename_.c_str()); |
| } else { |
| state_->level_ = LOG_LEVEL_NO_LOG; |
| LOG_ERROR("Failed to initialize for log [%s]", state_->filename_.c_str()); |
| } |
| |
| return result == TS_SUCCESS; |
| } |
| |
| void |
| Logger::setLogLevel(Logger::LogLevel level) |
| { |
| if (state_->initialized_) { |
| state_->level_ = level; |
| LOG_DEBUG("Set log level to %d for log [%s]", level, state_->filename_.c_str()); |
| } |
| } |
| |
| Logger::LogLevel |
| Logger::getLogLevel() const |
| { |
| if (!state_->initialized_) { |
| LOG_ERROR("Not initialized"); |
| } |
| return state_->level_; |
| } |
| |
| void |
| Logger::setRollingIntervalSeconds(int seconds) |
| { |
| if (state_->initialized_) { |
| state_->rolling_interval_seconds_ = seconds; |
| TSTextLogObjectRollingIntervalSecSet(state_->text_log_obj_, seconds); |
| LOG_DEBUG("Set rolling interval for log [%s] to %d seconds", state_->filename_.c_str(), seconds); |
| } else { |
| LOG_ERROR("Not initialized!"); |
| } |
| } |
| |
| int |
| Logger::getRollingIntervalSeconds() const |
| { |
| if (!state_->initialized_) { |
| LOG_ERROR("Not initialized"); |
| } |
| return state_->rolling_interval_seconds_; |
| } |
| |
| void |
| Logger::setRollingEnabled(bool enabled) |
| { |
| if (state_->initialized_) { |
| state_->rolling_enabled_ = enabled; |
| TSTextLogObjectRollingEnabledSet(state_->text_log_obj_, enabled ? ROLL_ON_TIME : 0); |
| LOG_DEBUG("Rolling for log [%s] is now %s", state_->filename_.c_str(), (enabled ? "true" : "false")); |
| } else { |
| LOG_ERROR("Not initialized!"); |
| } |
| } |
| |
| bool |
| Logger::isRollingEnabled() const |
| { |
| if (!state_->initialized_) { |
| LOG_ERROR("Not initialized!"); |
| } |
| return state_->rolling_enabled_; |
| } |
| |
| void |
| Logger::flush() |
| { |
| if (state_->initialized_) { |
| TSTextLogObjectFlush(state_->text_log_obj_); |
| } else { |
| LOG_ERROR("Not initialized!"); |
| } |
| } |
| |
| namespace |
| { |
| const int DEFAULT_BUFFER_SIZE_FOR_VARARGS = 8 * 1024; |
| |
| // We use a macro here because varargs would be a pain to forward via a helper |
| // function |
| #define TS_TEXT_LOG_OBJECT_WRITE(level) \ |
| char buffer[DEFAULT_BUFFER_SIZE_FOR_VARARGS]; \ |
| int n; \ |
| va_list ap; \ |
| while (true) { \ |
| va_start(ap, fmt); \ |
| n = vsnprintf(&buffer[0], sizeof(buffer), fmt, ap); \ |
| va_end(ap); \ |
| if (n > -1 && n < static_cast<int>(sizeof(buffer))) { \ |
| LOG_DEBUG("logging a " level " to '%s' with length %d", state_->filename_.c_str(), n); \ |
| TSTextLogObjectWrite(state_->text_log_obj_, const_cast<char *>("[" level "] %s"), buffer); \ |
| } else { \ |
| LOG_ERROR("Unable to log " level " message to '%s' due to size exceeding %zu bytes", state_->filename_.c_str(), \ |
| sizeof(buffer)); \ |
| } \ |
| return; \ |
| } |
| |
| } /* end anonymous namespace */ |
| |
| void |
| Logger::logDebug(const char *fmt, ...) |
| { |
| if (state_->level_ <= LOG_LEVEL_DEBUG) { |
| TS_TEXT_LOG_OBJECT_WRITE("DEBUG"); |
| } |
| } |
| |
| void |
| Logger::logInfo(const char *fmt, ...) |
| { |
| if (state_->level_ <= LOG_LEVEL_INFO) { |
| TS_TEXT_LOG_OBJECT_WRITE("INFO"); |
| } |
| } |
| |
| void |
| Logger::logError(const char *fmt, ...) |
| { |
| if (state_->level_ <= LOG_LEVEL_ERROR) { |
| TS_TEXT_LOG_OBJECT_WRITE("ERROR"); |
| } |
| } |