log.cpp 4.06 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-2020, 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
#include <spdlog/sinks/syslog_sink.h>
#include <spdlog/sinks/basic_file_sink.h>

#include <villas/log.hpp>
Steffen Vogel's avatar
Steffen Vogel committed
30
#include <villas/terminal.hpp>
31
32
33
34
35
36
37
#include <villas/exceptions.hpp>

using namespace villas;

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

Steffen Vogel's avatar
Steffen Vogel committed
38
Log::Log(Level lvl) :
Steffen Vogel's avatar
Steffen Vogel committed
39
	level(lvl),
Steffen Vogel's avatar
Steffen Vogel committed
40
//	pattern("%+")
Steffen Vogel's avatar
Steffen Vogel committed
41
	pattern("%H:%M:%S %^%l%$ %n: %v")
42
43
44
45
46
{
	char *p = getenv("VILLAS_LOG_PREFIX");
	if (p)
		prefix = p;

Steffen Vogel's avatar
Steffen Vogel committed
47
48
	sinks = std::make_shared<DistSink::element_type>();

Steffen Vogel's avatar
Steffen Vogel committed
49
50
51
	setLevel(level);
	setPattern(pattern);

52
	/* Default sink */
Steffen Vogel's avatar
Steffen Vogel committed
53
	sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
54
55
56
57
58
59

	sinks->add_sink(sink);
}

int Log::getWidth()
{
Steffen Vogel's avatar
Steffen Vogel committed
60
	int width = Terminal::getCols() - 50;
61
62
63
64
65
66
67
68
69

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

	return width;
}

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

Steffen Vogel's avatar
Steffen Vogel committed
72
	if (not logger) {
Steffen Vogel's avatar
Steffen Vogel committed
73
		logger = std::make_shared<Logger::element_type>(name, sink);
74

Steffen Vogel's avatar
Steffen Vogel committed
75
76
77
78
79
80
		logger->set_level(level);
		logger->set_pattern(prefix + pattern);

		spdlog::register_logger(logger);
	}

81
82
83
84
85
	return logger;
}

void Log::parse(json_t *cfg)
{
Steffen Vogel's avatar
Steffen Vogel committed
86
87
88
	const char *level = nullptr;
	const char *path = nullptr;
	const char *pattern = nullptr;
89
90
91
92
93

	int syslog;
	int ret;

	json_error_t err;
Steffen Vogel's avatar
Steffen Vogel committed
94
	json_t *json_expressions = nullptr;
95

Steffen Vogel's avatar
Steffen Vogel committed
96
	ret = json_unpack_ex(cfg, &err, JSON_STRICT, "{ s?: s, s?: s, s?: o, s?: b, s?: s }",
97
98
99
100
101
102
103
		"level", &level,
		"file", &path,
		"expressions", &json_expressions,
		"syslog", &syslog,
		"pattern", &pattern
	);
	if (ret)
104
		throw ConfigError(cfg, err, "node-config-logging");
105
106
107
108
109
110
111

	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
112
		sinks->add_sink(sink);
113
114
115
116
117
	}

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

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

	if (json_expressions) {
		if (!json_is_array(json_expressions))
123
			throw ConfigError(json_expressions, "node-config-logging-expressions", "The 'expressions' setting must be a list of objects.");
124
125
126
127
128
129
130

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

131
			ret = json_unpack_ex(json_expression, &err, JSON_STRICT, "{ s: s, s: s }",
132
133
134
135
				"name", &name,
				"level", &lvl
			);
			if (ret)
136
				throw ConfigError(json_expression, err, "node-config-logging-expressions");
137

Steffen Vogel's avatar
Steffen Vogel committed
138
			Logger logger = get(name);
139
140
141
142
143
144
145
			auto level = spdlog::level::from_str(lvl);

			logger->set_level(level);
		}
	}
}

Steffen Vogel's avatar
Steffen Vogel committed
146
147
148
149
150
void Log::setPattern(const std::string &pat)
{
	pattern = pat;

	spdlog::set_pattern(pattern, spdlog::pattern_time_type::utc);
Steffen Vogel's avatar
Steffen Vogel committed
151
	//sinks.set_pattern(pattern);
Steffen Vogel's avatar
Steffen Vogel committed
152
153
}

154
155
void Log::setLevel(Level lvl)
{
156
157
	level = lvl;

158
	spdlog::set_level(lvl);
Steffen Vogel's avatar
Steffen Vogel committed
159
	//sinks.set_level(lvl);
160
161
162
163
}

void Log::setLevel(const std::string &lvl)
{
164
165
166
167
168
169
	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);

170
	setLevel(spdlog::level::from_str(lvl));
171
172
}

Steffen Vogel's avatar
Steffen Vogel committed
173
174
175
176
Log::Level Log::getLevel() const
{
	return level;
}
Steffen Vogel's avatar
Steffen Vogel committed
177
178
179

std::string Log::getLevelName() const
{
Steffen Vogel's avatar
Steffen Vogel committed
180
181
182
	auto sv = spdlog::level::to_string_view(level);

	return std::string(sv.data());
Steffen Vogel's avatar
Steffen Vogel committed
183
}