1 /** @file 2 3 A brief file description 4 5 @section license License 6 7 Licensed to the Apache Software Foundation (ASF) under one 8 or more contributor license agreements. See the NOTICE file 9 distributed with this work for additional information 10 regarding copyright ownership. The ASF licenses this file 11 to you under the Apache License, Version 2.0 (the 12 "License"); you may not use this file except in compliance 13 with the License. You may obtain a copy of the License at 14 15 http://www.apache.org/licenses/LICENSE-2.0 16 17 Unless required by applicable law or agreed to in writing, software 18 distributed under the License is distributed on an "AS IS" BASIS, 19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 See the License for the specific language governing permissions and 21 limitations under the License. 22 */ 23 24 /**************************************************************************** 25 26 Diags.h 27 28 This file contains code to manipulate run-time diagnostics, and print 29 warnings and errors at runtime. Action tags and debugging tags are 30 supported, allowing run-time conditionals affecting diagnostics. 31 32 ****************************************************************************/ 33 34 #pragma once 35 36 #include <cstdarg> 37 #include "ink_mutex.h" 38 #include "Regex.h" 39 #include "ink_apidefs.h" 40 #include "ContFlags.h" 41 #include "ink_inet.h" 42 #include "BaseLogFile.h" 43 #include "SourceLocation.h" 44 45 #define DIAGS_MAGIC 0x12345678 46 #define BYTES_IN_MB 1000000 47 48 class Diags; 49 50 // extern int diags_on_for_plugins; 51 enum DiagsTagType { 52 DiagsTagType_Debug = 0, // do not renumber --- used as array index 53 DiagsTagType_Action = 1 54 }; 55 56 struct DiagsModeOutput { 57 bool to_stdout; 58 bool to_stderr; 59 bool to_syslog; 60 bool to_diagslog; 61 }; 62 63 enum DiagsLevel { // do not renumber --- used as array index 64 DL_Diag = 0, // process does not die 65 DL_Debug, // process does not die 66 DL_Status, // process does not die 67 DL_Note, // process does not die 68 DL_Warning, // process does not die 69 DL_Error, // process does not die 70 DL_Fatal, // causes process termination 71 DL_Alert, // causes process termination 72 DL_Emergency, // causes process termination, exits with UNRECOVERABLE_EXIT 73 DL_Undefined // must be last, used for size! 74 }; 75 76 enum StdStream { STDOUT = 0, STDERR }; 77 78 enum RollingEnabledValues { NO_ROLLING = 0, ROLL_ON_TIME, ROLL_ON_SIZE, ROLL_ON_TIME_OR_SIZE, INVALID_ROLLING_VALUE }; 79 80 enum DiagsShowLocation { SHOW_LOCATION_NONE = 0, SHOW_LOCATION_DEBUG, SHOW_LOCATION_ALL }; 81 82 #define DiagsLevel_Count DL_Undefined 83 84 #define DiagsLevel_IsTerminal(_l) (((_l) >= DL_Fatal) && ((_l) < DL_Undefined)) 85 86 // Cleanup Function Prototype - Called before ink_fatal to 87 // cleanup process state 88 typedef void (*DiagsCleanupFunc)(); 89 90 struct DiagsConfigState { 91 // this is static to eliminate many loads from the critical path 92 static int enabled[2]; // one debug, one action 93 DiagsModeOutput outputs[DiagsLevel_Count]; // where each level prints 94 }; 95 96 ////////////////////////////////////////////////////////////////////////////// 97 // 98 // class Diags 99 // 100 // The Diags class is used for global configuration of the run-time 101 // diagnostics system. This class provides the following services: 102 // 103 // * run-time notices, debugging, warnings, errors 104 // * debugging tags to selectively enable & disable diagnostics 105 // * action tags to selectively enable & disable code paths 106 // * configurable output to stdout, stderr, syslog, error logs 107 // * traffic_manager interface supporting on-the-fly reconfiguration 108 // 109 ////////////////////////////////////////////////////////////////////////////// 110 111 class Diags 112 { 113 public: 114 Diags(std::string_view prefix_string, const char *base_debug_tags, const char *base_action_tags, BaseLogFile *_diags_log, 115 int diags_log_perm = -1, int output_log_perm = -1); 116 virtual ~Diags(); 117 118 BaseLogFile *diags_log; 119 BaseLogFile *stdout_log; 120 BaseLogFile *stderr_log; 121 122 const unsigned int magic; 123 DiagsConfigState config; 124 DiagsShowLocation show_location; 125 DiagsCleanupFunc cleanup_func; 126 127 /////////////////////////// 128 // conditional debugging // 129 /////////////////////////// 130 131 bool get_override()132 get_override() const 133 { 134 return get_cont_flag(ContFlags::DEBUG_OVERRIDE); 135 } 136 137 bool test_override_ip(IpEndpoint const & test_ip)138 test_override_ip(IpEndpoint const &test_ip) 139 { 140 return this->debug_client_ip == test_ip; 141 } 142 143 bool 144 on(DiagsTagType mode = DiagsTagType_Debug) const 145 { 146 return ((config.enabled[mode] == 1) || (config.enabled[mode] == 2 && this->get_override())); 147 } 148 149 bool 150 on(const char *tag, DiagsTagType mode = DiagsTagType_Debug) const 151 { 152 return this->on(mode) && tag_activated(tag, mode); 153 } 154 155 ///////////////////////////////////// 156 // low-level tag inquiry functions // 157 ///////////////////////////////////// 158 159 bool tag_activated(const char *tag, DiagsTagType mode = DiagsTagType_Debug) const; 160 161 ///////////////////////////// 162 // raw printing interfaces // 163 ///////////////////////////// 164 165 const char *level_name(DiagsLevel level) const; 166 167 /////////////////////////////////////////////////////////////////////// 168 // user diagnostic output interfaces --- enabled on or off based // 169 // on the value of the enable flag, and the state of the debug tags. // 170 /////////////////////////////////////////////////////////////////////// 171 172 void print(const char * tag,DiagsLevel level,const SourceLocation * loc,const char * fmt,...)173 print(const char *tag, DiagsLevel level, const SourceLocation *loc, const char *fmt, ...) const TS_PRINTFLIKE(5, 6) 174 { 175 va_list ap; 176 va_start(ap, fmt); 177 print_va(tag, level, loc, fmt, ap); 178 va_end(ap); 179 } 180 181 void print_va(const char *tag, DiagsLevel level, const SourceLocation *loc, const char *fmt, va_list ap) const; 182 183 void log(const char * tag,DiagsLevel level,const SourceLocation * loc,const char * fmt,...)184 log(const char *tag, DiagsLevel level, const SourceLocation *loc, const char *fmt, ...) const TS_PRINTFLIKE(5, 6) 185 { 186 if (on(tag)) { 187 va_list ap; 188 va_start(ap, fmt); 189 print_va(tag, level, loc, fmt, ap); 190 va_end(ap); 191 } 192 } 193 194 void log_va(const char * tag,DiagsLevel level,const SourceLocation * loc,const char * fmt,va_list ap)195 log_va(const char *tag, DiagsLevel level, const SourceLocation *loc, const char *fmt, va_list ap) 196 { 197 if (on(tag)) { 198 print_va(tag, level, loc, fmt, ap); 199 } 200 } 201 202 void error(DiagsLevel level,const SourceLocation * loc,const char * fmt,...)203 error(DiagsLevel level, const SourceLocation *loc, const char *fmt, ...) const TS_PRINTFLIKE(4, 5) 204 { 205 va_list ap; 206 va_start(ap, fmt); 207 error_va(level, loc, fmt, ap); 208 va_end(ap); 209 } 210 211 virtual void error_va(DiagsLevel level, const SourceLocation *loc, const char *fmt, va_list ap) const; 212 213 void dump(FILE *fp = stdout) const; 214 215 void activate_taglist(const char *taglist, DiagsTagType mode = DiagsTagType_Debug); 216 217 void deactivate_all(DiagsTagType mode = DiagsTagType_Debug); 218 219 bool setup_diagslog(BaseLogFile *blf); 220 void config_roll_diagslog(RollingEnabledValues re, int ri, int rs); 221 void config_roll_outputlog(RollingEnabledValues re, int ri, int rs); 222 bool reseat_diagslog(); 223 bool should_roll_diagslog(); 224 bool should_roll_outputlog(); 225 226 bool set_std_output(StdStream stream, const char *file); 227 228 const char *base_debug_tags; // internal copy of default debug tags 229 const char *base_action_tags; // internal copy of default action tags 230 231 IpAddr debug_client_ip; 232 233 private: 234 const std::string prefix_str; 235 mutable ink_mutex tag_table_lock; // prevents reconfig/read races 236 DFA *activated_tags[2]; // 1 table for debug, 1 for action 237 238 // These are the default logfile permissions 239 int diags_logfile_perm = -1; 240 int output_logfile_perm = -1; 241 242 // log rotation variables 243 RollingEnabledValues outputlog_rolling_enabled; 244 int outputlog_rolling_size; 245 int outputlog_rolling_interval; 246 RollingEnabledValues diagslog_rolling_enabled; 247 int diagslog_rolling_interval; 248 int diagslog_rolling_size; 249 time_t outputlog_time_last_roll; 250 time_t diagslog_time_last_roll; 251 252 bool rebind_std_stream(StdStream stream, int new_fd); 253 254 void lock()255 lock() const 256 { 257 ink_mutex_acquire(&tag_table_lock); 258 } 259 260 void unlock()261 unlock() const 262 { 263 ink_mutex_release(&tag_table_lock); 264 } 265 }; 266 267 ////////////////////////////////////////////////////////////////////////// 268 // // 269 // Macros // 270 // // 271 // The following are diagnostic macros that wrap up the compiler // 272 // __FILE__, __FUNCTION__, and __LINE__ macros into closures // 273 // and then invoke the closure on the remaining arguments. // 274 // // 275 // This closure hack is done, because the cpp preprocessor doesn't // 276 // support manipulation and union of varargs parameters. // 277 // // 278 ////////////////////////////////////////////////////////////////////////// 279 280 #if !defined(__GNUC__) 281 #ifndef __FUNCTION__ 282 #define __FUNCTION__ nullptr 283 #endif 284 #endif 285 286 extern inkcoreapi Diags *diags; 287 288 #define DiagsError(level, fmt, ...) \ 289 do { \ 290 SourceLocation loc = MakeSourceLocation(); \ 291 diags->error(level, &loc, fmt, ##__VA_ARGS__); \ 292 } while (0) 293 294 #define Status(...) DiagsError(DL_Status, __VA_ARGS__) // Log information 295 #define Note(...) DiagsError(DL_Note, __VA_ARGS__) // Log significant information 296 #define Warning(...) DiagsError(DL_Warning, __VA_ARGS__) // Log concerning information 297 #define Error(...) DiagsError(DL_Error, __VA_ARGS__) // Log operational failure, fail CI 298 #define Fatal(...) DiagsError(DL_Fatal, __VA_ARGS__) // Log recoverable crash, fail CI, exit & allow restart 299 #define Alert(...) DiagsError(DL_Alert, __VA_ARGS__) // Log recoverable crash, fail CI, exit & restart, Ops attention 300 #define Emergency(...) DiagsError(DL_Emergency, __VA_ARGS__) // Log unrecoverable crash, fail CI, exit, Ops attention 301 302 #define DiagsErrorV(level, fmt, ap) \ 303 do { \ 304 const SourceLocation loc = MakeSourceLocation(); \ 305 diags->error_va(level, &loc, fmt, ap); \ 306 } while (0) 307 308 #define StatusV(fmt, ap) DiagsErrorV(DL_Status, fmt, ap) 309 #define NoteV(fmt, ap) DiagsErrorV(DL_Note, fmt, ap) 310 #define WarningV(fmt, ap) DiagsErrorV(DL_Warning, fmt, ap) 311 #define ErrorV(fmt, ap) DiagsErrorV(DL_Error, fmt, ap) 312 #define FatalV(fmt, ap) DiagsErrorV(DL_Fatal, fmt, ap) 313 #define AlertV(fmt, ap) DiagsErrorV(DL_Alert, fmt, ap) 314 #define EmergencyV(fmt, ap) DiagsErrorV(DL_Emergency, fmt, ap) 315 316 #if TS_USE_DIAGS 317 318 #define Diag(tag, ...) \ 319 do { \ 320 if (unlikely(diags->on())) { \ 321 const SourceLocation loc = MakeSourceLocation(); \ 322 diags->log(tag, DL_Diag, &loc, __VA_ARGS__); \ 323 } \ 324 } while (0) 325 326 #define Debug(tag, ...) \ 327 do { \ 328 if (unlikely(diags->on())) { \ 329 const SourceLocation loc = MakeSourceLocation(); \ 330 diags->log(tag, DL_Debug, &loc, __VA_ARGS__); \ 331 } \ 332 } while (0) 333 334 #define SpecificDebug(flag, tag, ...) \ 335 do { \ 336 if (unlikely(diags->on())) { \ 337 const SourceLocation loc = MakeSourceLocation(); \ 338 flag ? diags->print(tag, DL_Debug, &loc, __VA_ARGS__) : diags->log(tag, DL_Debug, &loc, __VA_ARGS__); \ 339 } \ 340 } while (0) 341 342 #define is_debug_tag_set(_t) unlikely(diags->on(_t, DiagsTagType_Debug)) 343 #define is_action_tag_set(_t) unlikely(diags->on(_t, DiagsTagType_Action)) 344 #define debug_tag_assert(_t, _a) (is_debug_tag_set(_t) ? (ink_release_assert(_a), 0) : 0) 345 #define action_tag_assert(_t, _a) (is_action_tag_set(_t) ? (ink_release_assert(_a), 0) : 0) 346 #define is_diags_on(_t) unlikely(diags->on(_t)) 347 348 #else // TS_USE_DIAGS 349 350 #define Diag(tag, fmt, ...) 351 #define Debug(tag, fmt, ...) 352 #define SpecificDebug(flag, tag, ...) 353 354 #define is_debug_tag_set(_t) 0 355 #define is_action_tag_set(_t) 0 356 #define debug_tag_assert(_t, _a) /**/ 357 #define action_tag_assert(_t, _a) /**/ 358 #define is_diags_on(_t) 0 359 360 #endif // TS_USE_DIAGS 361