Improve handling of verbosity
[src/app-framework-binder.git] / src / verbose.c
1 /*
2  Copyright (C) 2016, 2017 "IoT.bzh"
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
6  Licensed under the Apache License, Version 2.0 (the "License");
7  you may not use this file except in compliance with the License.
8  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 #include <stdio.h>
20 #include <stdarg.h>
21
22 #include "verbose.h"
23
24 #if !defined(DEFAULT_VERBOSITY)
25 # define DEFAULT_VERBOSITY Verbosity_Level_Warning
26 #endif
27
28 int verbosity = 1;
29 void (*verbose_observer)(int loglevel, const char *file, int line, const char *function, const char *fmt, va_list args);
30
31 #define CROP_LOGLEVEL(x) ((x) < Log_Level_Emergency ? Log_Level_Emergency : (x) > Log_Level_Debug ? Log_Level_Debug : (x))
32
33 #if defined(VERBOSE_WITH_SYSLOG)
34
35 #include <syslog.h>
36
37 static void _vverbose_(int loglevel, const char *file, int line, const char *function, const char *fmt, va_list args)
38 {
39         char *p;
40
41         if (file == NULL || vasprintf(&p, fmt, args) < 0)
42                 vsyslog(loglevel, fmt, args);
43         else {
44                 syslog(CROP_LOGLEVEL(loglevel), "%s [%s:%d, function]", p, file, line, function);
45                 free(p);
46         }
47 }
48
49 void verbose_set_name(const char *name, int authority)
50 {
51         openlog(name, LOG_PERROR, authority ? LOG_AUTH : LOG_USER);
52 }
53
54 #elif defined(VERBOSE_WITH_SYSTEMD)
55
56 #define SD_JOURNAL_SUPPRESS_LOCATION
57
58 #include <systemd/sd-journal.h>
59
60 static const char *appname;
61
62 static int appauthority;
63
64 static void _vverbose_(int loglevel, const char *file, int line, const char *function, const char *fmt, va_list args)
65 {
66         char lino[20];
67
68         if (file == NULL) {
69                 sd_journal_printv(loglevel, fmt, args);
70         } else {
71                 sprintf(lino, "%d", line);
72                 sd_journal_printv_with_location(loglevel, file, lino, function, fmt, args);
73         }
74 }
75
76 void verbose_set_name(const char *name, int authority)
77 {
78         appname = name;
79         appauthority = authority;
80 }
81
82 #else
83
84 #include <unistd.h>
85 #include <errno.h>
86 #include <string.h>
87 #include <sys/uio.h>
88
89 static const char *appname;
90
91 static int appauthority;
92
93 static const char *prefixes[] = {
94         "<0> EMERGENCY",
95         "<1> ALERT",
96         "<2> CRITICAL",
97         "<3> ERROR",
98         "<4> WARNING",
99         "<5> NOTICE",
100         "<6> INFO",
101         "<7> DEBUG"
102 };
103
104 static int tty;
105
106 static const char chars[] = { '\n', '?', ':', ' ', '[', ',', ']' };
107
108 static void _vverbose_(int loglevel, const char *file, int line, const char *function, const char *fmt, va_list args)
109 {
110         char buffer[4000];
111         char lino[40];
112         int saverr, n, rc;
113         struct iovec iov[20];
114
115         saverr = errno;
116
117         if (!tty)
118                 tty = 1 + isatty(STDERR_FILENO);
119
120         iov[0].iov_base = (void*)prefixes[CROP_LOGLEVEL(loglevel)] + (tty - 1 ? 4 : 0);
121         iov[0].iov_len = strlen(iov[0].iov_base);
122
123         iov[1].iov_base = (void*)&chars[2];
124         iov[1].iov_len = 2;
125
126         n = 2;
127         if (fmt) {
128                 iov[n].iov_base = buffer;
129                 rc = vsnprintf(buffer, sizeof buffer, fmt, args);
130                 if (rc < 0)
131                         rc = 0;
132                 else if ((size_t)rc > sizeof buffer) {
133                         rc = (int)sizeof buffer;
134                         buffer[rc - 1] = buffer[rc - 2]  = buffer[rc - 3] = '.';
135                 }
136                 iov[n++].iov_len = (size_t)rc;
137         }
138         if (file && (!fmt || tty == 1 || loglevel <= Log_Level_Warning)) {
139                 iov[n].iov_base = (void*)&chars[3 + !fmt];
140                 iov[n++].iov_len = 2 - !fmt;
141                 iov[n].iov_base = (void*)file;
142                 iov[n++].iov_len = strlen(file);
143                 iov[n].iov_base = (void*)&chars[2];
144                 iov[n++].iov_len = 1;
145                 if (line) {
146                         iov[n].iov_base = lino;
147                         iov[n++].iov_len = snprintf(lino, sizeof lino, "%d", line);
148                 } else {
149                         iov[n].iov_base = (void*)&chars[1];
150                         iov[n++].iov_len = 1;
151                 }
152                 iov[n].iov_base = (void*)&chars[5];
153                 iov[n++].iov_len = 1;
154                 if (function) {
155                         iov[n].iov_base = (void*)function;
156                         iov[n++].iov_len = strlen(function);
157                 } else {
158                         iov[n].iov_base = (void*)&chars[1];
159                         iov[n++].iov_len = 1;
160                 }
161                 iov[n].iov_base = (void*)&chars[6];
162                 iov[n++].iov_len = 1;
163         }
164         if (n == 2) {
165                 iov[n].iov_base = (void*)&chars[1];
166                 iov[n++].iov_len = 1;
167         }
168         iov[n].iov_base = (void*)&chars[0];
169         iov[n++].iov_len = 1;
170
171         writev(STDERR_FILENO, iov, n);
172
173         errno = saverr;
174 }
175
176 void verbose_set_name(const char *name, int authority)
177 {
178         appname = name;
179         appauthority = authority;
180 }
181
182 #endif
183
184 void verbose(int loglevel, const char *file, int line, const char *function, const char *fmt, ...)
185 {
186         va_list ap;
187
188         va_start(ap, fmt);
189         vverbose(loglevel, file, line, function, fmt, ap);
190         va_end(ap);
191 }
192
193 void vverbose(int loglevel, const char *file, int line, const char *function, const char *fmt, va_list args)
194 {
195         void (*observer)(int loglevel, const char *file, int line, const char *function, const char *fmt, va_list args) = verbose_observer;
196
197         if (!observer)
198                 _vverbose_(loglevel, file, line, function, fmt, args);
199         else {
200                 va_list ap;
201                 va_copy(ap, args);
202                 _vverbose_(loglevel, file, line, function, fmt, args);
203                 observer(loglevel, file, line, function, fmt, ap);
204                 va_end(ap);
205         }
206 }
207