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