1 /**
2 Licensed to the Apache Software Foundation (ASF) under one
3 or more contributor license agreements. See the NOTICE file
4 distributed with this work for additional information
5 regarding copyright ownership. The ASF licenses this file
6 to you under the Apache License, Version 2.0 (the
7 "License"); you may not use this file except in compliance
8 with the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 */
18 /**
19 * @file Logger.cc
20 * @warning log rolling doesn't work correctly in 3.2.x see:
21 * https://issues.apache.org/jira/browse/TS-1813
22 * Apply the patch in TS-1813 to correct log rolling in 3.2.x
23 */
24
25 #include "tscpp/api/Logger.h"
26 #include <cstdarg>
27 #include <vector>
28 #include <cstdio>
29 #include <string>
30 #include <cstring>
31 #include "ts/ts.h"
32 #include "tscpp/api/noncopyable.h"
33 #include "logging_internal.h"
34
35 using std::vector;
36 using std::string;
37
38 using atscppapi::Logger;
39
40 /**
41 * @private
42 */
43 struct atscppapi::LoggerState : noncopyable {
44 std::string filename_;
45 bool add_timestamp_ = false;
46 bool rename_file_ = false;
47 Logger::LogLevel level_ = Logger::LOG_LEVEL_NO_LOG;
48 bool rolling_enabled_ = false;
49 int rolling_interval_seconds_ = -1;
50 TSTextLogObject text_log_obj_ = nullptr;
51 bool initialized_ = false;
52
53 LoggerState()
54
55 = default;
56 ;
57 ~LoggerState() = default;
58 ;
59 };
60
61 namespace
62 {
63 // Since the TSTextLog API doesn't support override the log file sizes (I will add this to TS api at some point)
64 // we will use the roll size specified by default in records.config.
65 const int ROLL_ON_TIME = 1; // See RollingEnabledValues in LogConfig.h
66 } // namespace
67
68 /*
69 * These have default values specified for add_timestamp and rename_file in Logger.h
70 */
Logger()71 Logger::Logger()
72 {
73 state_ = new LoggerState();
74 }
75
~Logger()76 Logger::~Logger()
77 {
78 if (state_->initialized_ && state_->text_log_obj_) {
79 TSTextLogObjectDestroy(state_->text_log_obj_);
80 }
81
82 delete state_;
83 }
84
85 /*
86 * These have default values specified for rolling_enabled and rolling_interval_seconds in Logger.h
87 */
88 bool
init(const string & file,bool add_timestamp,bool rename_file,LogLevel level,bool rolling_enabled,int rolling_interval_seconds)89 Logger::init(const string &file, bool add_timestamp, bool rename_file, LogLevel level, bool rolling_enabled,
90 int rolling_interval_seconds)
91 {
92 if (state_->initialized_) {
93 LOG_ERROR("Attempt to reinitialize a logger named '%s' that's already been initialized to '%s'.", file.c_str(),
94 state_->filename_.c_str());
95 return false;
96 }
97 state_->filename_ = file;
98 state_->add_timestamp_ = add_timestamp;
99 state_->rename_file_ = rename_file;
100 state_->level_ = level;
101 state_->rolling_enabled_ = rolling_enabled;
102 state_->rolling_interval_seconds_ = rolling_interval_seconds;
103 state_->initialized_ = true; // set this to true always - we are not providing re-init() after a failed init()
104
105 int mode = 0;
106 if (state_->add_timestamp_) {
107 mode |= TS_LOG_MODE_ADD_TIMESTAMP;
108 }
109
110 if (!state_->rename_file_) {
111 mode |= TS_LOG_MODE_DO_NOT_RENAME;
112 }
113
114 TSReturnCode result = TSTextLogObjectCreate(state_->filename_.c_str(), mode, &state_->text_log_obj_);
115
116 if (result == TS_SUCCESS) {
117 TSTextLogObjectRollingEnabledSet(state_->text_log_obj_, state_->rolling_enabled_ ? ROLL_ON_TIME : 0);
118 TSTextLogObjectRollingIntervalSecSet(state_->text_log_obj_, state_->rolling_interval_seconds_);
119 LOG_DEBUG("Initialized log [%s]", state_->filename_.c_str());
120 } else {
121 state_->level_ = LOG_LEVEL_NO_LOG;
122 LOG_ERROR("Failed to initialize for log [%s]", state_->filename_.c_str());
123 }
124
125 return result == TS_SUCCESS;
126 }
127
128 void
setLogLevel(Logger::LogLevel level)129 Logger::setLogLevel(Logger::LogLevel level)
130 {
131 if (state_->initialized_) {
132 state_->level_ = level;
133 LOG_DEBUG("Set log level to %d for log [%s]", level, state_->filename_.c_str());
134 }
135 }
136
137 Logger::LogLevel
getLogLevel() const138 Logger::getLogLevel() const
139 {
140 if (!state_->initialized_) {
141 LOG_ERROR("Not initialized");
142 }
143 return state_->level_;
144 }
145
146 void
setRollingIntervalSeconds(int seconds)147 Logger::setRollingIntervalSeconds(int seconds)
148 {
149 if (state_->initialized_) {
150 state_->rolling_interval_seconds_ = seconds;
151 TSTextLogObjectRollingIntervalSecSet(state_->text_log_obj_, seconds);
152 LOG_DEBUG("Set rolling interval for log [%s] to %d seconds", state_->filename_.c_str(), seconds);
153 } else {
154 LOG_ERROR("Not initialized!");
155 }
156 }
157
158 int
getRollingIntervalSeconds() const159 Logger::getRollingIntervalSeconds() const
160 {
161 if (!state_->initialized_) {
162 LOG_ERROR("Not initialized");
163 }
164 return state_->rolling_interval_seconds_;
165 }
166
167 void
setRollingEnabled(bool enabled)168 Logger::setRollingEnabled(bool enabled)
169 {
170 if (state_->initialized_) {
171 state_->rolling_enabled_ = enabled;
172 TSTextLogObjectRollingEnabledSet(state_->text_log_obj_, enabled ? ROLL_ON_TIME : 0);
173 LOG_DEBUG("Rolling for log [%s] is now %s", state_->filename_.c_str(), (enabled ? "true" : "false"));
174 } else {
175 LOG_ERROR("Not initialized!");
176 }
177 }
178
179 bool
isRollingEnabled() const180 Logger::isRollingEnabled() const
181 {
182 if (!state_->initialized_) {
183 LOG_ERROR("Not initialized!");
184 }
185 return state_->rolling_enabled_;
186 }
187
188 void
flush()189 Logger::flush()
190 {
191 if (state_->initialized_) {
192 TSTextLogObjectFlush(state_->text_log_obj_);
193 } else {
194 LOG_ERROR("Not initialized!");
195 }
196 }
197
198 namespace
199 {
200 const int DEFAULT_BUFFER_SIZE_FOR_VARARGS = 8 * 1024;
201
202 // We use a macro here because varargs would be a pain to forward via a helper
203 // function
204 #define TS_TEXT_LOG_OBJECT_WRITE(level) \
205 char buffer[DEFAULT_BUFFER_SIZE_FOR_VARARGS]; \
206 int n; \
207 va_list ap; \
208 while (true) { \
209 va_start(ap, fmt); \
210 n = vsnprintf(&buffer[0], sizeof(buffer), fmt, ap); \
211 va_end(ap); \
212 if (n > -1 && n < static_cast<int>(sizeof(buffer))) { \
213 LOG_DEBUG("logging a " level " to '%s' with length %d", state_->filename_.c_str(), n); \
214 TSTextLogObjectWrite(state_->text_log_obj_, const_cast<char *>("[" level "] %s"), buffer); \
215 } else { \
216 LOG_ERROR("Unable to log " level " message to '%s' due to size exceeding %zu bytes", state_->filename_.c_str(), \
217 sizeof(buffer)); \
218 } \
219 return; \
220 }
221
222 } /* end anonymous namespace */
223
224 void
logDebug(const char * fmt,...)225 Logger::logDebug(const char *fmt, ...)
226 {
227 if (state_->level_ <= LOG_LEVEL_DEBUG) {
228 TS_TEXT_LOG_OBJECT_WRITE("DEBUG");
229 }
230 }
231
232 void
logInfo(const char * fmt,...)233 Logger::logInfo(const char *fmt, ...)
234 {
235 if (state_->level_ <= LOG_LEVEL_INFO) {
236 TS_TEXT_LOG_OBJECT_WRITE("INFO");
237 }
238 }
239
240 void
logError(const char * fmt,...)241 Logger::logError(const char *fmt, ...)
242 {
243 if (state_->level_ <= LOG_LEVEL_ERROR) {
244 TS_TEXT_LOG_OBJECT_WRITE("ERROR");
245 }
246 }
247