/*  Copyright (C) CZ.NIC, z.s.p.o. and contributors
 *  SPDX-License-Identifier: GPL-2.0-or-later
 *  For more information, see <https://www.knot-dns.cz/>
 */

/*!
 * \brief Logging facility.
 *
 * Supported log levels/priorities:
 * LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, and LOG_DEBUG.
 *
 * \see syslog.h
 */

#pragma once

#include <assert.h>
#include <syslog.h>
#include <stdint.h>
#include <stdbool.h>

#include "libknot/dname.h"
#include "knot/conf/conf.h"

/*! \brief Format for timestamps in log files. */
#define KNOT_LOG_TIME_FORMAT "%Y-%m-%dT%H:%M:%S%z"

/*! \brief Logging targets. */
typedef enum {
	LOG_TARGET_SYSLOG = 0, /*!< System log. */
	LOG_TARGET_STDERR = 1, /*!< Standard error stream. */
	LOG_TARGET_STDOUT = 2, /*!< Standard output stream. */
	LOG_TARGET_FILE   = 3  /*!< Generic logging to a file (unbuffered). */
} log_target_t;

/*! \brief Logging sources. */
typedef enum {
	LOG_SOURCE_SERVER  = 0, /*!< Server module. */
	LOG_SOURCE_CONTROL = 1, /*!< Server control module. */
	LOG_SOURCE_ZONE    = 2, /*!< Zone manipulation module. */
	LOG_SOURCE_QUIC    = 3, /*!< QUIC debug information. */
	LOG_SOURCE_ANY     = 4  /*!< Any module (QUIC not included). */
} log_source_t;

/*! \brief Logging format flags. */
typedef enum {
	LOG_FLAG_NOTIMESTAMP = 1 << 0, /*!< Don't print timestamp prefix. */
	LOG_FLAG_NOINFO      = 1 << 1  /*!< Don't print info level prefix. */
} log_flag_t;

/*!
 * \brief Setup logging subsystem.
 */
void log_init(void);

/*!
 * \brief Close and deinitialize log.
 */
void log_close(void);

/*!
 * \brief Set logging format flag.
 */
void log_flag_set(log_flag_t flag);

/*!
 * \brief Set log levels for given target.
 *
 * \param target  Logging target index.
 * \param src     Logging source.
 * \param levels  Bitmask of specified log levels.
 */
void log_levels_set(log_target_t target, log_source_t src, int levels);

/*!
 * \brief Add log levels to a given target.
 *
 * New levels are added on top of existing, the resulting levels set is
 * "old_levels OR new_levels".
 *
 * \param target  Logging target index.
 * \param src     Logging source.
 * \param levels  Bitmask of specified log levels.
 */
void log_levels_add(log_target_t target, log_source_t src, int levels);

/*!
 * \brief Log message into server category.
 *
 * Function follows printf() format.
 *
 * \note LOG_SOURCE_ANY is not a valid value for the src parameter.
 *
 * \param priority  Message priority.
 * \param src       Message source.
 * \param fmt       Content of the logged message.
 */
void log_fmt(int priority, log_source_t src, const char *fmt, ...)
__attribute__((format(printf, 3, 4)));

/*!
 * \brief Log message into zone category.
 *
 * \see log_fmt
 *
 * \param priority  Message priority.
 * \param src       Message source.
 * \param zone      Zone name in wire format.
 * \param param     Optional key-value parameter for structured logging.
 * \param fmt       Content of the logged message.
 */
void log_fmt_zone(int priority, log_source_t src, const knot_dname_t *zone,
                  const char *param, const char *fmt, ...)
__attribute__((format(printf, 5, 6)));

/*!
 * \brief Log message into zone category.
 *
 * \see log_fmt
 *
 * \param zone  Zone name as an ASCII string.
 * \param priority  Message priority.
 * \param src       Message source.
 * \param fmt       Content of the logged message.
 */
void log_fmt_zone_str(int priority, log_source_t src, const char *zone, const char *fmt, ...)
__attribute__((format(printf, 4, 5)));

/*!
 * \brief Convenient logging macros.
 */
#define log_fatal(msg, ...)   log_fmt(LOG_CRIT,    LOG_SOURCE_SERVER, msg, ##__VA_ARGS__)
#define log_error(msg, ...)   log_fmt(LOG_ERR,     LOG_SOURCE_SERVER, msg, ##__VA_ARGS__)
#define log_warning(msg, ...) log_fmt(LOG_WARNING, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__)
#define log_notice(msg, ...)  log_fmt(LOG_NOTICE,  LOG_SOURCE_SERVER, msg, ##__VA_ARGS__)
#define log_info(msg, ...)    log_fmt(LOG_INFO,    LOG_SOURCE_SERVER, msg, ##__VA_ARGS__)
#define log_debug(msg, ...)   log_fmt(LOG_DEBUG,   LOG_SOURCE_SERVER, msg, ##__VA_ARGS__)

#define log_ctl_fatal(msg, ...)   log_fmt(LOG_CRIT,    LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__)
#define log_ctl_error(msg, ...)   log_fmt(LOG_ERR,     LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__)
#define log_ctl_warning(msg, ...) log_fmt(LOG_WARNING, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__)
#define log_ctl_notice(msg, ...)  log_fmt(LOG_NOTICE,  LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__)
#define log_ctl_info(msg, ...)    log_fmt(LOG_INFO,    LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__)
#define log_ctl_debug(msg, ...)   log_fmt(LOG_DEBUG,   LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__)

#define log_ctl_zone_str_error(zone, msg, ...) log_fmt_zone_str(LOG_ERR,   LOG_SOURCE_CONTROL, zone, msg, ##__VA_ARGS__)
#define log_ctl_zone_str_info(zone, msg, ...)  log_fmt_zone_str(LOG_INFO,  LOG_SOURCE_CONTROL, zone, msg, ##__VA_ARGS__)
#define log_ctl_zone_str_debug(zone, msg, ...) log_fmt_zone_str(LOG_DEBUG, LOG_SOURCE_CONTROL, zone, msg, ##__VA_ARGS__)

#define log_zone_fatal(zone, msg, ...)   log_fmt_zone(LOG_CRIT,    LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__)
#define log_zone_error(zone, msg, ...)   log_fmt_zone(LOG_ERR,     LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__)
#define log_zone_warning(zone, msg, ...) log_fmt_zone(LOG_WARNING, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__)
#define log_zone_notice(zone, msg, ...)  log_fmt_zone(LOG_NOTICE,  LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__)
#define log_zone_info(zone, msg, ...)    log_fmt_zone(LOG_INFO,    LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__)
#define log_zone_debug(zone, msg, ...)   log_fmt_zone(LOG_DEBUG,   LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__)

#define log_zone_str_fatal(zone, msg, ...)   log_fmt_zone_str(LOG_CRIT,    LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__)
#define log_zone_str_error(zone, msg, ...)   log_fmt_zone_str(LOG_ERR,     LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__)
#define log_zone_str_warning(zone, msg, ...) log_fmt_zone_str(LOG_WARNING, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__)
#define log_zone_str_notice(zone, msg, ...)  log_fmt_zone_str(LOG_NOTICE,  LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__)
#define log_zone_str_info(zone, msg, ...)    log_fmt_zone_str(LOG_INFO,    LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__)
#define log_zone_str_debug(zone, msg, ...)   log_fmt_zone_str(LOG_DEBUG,   LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__)

/*!
 * \brief Update open files ownership.
 *
 * \param uid  New owner id.
 * \param gid  New group id.
 *
 * \return Error code, KNOT_EOK if success.
 */
int log_update_privileges(int uid, int gid);

/*!
 * \brief Setup logging facilities from config.
 */
void log_reconfigure(conf_t *conf);

/*!
 * \brief Check if debug logging is enabled.
 */
bool log_enabled_debug(void);

/*!
 * \brief Check if QUIC debug logging is enabled.
 */
bool log_enabled_quic_debug(void);
