sphinx: better logging and verbosity handling
[profile/ivi/speech-recognition.git] / src / plugins / speech-to-text / sphinx / logger.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #define __USE_GNU                        /* F_SETPIPE_SZ */
4 #include <fcntl.h>
5 #include <string.h>
6 #include <errno.h>
7
8 #include <murphy/common/log.h>
9 #include <murphy/common/mainloop.h>
10
11 #include "sphinx-plugin.h"
12
13 #define SPHINX_INFO   "INFO: "
14 #define SPHINX_ERROR  "ERROR: "
15 #define SPHINX_WARN   "WARNING: "
16 #define SPHINX_SYSERR "SYSTEM_ERROR: "
17 #define SPHINX_FATAL  "FATAL_ERROR: "
18
19 #define RD 0
20 #define WR 1
21
22 typedef struct {
23     int             fd[2];               /* log message pipe */
24     FILE           *fp;                  /* log write stream */
25     mrp_io_watch_t *w;                   /* log I/O watch */
26     char            buf[4096];           /* log line buffer */
27     ssize_t         n;
28 } logger_t;
29
30
31 static ssize_t pull_log(logger_t *logger)
32 {
33     char    *p;
34     ssize_t  n, l;
35
36     p = logger->buf + logger->n;
37     l = sizeof(logger->buf) - logger->n - 1;
38
39     n = read(logger->fd[RD], p, l);
40
41     if (n <= 0)
42         return n;
43
44     p[n] = '\0';
45     logger->n += n;
46
47     return logger->n;
48 }
49
50
51 static void push_log(logger_t *logger)
52 {
53     char    *b, *e, *lb, *le, *file, lvl, name[1024];
54     int      line, len, nlen;
55     ssize_t  n;
56
57     name[sizeof(name) - 1] = '\0';
58
59     lvl  = 0;
60     file = NULL;
61
62     while (logger->n > 0) {
63         b = logger->buf;
64
65         if (!strncmp(b, SPHINX_INFO  , len = sizeof(SPHINX_INFO  ) - 1) ||
66             !strncmp(b, SPHINX_WARN  , len = sizeof(SPHINX_WARN  ) - 1) ||
67             !strncmp(b, SPHINX_ERROR , len = sizeof(SPHINX_ERROR ) - 1) ||
68             !strncmp(b, SPHINX_SYSERR, len = sizeof(SPHINX_SYSERR) - 1) ||
69             !strncmp(b, SPHINX_FATAL , len = sizeof(SPHINX_FATAL ) - 1)) {
70             lvl  = *b;
71             b   += len;
72             lb  = strchr(b, '(');
73         }
74         else
75             lvl = 0;
76
77         if ((e = strchr(b, '\n')) == NULL) {
78             if (logger->n >= sizeof(logger->buf) - 1) {
79                 mrp_log_warning("Discarding too long sphinx log buffer.");
80                 logger->n = 0;
81             }
82             return;
83         }
84
85         if (lb != NULL) {
86             line = (int)strtoul(lb + 1, &le, 10);
87
88             if (lb != NULL && *le == ')') {
89                 nlen = lb - b;
90                 snprintf(name, sizeof(name) - 1, "%*.*s", nlen, nlen, b);
91                 file = name;
92                 b = le + 1;
93                 if (b[0] == ':' && b[1] == ' ')
94                     b += 2;
95             }
96         }
97         else {
98             if (file == NULL)
99                 file = "<unknown-file>";
100         }
101
102         n = e - b;
103
104         switch (lvl) {
105         case 'I':
106         default:
107             mrp_debug_at("sphinx", line, file, "%*.*s", n, n, b);
108             break;
109         case 'W':
110             mrp_log_msg(MRP_LOG_WARNING, file, line, "sphinx", "%*.*s", n, n, b);
111             break;
112         case 'E':
113         case 'S':
114         case 'F':
115             mrp_log_msg(MRP_LOG_ERROR, file, line, "sphinx", "%*.*s", n, n, b);
116             break;
117         }
118
119         b = e + 1;
120         n = logger->n - (b - logger->buf);
121
122         if (n <= 0)
123             logger->n = 0;
124         else {
125             memmove(logger->buf, b, n);
126             logger->n = n;
127             logger->buf[n] = '\0';
128         }
129     }
130 }
131
132
133 static void log_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
134                    void *user_data)
135 {
136     logger_t *logger = (logger_t *)user_data;
137
138     MRP_UNUSED(fd);
139
140     if (events & MRP_IO_EVENT_IN)
141         while (pull_log(logger) > 0)
142             push_log(logger);
143
144     if (events & MRP_IO_EVENT_HUP)
145         mrp_del_io_watch(w);
146 }
147
148
149 FILE *logger_create(context_t *ctx)
150 {
151     static logger_t  logger = { { -1, -1 }, NULL, 0 };
152     mrp_mainloop_t  *ml     = plugin_get_mainloop(ctx->plugin);
153     mrp_io_event_t   events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
154
155     if (logger.fp != NULL)
156         return logger.fp;
157
158     if (pipe(logger.fd) < 0) {
159         mrp_log_error("Failed to create sphinx logging pipe (error %d: %s).",
160                       errno, strerror(errno));
161         goto fail;
162     }
163
164     if ((logger.fp = fdopen(logger.fd[WR], "w")) == NULL)
165         goto fail;
166
167     setvbuf(logger.fp, NULL, _IOLBF, 0);
168
169     fcntl(logger.fd[WR], F_SETPIPE_SZ, 512 * 1024);
170     fcntl(logger.fd[WR], F_SETFL, O_NONBLOCK);
171     fcntl(logger.fd[RD], F_SETFL, O_NONBLOCK);
172
173     logger.w = mrp_add_io_watch(ml, logger.fd[RD], events, log_cb, &logger);
174
175     if (logger.w != NULL)
176         return logger.fp;
177
178     /* fallthru */
179
180  fail:
181     close(logger.fd[0]);
182     close(logger.fd[1]);
183     if (logger.fp != NULL)
184         fclose(logger.fp);
185
186     logger.fd[0] = -1;
187     logger.fd[1] = -1;
188     logger.fp = NULL;
189
190     return NULL;
191 }