/*
 * Logging - VTY code
 * Copyright (C) 2019 Cumulus Networks, Inc.
 *                    Stephen Worley
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; see the file COPYING; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <zebra.h>

#include "lib/log_vty.h"
#include "command.h"
#include "lib/log.h"
#include "lib/zlog_targets.h"
#include "lib/lib_errors.h"
#include "lib/printfrr.h"

#ifndef VTYSH_EXTRACT_PL
#include "lib/log_vty_clippy.c"
#endif

#define ZLOG_MAXLVL(a, b) MAX(a, b)

DEFINE_HOOK(zlog_rotate, (), ())

static const int log_default_lvl = LOG_DEBUG;

static int log_config_stdout_lvl = ZLOG_DISABLED;
static int log_config_syslog_lvl = ZLOG_DISABLED;
static int log_cmdline_stdout_lvl = ZLOG_DISABLED;
static int log_cmdline_syslog_lvl = ZLOG_DISABLED;

static struct zlog_cfg_file zt_file_cmdline = {
	.prio_min = ZLOG_DISABLED,
};
static struct zlog_cfg_file zt_file = {
	.prio_min = ZLOG_DISABLED,
};
static struct zlog_cfg_file zt_stdout = {
	.prio_min = ZLOG_DISABLED,
};
static struct zlog_cfg_filterfile zt_filterfile = {
	.parent = {
		.prio_min = ZLOG_DISABLED,
	},
};

static const char *zlog_progname;
static const char *zlog_protoname;

static const struct facility_map {
	int facility;
	const char *name;
	size_t match;
} syslog_facilities[] = {
	{LOG_KERN, "kern", 1},
	{LOG_USER, "user", 2},
	{LOG_MAIL, "mail", 1},
	{LOG_DAEMON, "daemon", 1},
	{LOG_AUTH, "auth", 1},
	{LOG_SYSLOG, "syslog", 1},
	{LOG_LPR, "lpr", 2},
	{LOG_NEWS, "news", 1},
	{LOG_UUCP, "uucp", 2},
	{LOG_CRON, "cron", 1},
#ifdef LOG_FTP
	{LOG_FTP, "ftp", 1},
#endif
	{LOG_LOCAL0, "local0", 6},
	{LOG_LOCAL1, "local1", 6},
	{LOG_LOCAL2, "local2", 6},
	{LOG_LOCAL3, "local3", 6},
	{LOG_LOCAL4, "local4", 6},
	{LOG_LOCAL5, "local5", 6},
	{LOG_LOCAL6, "local6", 6},
	{LOG_LOCAL7, "local7", 6},
	{0, NULL, 0},
};

static const char * const zlog_priority[] = {
	"emergencies",   "alerts",	"critical",  "errors", "warnings",
	"notifications", "informational", "debugging", NULL,
};

static const char *facility_name(int facility)
{
	const struct facility_map *fm;

	for (fm = syslog_facilities; fm->name; fm++)
		if (fm->facility == facility)
			return fm->name;
	return "";
}

static int facility_match(const char *str)
{
	const struct facility_map *fm;

	for (fm = syslog_facilities; fm->name; fm++)
		if (!strncmp(str, fm->name, fm->match))
			return fm->facility;
	return -1;
}

int log_level_match(const char *s)
{
	int level;

	for (level = 0; zlog_priority[level] != NULL; level++)
		if (!strncmp(s, zlog_priority[level], 2))
			return level;
	return ZLOG_DISABLED;
}

void zlog_rotate(void)
{
	zlog_file_rotate(&zt_file);
	zlog_file_rotate(&zt_filterfile.parent);
	hook_call(zlog_rotate);
}


void log_show_syslog(struct vty *vty)
{
	int level = zlog_syslog_get_prio_min();

	vty_out(vty, "Syslog logging: ");
	if (level == ZLOG_DISABLED)
		vty_out(vty, "disabled\n");
	else
		vty_out(vty, "level %s, facility %s, ident %s\n",
			zlog_priority[level],
			facility_name(zlog_syslog_get_facility()),
			zlog_progname);
}

DEFUN (show_logging,
       show_logging_cmd,
       "show logging",
       SHOW_STR
       "Show current logging configuration\n")
{
	log_show_syslog(vty);

	vty_out(vty, "Stdout logging: ");
	if (zt_stdout.prio_min == ZLOG_DISABLED)
		vty_out(vty, "disabled");
	else
		vty_out(vty, "level %s",
			zlog_priority[zt_stdout.prio_min]);
	vty_out(vty, "\n");

	vty_out(vty, "File logging: ");
	if (zt_file.prio_min == ZLOG_DISABLED || !zt_file.filename)
		vty_out(vty, "disabled");
	else
		vty_out(vty, "level %s, filename %s",
			zlog_priority[zt_file.prio_min], zt_file.filename);
	vty_out(vty, "\n");

	if (zt_filterfile.parent.prio_min != ZLOG_DISABLED
	    && zt_filterfile.parent.filename)
		vty_out(vty, "Filtered-file logging: level %s, filename %s\n",
			zlog_priority[zt_filterfile.parent.prio_min],
			zt_filterfile.parent.filename);

	if (log_cmdline_syslog_lvl != ZLOG_DISABLED)
		vty_out(vty,
			"From command line: \"--log syslog --log-level %s\"\n",
			zlog_priority[log_cmdline_syslog_lvl]);
	if (log_cmdline_stdout_lvl != ZLOG_DISABLED)
		vty_out(vty,
			"From command line: \"--log stdout --log-level %s\"\n",
			zlog_priority[log_cmdline_stdout_lvl]);
	if (zt_file_cmdline.prio_min != ZLOG_DISABLED)
		vty_out(vty,
			"From command line: \"--log file:%s --log-level %s\"\n",
			zt_file_cmdline.filename,
			zlog_priority[zt_file_cmdline.prio_min]);

	vty_out(vty, "Protocol name: %s\n", zlog_protoname);
	vty_out(vty, "Record priority: %s\n",
		(zt_file.record_priority ? "enabled" : "disabled"));
	vty_out(vty, "Timestamp precision: %d\n", zt_file.ts_subsec);
	return CMD_SUCCESS;
}

DEFPY (config_log_stdout,
       config_log_stdout_cmd,
       "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
       "Logging control\n"
       "Set stdout logging level\n"
       LOG_LEVEL_DESC)
{
	int level;

	if (levelarg) {
		level = log_level_match(levelarg);
		if (level == ZLOG_DISABLED)
			return CMD_ERR_NO_MATCH;
	} else
		level = log_default_lvl;

	log_config_stdout_lvl = level;
	zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
					 log_cmdline_stdout_lvl);
	zlog_file_set_other(&zt_stdout);
	return CMD_SUCCESS;
}

DEFUN (no_config_log_stdout,
       no_config_log_stdout_cmd,
       "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
       NO_STR
       "Logging control\n"
       "Cancel logging to stdout\n"
       LOG_LEVEL_DESC)
{
	log_config_stdout_lvl = ZLOG_DISABLED;
	zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
					 log_cmdline_stdout_lvl);
	zlog_file_set_other(&zt_stdout);
	return CMD_SUCCESS;
}

DEFUN_HIDDEN (config_log_monitor,
       config_log_monitor_cmd,
       "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
       "Logging control\n"
       "Set terminal line (monitor) logging level\n"
       LOG_LEVEL_DESC)
{
	vty_out(vty, "%% \"log monitor\" is deprecated and does nothing.\n");
	return CMD_SUCCESS;
}

DEFUN_HIDDEN (no_config_log_monitor,
       no_config_log_monitor_cmd,
       "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
       NO_STR
       "Logging control\n"
       "Disable terminal line (monitor) logging\n"
       LOG_LEVEL_DESC)
{
	return CMD_SUCCESS;
}

static int set_log_file(struct zlog_cfg_file *target, struct vty *vty,
			const char *fname, int loglevel)
{
	char path[MAXPATHLEN + 1];
	const char *fullpath;
	bool ok;


	/* Path detection. */
	if (!IS_DIRECTORY_SEP(*fname)) {
		char cwd[MAXPATHLEN + 1];

		cwd[MAXPATHLEN] = '\0';

		if (getcwd(cwd, MAXPATHLEN) == NULL) {
			flog_err_sys(EC_LIB_SYSTEM_CALL,
				     "config_log_file: Unable to alloc mem!");
			return CMD_WARNING_CONFIG_FAILED;
		}

		int pr = snprintf(path, sizeof(path), "%s/%s", cwd, fname);
		if (pr < 0 || (unsigned int)pr >= sizeof(path)) {
			flog_err_sys(
				EC_LIB_SYSTEM_CALL,
				"%s: Path too long ('%s/%s'); system maximum is %u",
				__func__, cwd, fname, MAXPATHLEN);
			return CMD_WARNING_CONFIG_FAILED;
		}

		fullpath = path;
	} else
		fullpath = fname;

	target->prio_min = loglevel;
	ok = zlog_file_set_filename(target, fullpath);

	if (!ok) {
		if (vty)
			vty_out(vty, "can't open logfile %s\n", fname);
		return CMD_WARNING_CONFIG_FAILED;
	}
	return CMD_SUCCESS;
}

void command_setup_early_logging(const char *dest, const char *level)
{
	int nlevel;
	char *sep;
	int len;
	char type[8];

	if (level) {
		nlevel = log_level_match(level);

		if (nlevel == ZLOG_DISABLED) {
			fprintf(stderr, "invalid log level \"%s\"\n", level);
			exit(1);
		}
	} else
		nlevel = log_default_lvl;

	if (!dest)
		return;

	sep = strchr(dest, ':');
	len = sep ? (int)(sep - dest) : (int)strlen(dest);

	snprintfrr(type, sizeof(type), "%.*s", len, dest);

	if (strcmp(type, "stdout") == 0) {
		log_cmdline_stdout_lvl = nlevel;
		zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
						 log_cmdline_stdout_lvl);
		zlog_file_set_other(&zt_stdout);
		return;
	}
	if (strcmp(type, "syslog") == 0) {
		log_cmdline_syslog_lvl = nlevel;
		zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
						     log_cmdline_syslog_lvl));
		return;
	}
	if (strcmp(type, "file") == 0 && sep) {
		sep++;
		set_log_file(&zt_file_cmdline, NULL, sep, nlevel);
		return;
	}

	fprintf(stderr, "invalid log target \"%s\" (\"%s\")\n", type, dest);
	exit(1);
}

DEFUN (clear_log_cmdline,
       clear_log_cmdline_cmd,
       "clear log cmdline-targets",
       CLEAR_STR
       "Logging control\n"
       "Disable log targets specified at startup by --log option\n")
{
	zt_file_cmdline.prio_min = ZLOG_DISABLED;
	zlog_file_set_other(&zt_file_cmdline);

	log_cmdline_syslog_lvl = ZLOG_DISABLED;
	zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
					     log_cmdline_syslog_lvl));

	log_cmdline_stdout_lvl = ZLOG_DISABLED;
	zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
					 log_cmdline_stdout_lvl);
	zlog_file_set_other(&zt_stdout);

	return CMD_SUCCESS;
}

DEFPY (config_log_file,
       config_log_file_cmd,
       "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
       "Logging control\n"
       "Logging to file\n"
       "Logging filename\n"
       LOG_LEVEL_DESC)
{
	int level = log_default_lvl;

	if (levelarg) {
		level = log_level_match(levelarg);
		if (level == ZLOG_DISABLED)
			return CMD_ERR_NO_MATCH;
	}
	return set_log_file(&zt_file, vty, filename, level);
}

DEFUN (no_config_log_file,
       no_config_log_file_cmd,
       "no log file [FILENAME [LEVEL]]",
       NO_STR
       "Logging control\n"
       "Cancel logging to file\n"
       "Logging file name\n"
       "Logging level\n")
{
	zt_file.prio_min = ZLOG_DISABLED;
	zlog_file_set_other(&zt_file);
	return CMD_SUCCESS;
}

DEFPY (config_log_syslog,
       config_log_syslog_cmd,
       "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
       "Logging control\n"
       "Set syslog logging level\n"
       LOG_LEVEL_DESC)
{
	int level;

	if (levelarg) {
		level = log_level_match(levelarg);

		if (level == ZLOG_DISABLED)
			return CMD_ERR_NO_MATCH;
	} else
		level = log_default_lvl;

	log_config_syslog_lvl = level;
	zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
					     log_cmdline_syslog_lvl));
	return CMD_SUCCESS;
}

DEFUN (no_config_log_syslog,
       no_config_log_syslog_cmd,
       "no log syslog [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>] [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
       NO_STR
       "Logging control\n"
       "Cancel logging to syslog\n"
       LOG_FACILITY_DESC
       LOG_LEVEL_DESC)
{
	log_config_syslog_lvl = ZLOG_DISABLED;
	zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
					     log_cmdline_syslog_lvl));
	return CMD_SUCCESS;
}

DEFPY (config_log_facility,
       config_log_facility_cmd,
       "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>$facilityarg",
       "Logging control\n"
       "Facility parameter for syslog messages\n"
       LOG_FACILITY_DESC)
{
	int facility = facility_match(facilityarg);

	zlog_syslog_set_facility(facility);
	return CMD_SUCCESS;
}

DEFUN (no_config_log_facility,
       no_config_log_facility_cmd,
       "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
       NO_STR
       "Logging control\n"
       "Reset syslog facility to default (daemon)\n"
       LOG_FACILITY_DESC)
{
	zlog_syslog_set_facility(LOG_DAEMON);
	return CMD_SUCCESS;
}

DEFUN (config_log_record_priority,
       config_log_record_priority_cmd,
       "log record-priority",
       "Logging control\n"
       "Log the priority of the message within the message\n")
{
	zt_file.record_priority = true;
	zlog_file_set_other(&zt_file);
	zt_stdout.record_priority = true;
	zlog_file_set_other(&zt_stdout);
	zt_filterfile.parent.record_priority = true;
	zlog_file_set_other(&zt_filterfile.parent);
	return CMD_SUCCESS;
}

DEFUN (no_config_log_record_priority,
       no_config_log_record_priority_cmd,
       "no log record-priority",
       NO_STR
       "Logging control\n"
       "Do not log the priority of the message within the message\n")
{
	zt_file.record_priority = false;
	zlog_file_set_other(&zt_file);
	zt_stdout.record_priority = false;
	zlog_file_set_other(&zt_stdout);
	zt_filterfile.parent.record_priority = false;
	zlog_file_set_other(&zt_filterfile.parent);
	return CMD_SUCCESS;
}

DEFPY (config_log_timestamp_precision,
       config_log_timestamp_precision_cmd,
       "log timestamp precision (0-6)",
       "Logging control\n"
       "Timestamp configuration\n"
       "Set the timestamp precision\n"
       "Number of subsecond digits\n")
{
	zt_file.ts_subsec = precision;
	zlog_file_set_other(&zt_file);
	zt_stdout.ts_subsec = precision;
	zlog_file_set_other(&zt_stdout);
	zt_filterfile.parent.ts_subsec = precision;
	zlog_file_set_other(&zt_filterfile.parent);
	return CMD_SUCCESS;
}

DEFUN (no_config_log_timestamp_precision,
       no_config_log_timestamp_precision_cmd,
       "no log timestamp precision [(0-6)]",
       NO_STR
       "Logging control\n"
       "Timestamp configuration\n"
       "Reset the timestamp precision to the default value of 0\n"
       "Number of subsecond digits\n")
{
	zt_file.ts_subsec = 0;
	zlog_file_set_other(&zt_file);
	zt_stdout.ts_subsec = 0;
	zlog_file_set_other(&zt_stdout);
	zt_filterfile.parent.ts_subsec = 0;
	zlog_file_set_other(&zt_filterfile.parent);
	return CMD_SUCCESS;
}

DEFPY (config_log_filterfile,
       config_log_filterfile_cmd,
       "log filtered-file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
       "Logging control\n"
       "Logging to file with string filter\n"
       "Logging filename\n"
       LOG_LEVEL_DESC)
{
	int level = log_default_lvl;

	if (levelarg) {
		level = log_level_match(levelarg);
		if (level == ZLOG_DISABLED)
			return CMD_ERR_NO_MATCH;
	}
	return set_log_file(&zt_filterfile.parent, vty, filename, level);
}

DEFUN (no_config_log_filterfile,
       no_config_log_filterfile_cmd,
       "no log filtered-file [FILENAME [LEVEL]]",
       NO_STR
       "Logging control\n"
       "Cancel logging to file with string filter\n"
       "Logging file name\n"
       "Logging level\n")
{
	zt_filterfile.parent.prio_min = ZLOG_DISABLED;
	zlog_file_set_other(&zt_filterfile.parent);
	return CMD_SUCCESS;
}

DEFPY (log_filter,
       log_filter_cmd,
       "[no] log-filter WORD$filter",
       NO_STR
       FILTER_LOG_STR
       "String to filter by\n")
{
	int ret = 0;

	if (no)
		ret = zlog_filter_del(filter);
	else
		ret = zlog_filter_add(filter);

	if (ret == 1) {
		vty_out(vty, "%% filter table full\n");
		return CMD_WARNING;
	} else if (ret != 0) {
		vty_out(vty, "%% failed to %s log filter\n",
			(no ? "remove" : "apply"));
		return CMD_WARNING;
	}

	vty_out(vty, " %s\n", filter);
	return CMD_SUCCESS;
}

/* Clear all log filters */
DEFPY (log_filter_clear,
       log_filter_clear_cmd,
       "clear log-filter",
       CLEAR_STR
       FILTER_LOG_STR)
{
	zlog_filter_clear();
	return CMD_SUCCESS;
}

/* Show log filter */
DEFPY (show_log_filter,
       show_log_filter_cmd,
       "show log-filter",
       SHOW_STR
       FILTER_LOG_STR)
{
	char log_filters[ZLOG_FILTERS_MAX * (ZLOG_FILTER_LENGTH_MAX + 3)] = "";
	int len = 0;

	len = zlog_filter_dump(log_filters, sizeof(log_filters));

	if (len == -1) {
		vty_out(vty, "%% failed to get filters\n");
		return CMD_WARNING;
	}

	if (len != 0)
		vty_out(vty, "%s", log_filters);

	return CMD_SUCCESS;
}

void log_config_write(struct vty *vty)
{
	bool show_cmdline_hint = false;

	if (zt_file.prio_min != ZLOG_DISABLED && zt_file.filename) {
		vty_out(vty, "log file %s", zt_file.filename);

		if (zt_file.prio_min != log_default_lvl)
			vty_out(vty, " %s", zlog_priority[zt_file.prio_min]);
		vty_out(vty, "\n");
	}

	if (zt_filterfile.parent.prio_min != ZLOG_DISABLED
	    && zt_filterfile.parent.filename) {
		vty_out(vty, "log filtered-file %s",
			zt_filterfile.parent.filename);

		if (zt_filterfile.parent.prio_min != log_default_lvl)
			vty_out(vty, " %s",
				zlog_priority[zt_filterfile.parent.prio_min]);
		vty_out(vty, "\n");
	}

	if (log_config_stdout_lvl != ZLOG_DISABLED) {
		vty_out(vty, "log stdout");

		if (log_config_stdout_lvl != log_default_lvl)
			vty_out(vty, " %s",
				zlog_priority[log_config_stdout_lvl]);
		vty_out(vty, "\n");
	}

	if (log_config_syslog_lvl != ZLOG_DISABLED) {
		vty_out(vty, "log syslog");

		if (log_config_syslog_lvl != log_default_lvl)
			vty_out(vty, " %s",
				zlog_priority[log_config_syslog_lvl]);
		vty_out(vty, "\n");
	}

	if (log_cmdline_syslog_lvl != ZLOG_DISABLED) {
		vty_out(vty,
			"! \"log syslog %s\" enabled by \"--log\" startup option\n",
			zlog_priority[log_cmdline_syslog_lvl]);
		show_cmdline_hint = true;
	}
	if (log_cmdline_stdout_lvl != ZLOG_DISABLED) {
		vty_out(vty,
			"! \"log stdout %s\" enabled by \"--log\" startup option\n",
			zlog_priority[log_cmdline_stdout_lvl]);
		show_cmdline_hint = true;
	}
	if (zt_file_cmdline.prio_min != ZLOG_DISABLED) {
		vty_out(vty,
			"! \"log file %s %s\" enabled by \"--log\" startup option\n",
			zt_file_cmdline.filename,
			zlog_priority[zt_file_cmdline.prio_min]);
		show_cmdline_hint = true;
	}
	if (show_cmdline_hint)
		vty_out(vty,
			"! use \"clear log cmdline-targets\" to remove this target\n");

	if (zlog_syslog_get_facility() != LOG_DAEMON)
		vty_out(vty, "log facility %s\n",
			facility_name(zlog_syslog_get_facility()));

	if (zt_file.record_priority == 1)
		vty_out(vty, "log record-priority\n");

	if (zt_file.ts_subsec > 0)
		vty_out(vty, "log timestamp precision %d\n",
			zt_file.ts_subsec);
}

static int log_vty_init(const char *progname, const char *protoname,
			 unsigned short instance, uid_t uid, gid_t gid)
{
	zlog_progname = progname;
	zlog_protoname = protoname;

	zlog_filterfile_init(&zt_filterfile);

	zlog_file_set_fd(&zt_stdout, STDOUT_FILENO);
	return 0;
}

__attribute__((_CONSTRUCTOR(475))) static void log_vty_preinit(void)
{
	hook_register(zlog_init, log_vty_init);
}

void log_cmd_init(void)
{
	install_element(VIEW_NODE, &show_logging_cmd);
	install_element(ENABLE_NODE, &clear_log_cmdline_cmd);

	install_element(CONFIG_NODE, &config_log_stdout_cmd);
	install_element(CONFIG_NODE, &no_config_log_stdout_cmd);
	install_element(CONFIG_NODE, &config_log_monitor_cmd);
	install_element(CONFIG_NODE, &no_config_log_monitor_cmd);
	install_element(CONFIG_NODE, &config_log_file_cmd);
	install_element(CONFIG_NODE, &no_config_log_file_cmd);
	install_element(CONFIG_NODE, &config_log_syslog_cmd);
	install_element(CONFIG_NODE, &no_config_log_syslog_cmd);
	install_element(CONFIG_NODE, &config_log_facility_cmd);
	install_element(CONFIG_NODE, &no_config_log_facility_cmd);
	install_element(CONFIG_NODE, &config_log_record_priority_cmd);
	install_element(CONFIG_NODE, &no_config_log_record_priority_cmd);
	install_element(CONFIG_NODE, &config_log_timestamp_precision_cmd);
	install_element(CONFIG_NODE, &no_config_log_timestamp_precision_cmd);

	install_element(VIEW_NODE, &show_log_filter_cmd);
	install_element(CONFIG_NODE, &log_filter_cmd);
	install_element(CONFIG_NODE, &log_filter_clear_cmd);
	install_element(CONFIG_NODE, &config_log_filterfile_cmd);
	install_element(CONFIG_NODE, &no_config_log_filterfile_cmd);
}
