You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

logger_syslog.c 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /*
  2. * Copyright 2024 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "config.h"
  17. #include "logger.h"
  18. #include "libserver/cfg_file.h"
  19. #include "logger_private.h"
  20. #define SYSLOG_LOG_QUARK g_quark_from_static_string("syslog_logger")
  21. struct rspamd_syslog_logger_priv {
  22. int log_facility;
  23. };
  24. #ifdef HAVE_SYSLOG_H
  25. #include <syslog.h>
  26. void *
  27. rspamd_log_syslog_init(rspamd_logger_t *logger, struct rspamd_config *cfg,
  28. uid_t uid, gid_t gid, GError **err)
  29. {
  30. struct rspamd_syslog_logger_priv *priv;
  31. if (!cfg) {
  32. g_set_error(err, SYSLOG_LOG_QUARK, EINVAL,
  33. "no log config specified");
  34. return NULL;
  35. }
  36. priv = g_malloc0(sizeof(*priv));
  37. priv->log_facility = cfg->log_facility;
  38. openlog("rspamd", LOG_CONS | LOG_NDELAY | LOG_PID, priv->log_facility);
  39. return priv;
  40. }
  41. void rspamd_log_syslog_dtor(rspamd_logger_t *logger, gpointer arg)
  42. {
  43. struct rspamd_syslog_logger_priv *priv = (struct rspamd_syslog_logger_priv *) arg;
  44. closelog();
  45. g_free(priv);
  46. }
  47. bool rspamd_log_syslog_log(const char *module, const char *id,
  48. const char *function,
  49. int level_flags,
  50. const char *message,
  51. gsize mlen,
  52. rspamd_logger_t *rspamd_log,
  53. gpointer arg)
  54. {
  55. static const struct {
  56. GLogLevelFlags glib_level;
  57. int syslog_level;
  58. } levels_match[] = {
  59. {G_LOG_LEVEL_DEBUG, LOG_DEBUG},
  60. {G_LOG_LEVEL_INFO, LOG_INFO},
  61. {G_LOG_LEVEL_WARNING, LOG_WARNING},
  62. {G_LOG_LEVEL_CRITICAL, LOG_ERR}};
  63. unsigned i;
  64. int syslog_level;
  65. if (!(level_flags & RSPAMD_LOG_FORCED) && !rspamd_log->enabled) {
  66. return false;
  67. }
  68. /* Detect level */
  69. syslog_level = LOG_DEBUG;
  70. for (i = 0; i < G_N_ELEMENTS(levels_match); i++) {
  71. if (level_flags & levels_match[i].glib_level) {
  72. syslog_level = levels_match[i].syslog_level;
  73. break;
  74. }
  75. }
  76. bool log_json = (rspamd_log->flags & RSPAMD_LOG_FLAG_JSON);
  77. /* Ensure safety as %.*s is used */
  78. char idbuf[RSPAMD_LOG_ID_LEN + 1];
  79. if (id != NULL) {
  80. rspamd_strlcpy(idbuf, id, RSPAMD_LOG_ID_LEN + 1);
  81. }
  82. else {
  83. idbuf[0] = '\0';
  84. }
  85. if (log_json) {
  86. long now = rspamd_get_calendar_ticks();
  87. if (rspamd_memcspn(message, "\"\\\r\n\b\t\v", mlen) == mlen) {
  88. /* Fast path */
  89. syslog(syslog_level, "{\"ts\": %ld, "
  90. "\"pid\": %d, "
  91. "\"severity\": \"%s\", "
  92. "\"worker_type\": \"%s\", "
  93. "\"id\": \"%s\", "
  94. "\"module\": \"%s\", "
  95. "\"function\": \"%s\", "
  96. "\"message\": \"%.*s\"}",
  97. now,
  98. (int) rspamd_log->pid,
  99. rspamd_get_log_severity_string(level_flags),
  100. rspamd_log->process_type,
  101. idbuf,
  102. module != NULL ? module : "",
  103. function != NULL ? function : "",
  104. (int) mlen, message);
  105. }
  106. else {
  107. /* Escaped version */
  108. /* We need to do JSON escaping of the quotes */
  109. const char *p, *end = message + mlen;
  110. long escaped_len;
  111. for (p = message, escaped_len = 0; p < end; p++, escaped_len++) {
  112. switch (*p) {
  113. case '\v':
  114. case '\0':
  115. escaped_len += 5;
  116. break;
  117. case '\\':
  118. case '"':
  119. case '\n':
  120. case '\r':
  121. case '\b':
  122. case '\t':
  123. escaped_len++;
  124. break;
  125. default:
  126. break;
  127. }
  128. }
  129. char *dst = g_malloc(escaped_len + 1);
  130. char *d;
  131. for (p = message, d = dst; p < end; p++, d++) {
  132. switch (*p) {
  133. case '\n':
  134. *d++ = '\\';
  135. *d = 'n';
  136. break;
  137. case '\r':
  138. *d++ = '\\';
  139. *d = 'r';
  140. break;
  141. case '\b':
  142. *d++ = '\\';
  143. *d = 'b';
  144. break;
  145. case '\t':
  146. *d++ = '\\';
  147. *d = 't';
  148. break;
  149. case '\f':
  150. *d++ = '\\';
  151. *d = 'f';
  152. break;
  153. case '\0':
  154. *d++ = '\\';
  155. *d++ = 'u';
  156. *d++ = '0';
  157. *d++ = '0';
  158. *d++ = '0';
  159. *d = '0';
  160. break;
  161. case '\v':
  162. *d++ = '\\';
  163. *d++ = 'u';
  164. *d++ = '0';
  165. *d++ = '0';
  166. *d++ = '0';
  167. *d = 'B';
  168. break;
  169. case '\\':
  170. *d++ = '\\';
  171. *d = '\\';
  172. break;
  173. case '"':
  174. *d++ = '\\';
  175. *d = '"';
  176. break;
  177. default:
  178. *d = *p;
  179. break;
  180. }
  181. }
  182. *d = '\0';
  183. syslog(syslog_level, "{\"ts\": %ld, "
  184. "\"pid\": %d, "
  185. "\"severity\": \"%s\", "
  186. "\"worker_type\": \"%s\", "
  187. "\"id\": \"%s\", "
  188. "\"module\": \"%s\", "
  189. "\"function\": \"%s\", "
  190. "\"message\": \"%s\"}",
  191. now,
  192. (int) rspamd_log->pid,
  193. rspamd_get_log_severity_string(level_flags),
  194. rspamd_log->process_type,
  195. idbuf,
  196. module != NULL ? module : "",
  197. function != NULL ? function : "",
  198. dst);
  199. g_free(dst);
  200. }
  201. }
  202. else {
  203. syslog(syslog_level, "<%s>; %s; %s: %.*s",
  204. idbuf,
  205. module != NULL ? module : "",
  206. function != NULL ? function : "",
  207. (int) mlen, message);
  208. }
  209. return true;
  210. }
  211. #else
  212. void *
  213. rspamd_log_syslog_init(rspamd_logger_t *logger, struct rspamd_config *cfg,
  214. uid_t uid, gid_t gid, GError **err)
  215. {
  216. g_set_error(err, SYSLOG_LOG_QUARK, EINVAL, "syslog support is not compiled in");
  217. return NULL;
  218. }
  219. bool rspamd_log_syslog_log(const char *module, const char *id,
  220. const char *function,
  221. int level_flags,
  222. const char *message,
  223. gsize mlen,
  224. rspamd_logger_t *rspamd_log,
  225. gpointer arg)
  226. {
  227. return false;
  228. }
  229. void rspamd_log_syslog_dtor(rspamd_logger_t *logger, gpointer arg)
  230. {
  231. /* Left blank intentionally */
  232. }
  233. #endif
  234. void *
  235. rspamd_log_syslog_reload(rspamd_logger_t *logger, struct rspamd_config *cfg,
  236. gpointer arg, uid_t uid, gid_t gid, GError **err)
  237. {
  238. struct rspamd_syslog_logger_priv *npriv;
  239. npriv = rspamd_log_syslog_init(logger, cfg, uid, gid, err);
  240. if (npriv) {
  241. /* Close old */
  242. rspamd_log_syslog_dtor(logger, arg);
  243. }
  244. return npriv;
  245. }