/src/netcdf-c/libdispatch/nclog.c
Line | Count | Source |
1 | | /********************************************************************* |
2 | | * Copyright 2018, UCAR/Unidata |
3 | | * See netcdf/COPYRIGHT file for copying and redistribution conditions. |
4 | | * $Header$ |
5 | | *********************************************************************/ |
6 | | |
7 | | #include "config.h" |
8 | | #ifdef _MSC_VER |
9 | | #include<io.h> |
10 | | #endif |
11 | | |
12 | | #include <stdlib.h> |
13 | | #include <stdio.h> |
14 | | #include <stdarg.h> |
15 | | #include <string.h> |
16 | | #include <errno.h> |
17 | | #ifdef HAVE_FCNTL_H |
18 | | #include <fcntl.h> |
19 | | #endif |
20 | | #ifdef HAVE_UNISTD_H |
21 | | #include <unistd.h> |
22 | | #endif |
23 | | |
24 | | #include "netcdf.h" |
25 | | #include "nclog.h" |
26 | | |
27 | | #define PREFIXLEN 8 |
28 | | #define MAXTAGS 256 |
29 | | #define NCTAGDFALT "Log"; |
30 | | |
31 | | #define NC_MAX_FRAMES 1024 |
32 | | |
33 | | static int nclogginginitialized = 0; |
34 | | |
35 | | static struct NCLOGGLOBAL { |
36 | | int loglevel; |
37 | | int tracelevel; |
38 | | FILE* nclogstream; |
39 | | int depth; |
40 | | struct Frame { |
41 | | const char* fcn; |
42 | | int level; |
43 | | int depth; |
44 | | } frames[NC_MAX_FRAMES]; |
45 | | } nclog_global = {0,-1,NULL}; |
46 | | |
47 | | static const char* nctagset[] = {"OFF","ERR","WARN","NOTE","DEBUG",NULL}; |
48 | | |
49 | | /* Forward */ |
50 | | static const char* nctagname(int tag); |
51 | | static int nctagforname(const char* tag); |
52 | | /*!\defgroup NClog NClog Management |
53 | | @{*/ |
54 | | |
55 | | /*!\internal |
56 | | */ |
57 | | |
58 | | void |
59 | | ncloginit(void) |
60 | 1 | { |
61 | 1 | const char* envv = NULL; |
62 | 1 | if(nclogginginitialized) |
63 | 0 | return; |
64 | 1 | nclogginginitialized = 1; |
65 | 1 | memset(&nclog_global,0,sizeof(nclog_global)); |
66 | 1 | ncsetloglevel(NCLOGOFF); |
67 | 1 | nclog_global.tracelevel = -1; |
68 | 1 | nclog_global.nclogstream = stderr; |
69 | | /* Use environment variables to preset nclogging state*/ |
70 | 1 | envv = getenv(NCENVLOGGING); |
71 | 1 | if(envv != NULL) { |
72 | 0 | int level = nctagforname(envv); |
73 | 0 | if(level > 0) { |
74 | 0 | ncsetloglevel(NCLOGNOTE); |
75 | 0 | } |
76 | 0 | } |
77 | 1 | envv = getenv(NCENVTRACING); |
78 | 1 | if(envv != NULL) nctracelevel(atoi(envv)); |
79 | 1 | } |
80 | | |
81 | | /*! |
82 | | Enable logging messages to a given level. Set to NCLOGOFF to disable |
83 | | all messages, NCLOGERR for errors only, NCLOGWARN for warnings and |
84 | | errors, and so on |
85 | | |
86 | | \param[in] level Messages above this level are ignored |
87 | | |
88 | | \return The previous value of the logging flag. |
89 | | */ |
90 | | |
91 | | int |
92 | | ncsetloglevel(int level) |
93 | 1 | { |
94 | 1 | int was; |
95 | 1 | if(!nclogginginitialized) ncloginit(); |
96 | 1 | was = nclog_global.loglevel; |
97 | 1 | if(level >= 0 && level <= NCLOGDEBUG) |
98 | 1 | nclog_global.loglevel = level; |
99 | 1 | if(nclog_global.nclogstream == NULL) nclogopen(NULL); |
100 | 1 | return was; |
101 | 1 | } |
102 | | |
103 | | int |
104 | | nclogopen(FILE* stream) |
105 | 1 | { |
106 | 1 | if(!nclogginginitialized) ncloginit(); |
107 | 1 | if(stream == NULL) stream = stderr; |
108 | 1 | nclog_global.nclogstream = stream; |
109 | 1 | return 1; |
110 | 1 | } |
111 | | |
112 | | /*! |
113 | | Send logging messages. This uses a variable |
114 | | number of arguments and operates like the stdio |
115 | | printf function. |
116 | | |
117 | | \param[in] tag Indicate the kind of this log message. |
118 | | \param[in] format Format specification as with printf. |
119 | | */ |
120 | | |
121 | | void |
122 | | nclog(int tag, const char* fmt, ...) |
123 | 3 | { |
124 | 3 | if(fmt != NULL) { |
125 | 3 | va_list args; |
126 | 3 | va_start(args, fmt); |
127 | 3 | ncvlog(tag,fmt,args); |
128 | 3 | va_end(args); |
129 | 3 | } |
130 | 3 | } |
131 | | |
132 | | void |
133 | | ncvlog(int level, const char* fmt, va_list ap) |
134 | 3 | { |
135 | 3 | const char* prefix; |
136 | | |
137 | 3 | if(!nclogginginitialized) ncloginit(); |
138 | | |
139 | 3 | if(nclog_global.loglevel < level || nclog_global.nclogstream == NULL) { |
140 | 3 | return; |
141 | 3 | } |
142 | | |
143 | 0 | prefix = nctagname(level); |
144 | 0 | fprintf(nclog_global.nclogstream,"%s: ",prefix); |
145 | 0 | if(fmt != NULL) { |
146 | 0 | vfprintf(nclog_global.nclogstream, fmt, ap); |
147 | 0 | } |
148 | 0 | fprintf(nclog_global.nclogstream, "\n" ); |
149 | 0 | fflush(nclog_global.nclogstream); |
150 | 0 | } |
151 | | |
152 | | void |
153 | | nclogtext(int tag, const char* text) |
154 | 0 | { |
155 | 0 | nclogtextn(tag,text,strlen(text)); |
156 | 0 | } |
157 | | |
158 | | /*! |
159 | | Send arbitrarily long text as a logging message. |
160 | | Each line will be sent using nclog with the specified tag. |
161 | | \param[in] tag Indicate the kind of this log message. |
162 | | \param[in] text Arbitrary text to send as a logging message. |
163 | | */ |
164 | | |
165 | | void |
166 | | nclogtextn(int level, const char* text, size_t count) |
167 | 0 | { |
168 | 0 | if(nclog_global.loglevel > level || nclog_global.nclogstream == NULL) |
169 | 0 | return; |
170 | 0 | fwrite(text,1,count,nclog_global.nclogstream); |
171 | 0 | fflush(nclog_global.nclogstream); |
172 | 0 | } |
173 | | |
174 | | static const char* |
175 | | nctagname(int tag) |
176 | 0 | { |
177 | 0 | if(tag < NCLOGOFF || tag >= NCLOGDEBUG) |
178 | 0 | return "unknown"; |
179 | 0 | return nctagset[tag]; |
180 | 0 | } |
181 | | |
182 | | static int |
183 | | nctagforname(const char* tag) |
184 | 0 | { |
185 | 0 | int level; |
186 | 0 | const char** p = NULL; |
187 | 0 | for(level=0,p=nctagset;*p;p++,level++) { |
188 | 0 | if(strcasecmp(*p,tag)==0) return level; |
189 | 0 | } |
190 | 0 | return -1; |
191 | 0 | } |
192 | | |
193 | | /*! |
194 | | Send trace messages. |
195 | | \param[in] level Indicate the level of trace |
196 | | \param[in] format Format specification as with printf. |
197 | | */ |
198 | | |
199 | | int |
200 | | nctracelevel(int level) |
201 | 0 | { |
202 | 0 | int oldlevel; |
203 | 0 | if(!nclogginginitialized) ncloginit(); |
204 | 0 | oldlevel = nclog_global.tracelevel; |
205 | 0 | if(level < 0) { |
206 | 0 | nclog_global.tracelevel = level; |
207 | 0 | } else { /*(level >= 0)*/ |
208 | 0 | nclog_global.tracelevel = level; |
209 | 0 | nclogopen(NULL); /* use stderr */ |
210 | 0 | } |
211 | 0 | return oldlevel; |
212 | 0 | } |
213 | | |
214 | | void |
215 | | nctrace(int level, const char* fcn, const char* fmt, ...) |
216 | 0 | { |
217 | 0 | va_list args; |
218 | 0 | va_start(args, fmt); |
219 | 0 | ncvtrace(level,fcn,fmt,args); |
220 | 0 | va_end(args); |
221 | 0 | } |
222 | | |
223 | | void |
224 | | nctracemore(int level, const char* fmt, ...) |
225 | 0 | { |
226 | 0 | va_list args; |
227 | 0 | va_start(args, fmt); |
228 | 0 | ncvtrace(level,NULL,fmt,args); |
229 | 0 | va_end(args); |
230 | 0 | } |
231 | | |
232 | | void |
233 | | ncvtrace(int level, const char* fcn, const char* fmt, va_list ap) |
234 | 0 | { |
235 | 0 | struct Frame* frame; |
236 | 0 | if(!nclogginginitialized) ncloginit(); |
237 | 0 | if(fcn != NULL) { |
238 | 0 | frame = &nclog_global.frames[nclog_global.depth]; |
239 | 0 | frame->fcn = fcn; |
240 | 0 | frame->level = level; |
241 | 0 | frame->depth = nclog_global.depth; |
242 | 0 | } |
243 | 0 | if(level <= nclog_global.tracelevel) { |
244 | 0 | if(fcn != NULL) |
245 | 0 | fprintf(nclog_global.nclogstream,"%s: (%d): %s:","Enter",level,fcn); |
246 | 0 | if(fmt != NULL) |
247 | 0 | vfprintf(nclog_global.nclogstream, fmt, ap); |
248 | 0 | fprintf(nclog_global.nclogstream, "\n" ); |
249 | 0 | fflush(nclog_global.nclogstream); |
250 | 0 | } |
251 | 0 | if(fcn != NULL) nclog_global.depth++; |
252 | 0 | } |
253 | | |
254 | | int |
255 | | ncuntrace(const char* fcn, int err, const char* fmt, ...) |
256 | 0 | { |
257 | 0 | va_list args; |
258 | 0 | struct Frame* frame; |
259 | 0 | va_start(args, fmt); |
260 | 0 | if(nclog_global.depth == 0) { |
261 | 0 | fprintf(nclog_global.nclogstream,"*** Unmatched untrace: %s: depth==0\n",fcn); |
262 | 0 | goto done; |
263 | 0 | } |
264 | 0 | nclog_global.depth--; |
265 | 0 | frame = &nclog_global.frames[nclog_global.depth]; |
266 | 0 | if(frame->depth != nclog_global.depth || strcmp(frame->fcn,fcn) != 0) { |
267 | 0 | fprintf(nclog_global.nclogstream,"*** Unmatched untrace: fcn=%s expected=%s\n",frame->fcn,fcn); |
268 | 0 | goto done; |
269 | 0 | } |
270 | 0 | if(frame->level <= nclog_global.tracelevel) { |
271 | 0 | fprintf(nclog_global.nclogstream,"%s: (%d): %s: ","Exit",frame->level,frame->fcn); |
272 | 0 | if(err) |
273 | 0 | fprintf(nclog_global.nclogstream,"err=(%d) '%s':",err,nc_strerror(err)); |
274 | 0 | if(fmt != NULL) |
275 | 0 | vfprintf(nclog_global.nclogstream, fmt, args); |
276 | 0 | fprintf(nclog_global.nclogstream, "\n" ); |
277 | 0 | fflush(nclog_global.nclogstream); |
278 | 0 | } |
279 | 0 | done: |
280 | 0 | va_end(args); |
281 | 0 | if(err != 0) |
282 | 0 | return ncbreakpoint(err); |
283 | 0 | else |
284 | 0 | return err; |
285 | 0 | } |
286 | | |
287 | | int |
288 | | ncthrow(int err,const char* file,int line) |
289 | 0 | { |
290 | 0 | if(err == 0) return err; |
291 | 0 | return ncbreakpoint(err); |
292 | 0 | } |
293 | | |
294 | | int |
295 | | ncbreakpoint(int err) |
296 | 0 | { |
297 | 0 | return err; |
298 | 0 | } |
299 | | |
300 | | /**@}*/ |