log.cpp 3.92 KB
Newer Older
1
2
3
/** Logging and debugging routines
 *
 * @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
Steffen Vogel's avatar
Steffen Vogel committed
4
 * @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 * @license GNU General Public License (version 3)
 *
 * VILLAScommon
 *
 * 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 3 of the License, or
 * 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.  If not, see <http://www.gnu.org/licenses/>.
 *********************************************************************************/

23
24
25
#include <list>
#include <algorithm>

26
27
28
29
30
31
32
33
34
35
36
37
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/syslog_sink.h>
#include <spdlog/sinks/basic_file_sink.h>

#include <villas/log.hpp>
#include <villas/exceptions.hpp>

using namespace villas;

/** The global log instance */
Log villas::logging;

Steffen Vogel's avatar
Steffen Vogel committed
38
39
40
41
42
Log::Log(Level lvl) :
	level(lvl)
{ }

void Log::init()
43
44
45
46
47
{
	char *p = getenv("VILLAS_LOG_PREFIX");
	if (p)
		prefix = p;

48
49
	setLevel(level);
	setPattern("%H:%M:%S %^%l%$ %n: %v");
50

Steffen Vogel's avatar
Steffen Vogel committed
51
52
53
	sinks = std::make_shared<DistSink::element_type>();

	// Default sink
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
	auto sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();

	sinks->add_sink(sink);
}

int Log::getWidth()
{
	int width = Terminal::getCols() - 25;

	if (!prefix.empty())
		width -= prefix.length();

	return width;
}

Logger Log::get(const std::string &name)
{
Steffen Vogel's avatar
Steffen Vogel committed
71
	Logger logger = spdlog::get(name);
72

Steffen Vogel's avatar
Steffen Vogel committed
73
74
75
76
	if (not sinks)
		init();

	if (not logger) {
77
78
		logger = std::make_shared<Logger::element_type>(name, sinks);

Steffen Vogel's avatar
Steffen Vogel committed
79
80
81
82
83
84
		logger->set_level(level);
		logger->set_pattern(prefix + pattern);

		spdlog::register_logger(logger);
	}

85
86
87
88
89
90
91
92
93
94
95
96
97
	return logger;
}

void Log::parse(json_t *cfg)
{
	const char *level = NULL;
	const char *path = NULL;
	const char *pattern = NULL;

	int syslog;
	int ret;

	json_error_t err;
Steffen Vogel's avatar
Steffen Vogel committed
98
	json_t *json_expressions = nullptr;
99
100
101
102
103
104
105
106
107

	ret = json_unpack_ex(cfg, &err, 0, "{ s?: s, s?: s, s?: s, s?: b, s?: s }",
		"level", &level,
		"file", &path,
		"expressions", &json_expressions,
		"syslog", &syslog,
		"pattern", &pattern
	);
	if (ret)
108
		throw JsonError(err);
109
110
111
112
113
114
115

	if (level)
		setLevel(level);

	if (path) {
		auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(path);

Steffen Vogel's avatar
Steffen Vogel committed
116
		sinks->add_sink(sink);
117
118
119
120
121
	}

	if (syslog) {
		auto sink = std::make_shared<spdlog::sinks::syslog_sink_mt>("villas", LOG_PID, LOG_DAEMON);

Steffen Vogel's avatar
Steffen Vogel committed
122
		sinks->add_sink(sink);
123
124
125
126
	}

	if (json_expressions) {
		if (!json_is_array(json_expressions))
127
			throw ConfigError(json_expressions, "node-config.html#node-config-logging-expressions", "The 'expressions' setting must be a list of objects.");
128
129
130
131
132
133
134
135
136
137
138
139

		size_t i;
		json_t *json_expression;
		json_array_foreach(json_expressions, i, json_expression) {
			const char *name;
			const char *lvl;

			ret = json_unpack_ex(json_expression, &err, 0, "{ s: s, s: s }",
				"name", &name,
				"level", &lvl
			);
			if (ret)
140
				throw JsonError(err);
141

Steffen Vogel's avatar
Steffen Vogel committed
142
			Logger logger = get(name);
143
144
145
146
147
148
149
			auto level = spdlog::level::from_str(lvl);

			logger->set_level(level);
		}
	}
}

Steffen Vogel's avatar
Steffen Vogel committed
150
151
152
153
154
155
156
void Log::setPattern(const std::string &pat)
{
	pattern = pat;

	spdlog::set_pattern(pattern, spdlog::pattern_time_type::utc);
}

157
158
void Log::setLevel(Level lvl)
{
159
160
	level = lvl;

161
162
163
164
165
	spdlog::set_level(lvl);
}

void Log::setLevel(const std::string &lvl)
{
166
167
168
169
170
171
	std::list<std::string> l = SPDLOG_LEVEL_NAMES;

	auto it = std::find(l.begin(), l.end(), lvl);
	if (it == l.end())
		throw RuntimeError("Invalid log level {}", lvl);

172
	setLevel(spdlog::level::from_str(lvl));
173
174
}

Steffen Vogel's avatar
Steffen Vogel committed
175
176
177
178
Log::Level Log::getLevel() const
{
	return level;
}
Steffen Vogel's avatar
Steffen Vogel committed
179
180
181
182
183

std::string Log::getLevelName() const
{
	return std::string(spdlog::level::to_c_str(level));
}